Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Callbacks and deliver short circuiting #72

Merged
merged 2 commits into from
Aug 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
105 changes: 105 additions & 0 deletions spec/callbacks_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
require "./spec_helper"

abstract class BaseTestEmail < Carbon::Email
subject "My great subject"
from Carbon::Address.new("[email protected]")
to Carbon::Address.new("[email protected]")
end

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

private class EmailWithBeforeCallbacks < BaseTestEmail
property ran_before_callback : Bool = false

before_send do
self.ran_before_callback = true
end
end

private class EmailWithAfterCallbacks < BaseTestEmail
property ran_after_callback : Bool = false

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

private class EmailWithBothBeforeAndAfterCallbacks < BaseTestEmail
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

private class EmailUsingBeforeToStopSending < BaseTestEmail
before_send :dont_actually_send
after_send :never_actually_ran

property ran_after_callback : Bool = false

private def dont_actually_send
@deliverable = false
end

private def never_actually_ran(_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

context "Halting the deliver before it's sent" do
it "never sends" do
email = EmailUsingBeforeToStopSending.new
email.deliver
Carbon.should_not have_delivered_emails
email.deliverable?.should eq(false)
email.ran_after_callback.should eq(false)
end
end
end
14 changes: 14 additions & 0 deletions spec/email_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ private class EmailWithLayout < BareMinimumEmail
layout custom_layout
end

private class UndeliverableEmail < Carbon::Email
subject "My great subject"
from Carbon::Address.new("[email protected]")
to Carbon::Address.new("[email protected]")
end

describe Carbon::Email do
it "can build a bare minimum email" do
email = BareMinimumEmail.new
Expand Down Expand Up @@ -146,4 +152,12 @@ describe Carbon::Email do
email.html_body.should contain "Email Layout"
email.html_body.should contain "Email body"
end

context "deliverable?" do
it "is not delivery it is digiorno" do
email = UndeliverableEmail.new
email.deliverable = false
email.deliverable?.should eq(false)
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
16 changes: 15 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 @@ -9,6 +10,10 @@ abstract class Carbon::Email

def_equals subject, from, to, cc, bcc, headers, text_body, html_body

# Set this value to `false` to prevent the email from
# being delivered
property? deliverable : Bool = true

def cc
[] of Carbon::Address
end
Expand All @@ -25,6 +30,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 +131,12 @@ abstract class Carbon::Email
end

def deliver
settings.adapter.deliver_now(self)
before_send

if deliverable?
response = settings.adapter.deliver_now(self)
after_send(response)
end
end

def deliver_later
Expand Down