Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
allows the emails to have before/after callbacks. Fixes #65
  • Loading branch information
jwoertink committed Aug 13, 2022
commit 9d6ea7b354bd58444746eed545887d45ef5e1153
89 changes: 89 additions & 0 deletions spec/callbacks_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
require "./spec_helper"

abstract class BaseTestEmail < Carbon::Email
end

BaseTestEmail.configure do |setting|
setting.adapter = Carbon::DevAdapter.new
end

private class EmailWithBeforeCallbacks < BaseTestEmail
subject "My great subject"
from Carbon::Address.new("from@example.com")
to Carbon::Address.new("to@example.com")

property ran_before_callback : Bool = false

before_send do
self.ran_before_callback = true
end
end

private class EmailWithAfterCallbacks < BaseTestEmail
subject "My great subject"
from Carbon::Address.new("from@example.com")
to Carbon::Address.new("to@example.com")

property ran_after_callback : Bool = false

after_send do |_response|
self.ran_after_callback = true
end
end

private class EmailWithBothBeforeAndAfterCallbacks < BaseTestEmail
subject "My great subject"
from Carbon::Address.new("from@example.com")
to Carbon::Address.new("to@example.com")

property ran_before_callback : Bool = false
property ran_after_callback : Bool = false

before_send :mark_before_send
after_send :mark_after_send

private def mark_before_send
self.ran_before_callback = true
end

private def mark_after_send(_response)
self.ran_after_callback = true
end
end

describe "before/after callbacks" do
context "before an email is sent" do
it "runs the before_send callback" do
email = EmailWithBeforeCallbacks.new
email.ran_before_callback.should eq(false)
email.deliver
Carbon.should have_delivered_emails

email.ran_before_callback.should eq(true)
end
end

context "after an email is sent" do
it "runs the after_send callback" do
email = EmailWithAfterCallbacks.new
email.ran_after_callback.should eq(false)
email.deliver
Carbon.should have_delivered_emails

email.ran_after_callback.should eq(true)
end
end

context "running both callbacks" do
it "runs both callbacks" do
email = EmailWithBothBeforeAndAfterCallbacks.new
email.ran_before_callback.should eq(false)
email.ran_after_callback.should eq(false)
email.deliver
Carbon.should have_delivered_emails

email.ran_before_callback.should eq(true)
email.ran_after_callback.should eq(true)
end
end
end
81 changes: 81 additions & 0 deletions src/carbon/callbacks.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
module Carbon::Callbacks
# Runs the given method before the adapter calls `deliver_now`
#
# ```
# before_send :attach_metadata
#
# private def attach_metadata
# #...
# end
# ```
macro before_send(method_name)
before_send do
{{ method_name.id }}
end
end

# Runs the block before the adapter calls `deliver_now`
#
# ```
# before_send do
# #...
# end
# ```
macro before_send
def before_send
{% if @type.methods.map(&.name).includes?(:before_send.id) %}
previous_def
{% else %}
super
{% end %}

{{ yield }}
end
end

# Runs the given method after the adapter calls `deliver_now`.
# Passes in the return value of the adapter's `deliver_now` method.
#
# ```
# after_send :mark_email_as_sent
#
# private def mark_email_as_sent(response)
# # ...
# end
# ```
macro after_send(method_name)
after_send do |object|
{{ method_name.id }}(object)
end
end

# Runs the block after the adapter calls `deliver_now`, and passes the
# return value of the adapter's `deliver_now` method to the block.
#
# ```
# after_send do |response|
# # ...
# end
# ```
macro after_send(&block)
{%
if block.args.size != 1
raise <<-ERR
The 'after_send' callback requires exactly 1 block arg to be passed.
Example:
after_send { |value| some_method(value) }
ERR
end
%}
def after_send(%object)
{% if @type.methods.map(&.name).includes?(:after_send.id) %}
previous_def
{% else %}
super
{% end %}

{{ block.args.first }} = %object
{{ block.body }}
end
end
end
9 changes: 8 additions & 1 deletion src/carbon/email.cr
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require "ecr"

abstract class Carbon::Email
include Carbon::Callbacks
alias Recipients = Carbon::Emailable | Array(Carbon::Emailable)

abstract def subject : String
Expand All @@ -25,6 +26,10 @@ abstract class Carbon::Email

def html_layout(content_io : IO); end

def before_send; end

def after_send(result); end

getter headers

macro inherited
Expand Down Expand Up @@ -122,7 +127,9 @@ abstract class Carbon::Email
end

def deliver
settings.adapter.deliver_now(self)
before_send
response = settings.adapter.deliver_now(self)
after_send(response)
end

def deliver_later
Expand Down