Skip to content

Commit

Permalink
Add MailDeliveryJob for unified mail delivery
Browse files Browse the repository at this point in the history
Add `MailDeliveryJob` for delivering both regular and parameterized mail.
Deprecate using `DeliveryJob` and `Parameterized::DeliveryJob`.
  • Loading branch information
gmcgibbon committed Dec 4, 2018
1 parent 609c58b commit f5050d9
Show file tree
Hide file tree
Showing 12 changed files with 179 additions and 23 deletions.
2 changes: 1 addition & 1 deletion actionmailer/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
* Deliver parameterized mail with `ActionMailer::DeliveryJob` and remove `ActionMailer::Parameterized::DeliveryJob`.
* Add `MailDeliveryJob` for delivering both regular and parameterized mail. Deprecate using `DeliveryJob` and `Parameterized::DeliveryJob`.

*Gannon McGibbon*

Expand Down
1 change: 1 addition & 0 deletions actionmailer/lib/action_mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ module ActionMailer
autoload :TestHelper
autoload :MessageDelivery
autoload :DeliveryJob
autoload :MailDeliveryJob

def self.eager_load!
super
Expand Down
2 changes: 1 addition & 1 deletion actionmailer/lib/action_mailer/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ def _protected_ivars # :nodoc:

helper ActionMailer::MailHelper

class_attribute :delivery_job, default: ::ActionMailer::DeliveryJob
class_attribute :delivery_job, default: ::ActionMailer::MailDeliveryJob
class_attribute :default_params, default: {
mime_version: "1.0",
charset: "UTF-8",
Expand Down
13 changes: 10 additions & 3 deletions actionmailer/lib/action_mailer/delivery_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,16 @@ class DeliveryJob < ActiveJob::Base # :nodoc:

rescue_from StandardError, with: :handle_exception_with_mailer_class

def perform(mailer, mail_method, delivery_method, params, *args) #:nodoc:
mailer_class = params ? mailer.constantize.with(params) : mailer.constantize
mailer_class.public_send(mail_method, *args).send(delivery_method)
before_perform do
ActiveSupport::Deprecation.warn <<~MSG.squish
Sending mail with DeliveryJob and Parameterized::DeliveryJob
is deprecated and will be removed in Rails 6.1.
Please use MailDeliveryJob instead.
MSG
end

def perform(mailer, mail_method, delivery_method, *args) #:nodoc:
mailer.constantize.public_send(mail_method, *args).send(delivery_method)
end

private
Expand Down
38 changes: 38 additions & 0 deletions actionmailer/lib/action_mailer/mail_delivery_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# frozen_string_literal: true

require "active_job"

module ActionMailer
# The <tt>ActionMailer::NewDeliveryJob</tt> class is used when you
# want to send emails outside of the request-response cycle. It supports
# sending either parameterized or normal mail.
#
# Exceptions are rescued and handled by the mailer class.
class MailDeliveryJob < ActiveJob::Base # :nodoc:
queue_as { ActionMailer::Base.deliver_later_queue_name }

rescue_from StandardError, with: :handle_exception_with_mailer_class

def perform(mailer, mail_method, delivery_method, args:, params: nil) #:nodoc:
mailer_class = params ? mailer.constantize.with(params) : mailer.constantize
mailer_class.public_send(mail_method, *args).send(delivery_method)
end

private
# "Deserialize" the mailer class name by hand in case another argument
# (like a Global ID reference) raised DeserializationError.
def mailer_class
if mailer = Array(@serialized_arguments).first || Array(arguments).first
mailer.constantize
end
end

def handle_exception_with_mailer_class(exception)
if klass = mailer_class
klass.handle_exception exception
else
raise exception
end
end
end
end
10 changes: 9 additions & 1 deletion actionmailer/lib/action_mailer/message_delivery.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,18 @@ def enqueue_delivery(delivery_method, options = {})
"#deliver_later, 2. only touch the message *within your mailer " \
"method*, or 3. use a custom Active Job instead of #deliver_later."
else
args = @mailer_class.name, @action.to_s, delivery_method.to_s, nil, *@args
job = @mailer_class.delivery_job
args = arguments_for(job, delivery_method)
job.set(options).perform_later(*args)
end
end

def arguments_for(delivery_job, delivery_method)
if delivery_job <= MailDeliveryJob
[@mailer_class.name, @action.to_s, delivery_method.to_s, args: @args]
else
[@mailer_class.name, @action.to_s, delivery_method.to_s, *@args]
end
end
end
end
18 changes: 16 additions & 2 deletions actionmailer/lib/action_mailer/parameterized.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ def respond_to_missing?(method, include_all = false)
end
end

class DeliveryJob < ActionMailer::DeliveryJob # :nodoc:
def perform(mailer, mail_method, delivery_method, params, *args)
mailer.constantize.with(params).public_send(mail_method, *args).send(delivery_method)
end
end

class MessageDelivery < ActionMailer::MessageDelivery # :nodoc:
def initialize(mailer_class, action, params, *args)
super(mailer_class, action, *args)
Expand All @@ -139,11 +145,19 @@ def enqueue_delivery(delivery_method, options = {})
if processed?
super
else
args = @mailer_class.name, @action.to_s, delivery_method.to_s, @params, *@args
job = @mailer_class.delivery_job
job = @mailer_class.delivery_job
args = arguments_for(job, delivery_method)
job.set(options).perform_later(*args)
end
end

def arguments_for(delivery_job, delivery_method)
if delivery_job <= MailDeliveryJob
[@mailer_class.name, @action.to_s, delivery_method.to_s, params: @params, args: @args]
else
[@mailer_class.name, @action.to_s, delivery_method.to_s, @params, *@args]
end
end
end
end
end
4 changes: 2 additions & 2 deletions actionmailer/lib/action_mailer/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ def assert_enqueued_emails(number, &block)
# end
def assert_enqueued_email_with(mailer, method, args: nil, queue: "mailers", &block)
args = if args.is_a?(Hash)
[mailer.to_s, method.to_s, "deliver_now", args]
[mailer.to_s, method.to_s, "deliver_now", params: args, args: []]
else
[mailer.to_s, method.to_s, "deliver_now", nil, *args]
[mailer.to_s, method.to_s, "deliver_now", args: Array(args)]
end
assert_enqueued_with(job: mailer.delivery_job, args: args, queue: queue, &block)
end
Expand Down
86 changes: 86 additions & 0 deletions actionmailer/test/legacy_delivery_job_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# frozen_string_literal: true

require "abstract_unit"
require "active_job"
require "mailers/params_mailer"
require "mailers/delayed_mailer"

class LegacyDeliveryJobTest < ActiveSupport::TestCase
include ActiveJob::TestHelper

class LegacyDeliveryJob < ActionMailer::DeliveryJob
end

class LegacyParmeterizedDeliveryJob < ActionMailer::Parameterized::DeliveryJob
end

setup do
@previous_logger = ActiveJob::Base.logger
ActiveJob::Base.logger = Logger.new(nil)

@previous_delivery_method = ActionMailer::Base.delivery_method
ActionMailer::Base.delivery_method = :test

@previous_deliver_later_queue_name = ActionMailer::Base.deliver_later_queue_name
ActionMailer::Base.deliver_later_queue_name = :test_queue
end

teardown do
ActiveJob::Base.logger = @previous_logger
ParamsMailer.deliveries.clear

ActionMailer::Base.delivery_method = @previous_delivery_method
ActionMailer::Base.deliver_later_queue_name = @previous_deliver_later_queue_name
end

test "should send parameterized mail correctly" do
mail = ParamsMailer.with(inviter: "david@basecamp.com", invitee: "jason@basecamp.com").invitation
args = [
"ParamsMailer",
"invitation",
"deliver_now",
{ inviter: "david@basecamp.com", invitee: "jason@basecamp.com" },
]

with_delivery_job(LegacyParmeterizedDeliveryJob) do
assert_deprecated do
assert_performed_with(job: LegacyParmeterizedDeliveryJob, args: args) do
mail.deliver_later
end
end
end
end

test "should send mail correctly" do
mail = DelayedMailer.test_message(1, 2, 3)
args = [
"DelayedMailer",
"test_message",
"deliver_now",
1,
2,
3,
]

with_delivery_job(LegacyDeliveryJob) do
assert_deprecated do
assert_performed_with(job: LegacyDeliveryJob, args: args) do
mail.deliver_later
end
end
end
end

private

def with_delivery_job(job)
old_params_delivery_job = ParamsMailer.delivery_job
old_regular_delivery_job = DelayedMailer.delivery_job
ParamsMailer.delivery_job = job
DelayedMailer.delivery_job = job
yield
ensure
ParamsMailer.delivery_job = old_params_delivery_job
DelayedMailer.delivery_job = old_regular_delivery_job
end
end
16 changes: 8 additions & 8 deletions actionmailer/test/message_delivery_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,34 +64,34 @@ def test_should_enqueue_and_run_correctly_in_activejob
end

test "should enqueue the email with :deliver_now delivery method" do
assert_performed_with(job: ActionMailer::DeliveryJob, args: ["DelayedMailer", "test_message", "deliver_now", nil, 1, 2, 3]) do
assert_performed_with(job: ActionMailer::MailDeliveryJob, args: ["DelayedMailer", "test_message", "deliver_now", args: [1, 2, 3]]) do
@mail.deliver_later
end
end

test "should enqueue the email with :deliver_now! delivery method" do
assert_performed_with(job: ActionMailer::DeliveryJob, args: ["DelayedMailer", "test_message", "deliver_now!", nil, 1, 2, 3]) do
assert_performed_with(job: ActionMailer::MailDeliveryJob, args: ["DelayedMailer", "test_message", "deliver_now!", args: [1, 2, 3]]) do
@mail.deliver_later!
end
end

test "should enqueue a delivery with a delay" do
travel_to Time.new(2004, 11, 24, 01, 04, 44) do
assert_performed_with(job: ActionMailer::DeliveryJob, at: Time.current + 10.minutes, args: ["DelayedMailer", "test_message", "deliver_now", nil, 1, 2, 3]) do
assert_performed_with(job: ActionMailer::MailDeliveryJob, at: Time.current + 10.minutes, args: ["DelayedMailer", "test_message", "deliver_now", args: [1, 2, 3]]) do
@mail.deliver_later wait: 10.minutes
end
end
end

test "should enqueue a delivery at a specific time" do
later_time = Time.current + 1.hour
assert_performed_with(job: ActionMailer::DeliveryJob, at: later_time, args: ["DelayedMailer", "test_message", "deliver_now", nil, 1, 2, 3]) do
assert_performed_with(job: ActionMailer::MailDeliveryJob, at: later_time, args: ["DelayedMailer", "test_message", "deliver_now", args: [1, 2, 3]]) do
@mail.deliver_later wait_until: later_time
end
end

test "should enqueue the job on the correct queue" do
assert_performed_with(job: ActionMailer::DeliveryJob, args: ["DelayedMailer", "test_message", "deliver_now", nil, 1, 2, 3], queue: "test_queue") do
assert_performed_with(job: ActionMailer::MailDeliveryJob, args: ["DelayedMailer", "test_message", "deliver_now", args: [1, 2, 3]], queue: "test_queue") do
@mail.deliver_later
end
end
Expand All @@ -100,17 +100,17 @@ def test_should_enqueue_and_run_correctly_in_activejob
old_delivery_job = DelayedMailer.delivery_job
DelayedMailer.delivery_job = DummyJob

assert_performed_with(job: DummyJob, args: ["DelayedMailer", "test_message", "deliver_now", nil, 1, 2, 3]) do
assert_performed_with(job: DummyJob, args: ["DelayedMailer", "test_message", "deliver_now", args: [1, 2, 3]]) do
@mail.deliver_later
end

DelayedMailer.delivery_job = old_delivery_job
end

class DummyJob < ActionMailer::DeliveryJob; end
class DummyJob < ActionMailer::MailDeliveryJob; end

test "can override the queue when enqueuing mail" do
assert_performed_with(job: ActionMailer::DeliveryJob, args: ["DelayedMailer", "test_message", "deliver_now", nil, 1, 2, 3], queue: "another_queue") do
assert_performed_with(job: ActionMailer::MailDeliveryJob, args: ["DelayedMailer", "test_message", "deliver_now", args: [1, 2, 3]], queue: "another_queue") do
@mail.deliver_later(queue: :another_queue)
end
end
Expand Down
10 changes: 6 additions & 4 deletions actionmailer/test/parameterized_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class ParameterizedTest < ActiveSupport::TestCase
include ActiveJob::TestHelper

class DummyDeliveryJob < ActionMailer::DeliveryJob
class DummyDeliveryJob < ActionMailer::MailDeliveryJob
end

setup do
Expand Down Expand Up @@ -42,9 +42,10 @@ class DummyDeliveryJob < ActionMailer::DeliveryJob
"ParamsMailer",
"invitation",
"deliver_now",
{ inviter: "david@basecamp.com", invitee: "jason@basecamp.com" },
params: { inviter: "david@basecamp.com", invitee: "jason@basecamp.com" },
args: [],
]
assert_performed_with(job: ActionMailer::DeliveryJob, args: args) do
assert_performed_with(job: ActionMailer::MailDeliveryJob, args: args) do
@mail.deliver_later
end
end
Expand All @@ -68,7 +69,8 @@ class DummyDeliveryJob < ActionMailer::DeliveryJob
"ParamsMailer",
"invitation",
"deliver_now",
{ inviter: "david@basecamp.com", invitee: "jason@basecamp.com" },
params: { inviter: "david@basecamp.com", invitee: "jason@basecamp.com" },
args: [],
]

with_delivery_job DummyDeliveryJob do
Expand Down
2 changes: 1 addition & 1 deletion actionmailer/test/test_helper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def test_parameter_args
end
end

class CustomDeliveryJob < ActionMailer::DeliveryJob
class CustomDeliveryJob < ActionMailer::MailDeliveryJob
end

class CustomDeliveryMailer < TestHelperMailer
Expand Down

0 comments on commit f5050d9

Please sign in to comment.