-
Notifications
You must be signed in to change notification settings - Fork 21.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Offer the option to use parameterization for shared processing of hea…
…ders and ivars (#27825) Offer the option to use parameterization for shared processing of headers and ivars
- Loading branch information
Showing
6 changed files
with
212 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
module ActionMailer | ||
# Provides the option to parameterize mailers in other to share ivar setup, processing, and common headers. | ||
# | ||
# Consider this example that does not use parameterization: | ||
# | ||
# class InvitationsMailer < ApplicationMailer | ||
# def account_invitation(inviter, invitee) | ||
# @account = inviter.account | ||
# @inviter = inviter | ||
# @invitee = invitee | ||
# | ||
# subject = "#{@inviter.name} invited you to their Basecamp (#{@account.name})" | ||
# | ||
# mail \ | ||
# subject: subject, | ||
# to: invitee.email_address, | ||
# from: common_address(inviter), | ||
# reply_to: inviter.email_address_with_name | ||
# end | ||
# | ||
# def project_invitation(project, inviter, invitee) | ||
# @account = inviter.account | ||
# @project = project | ||
# @inviter = inviter | ||
# @invitee = invitee | ||
# @summarizer = ProjectInvitationSummarizer.new(@project.bucket) | ||
# | ||
# subject = "#{@inviter.name.familiar} added you to a project in Basecamp (#{@account.name})" | ||
# | ||
# mail \ | ||
# subject: subject, | ||
# to: invitee.email_address, | ||
# from: common_address(inviter), | ||
# reply_to: inviter.email_address_with_name | ||
# end | ||
# | ||
# def bulk_project_invitation(projects, inviter, invitee) | ||
# @account = inviter.account | ||
# @projects = projects.sort_by(&:name) | ||
# @inviter = inviter | ||
# @invitee = invitee | ||
# | ||
# subject = "#{@inviter.name.familiar} added you to some new stuff in Basecamp (#{@account.name})" | ||
# | ||
# mail \ | ||
# subject: subject, | ||
# to: invitee.email_address, | ||
# from: common_address(inviter), | ||
# reply_to: inviter.email_address_with_name | ||
# end | ||
# end | ||
# | ||
# InvitationsMailer.account_invitation(person_a, person_b).deliver_later | ||
# | ||
# Using parameterized mailers, this can be rewritten as: | ||
# | ||
# class InvitationsMailer < ApplicationMailer | ||
# before_action { @inviter, @invitee = params[:inviter], params[:invitee] } | ||
# before_action { @account = params[:inviter].account } | ||
# | ||
# default to: -> { @invitee.email_address }, | ||
# from: -> { common_address(@inviter) }, | ||
# reply_to: -> { @inviter.email_address_with_name } | ||
# | ||
# def account_invitation | ||
# mail subject: "#{@inviter.name} invited you to their Basecamp (#{@account.name})" | ||
# end | ||
# | ||
# def project_invitation | ||
# @project = params[:project] | ||
# @summarizer = ProjectInvitationSummarizer.new(@project.bucket) | ||
# | ||
# mail subject: "#{@inviter.name.familiar} added you to a project in Basecamp (#{@account.name})" | ||
# end | ||
# | ||
# def bulk_project_invitation | ||
# @projects = params[:projects].sort_by(&:name) | ||
# | ||
# mail subject: "#{@inviter.name.familiar} added you to some new stuff in Basecamp (#{@account.name})" | ||
# end | ||
# end | ||
# | ||
# InvitationsMailer.with(inviter: person_a, invitee: person_b).account_invitation.deliver_later | ||
# | ||
# That's a big improvement! It's also fully backwards compatible. So you can start to gradually transition | ||
# mailers that stand to benefit the most from parameterization one by one and leave the others behind. | ||
module Parameterized | ||
extend ActiveSupport::Concern | ||
|
||
included do | ||
attr_accessor :params | ||
end | ||
|
||
class_methods do | ||
def with(params) | ||
ActionMailer::Parameterized::Mailer.new(self, params) | ||
end | ||
end | ||
|
||
class Mailer | ||
def initialize(mailer, params) | ||
@mailer, @params = mailer, params | ||
end | ||
|
||
def method_missing(method_name, *args) | ||
if @mailer.action_methods.include?(method_name.to_s) | ||
ActionMailer::Parameterized::MessageDelivery.new(@mailer, method_name, *args).tap { |pmd| pmd.params = @params } | ||
else | ||
super | ||
end | ||
end | ||
end | ||
|
||
class MessageDelivery < ActionMailer::MessageDelivery | ||
attr_accessor :params | ||
|
||
private | ||
def processed_mailer | ||
@processed_mailer ||= @mailer_class.new.tap do |mailer| | ||
mailer.params = params | ||
mailer.process @action, *@args | ||
end | ||
end | ||
|
||
def enqueue_delivery(delivery_method, options = {}) | ||
if processed? | ||
super | ||
else | ||
args = @mailer_class.name, @action.to_s, delivery_method.to_s, @params, *@args | ||
ActionMailer::Parameterized::DeliveryJob.set(options).perform_later(*args) | ||
end | ||
end | ||
end | ||
|
||
class DeliveryJob < ActionMailer::DeliveryJob # :nodoc: | ||
def perform(mailer, mail_method, delivery_method, params, *args) #:nodoc: | ||
mailer.constantize.with(params).public_send(mail_method, *args).send(delivery_method) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
class ParamsMailer < ActionMailer::Base | ||
before_action { @inviter, @invitee = params[:inviter], params[:invitee] } | ||
|
||
default to: Proc.new { @invitee }, from: -> { @inviter } | ||
|
||
def invitation | ||
mail(subject: "Welcome to the project!") do |format| | ||
format.text { render plain: "So says #{@inviter}" } | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
require "abstract_unit" | ||
require "active_job" | ||
require "mailers/params_mailer" | ||
|
||
class ParameterizedTest < ActiveSupport::TestCase | ||
include ActiveJob::TestHelper | ||
|
||
setup do | ||
@previous_logger = ActiveJob::Base.logger | ||
ActiveJob::Base.logger = Logger.new(nil) | ||
|
||
@original_delivery_method = ActionMailer::Base.delivery_method | ||
ActionMailer::Base.delivery_method = :test | ||
|
||
@previous_delivery_method = ActionMailer::Base.delivery_method | ||
@previous_deliver_later_queue_name = ActionMailer::Base.deliver_later_queue_name | ||
ActionMailer::Base.deliver_later_queue_name = :test_queue | ||
ActionMailer::Base.delivery_method = :test | ||
|
||
@mail = ParamsMailer.with(inviter: "david@basecamp.com", invitee: "jason@basecamp.com").invitation | ||
end | ||
|
||
teardown do | ||
ActiveJob::Base.logger = @previous_logger | ||
ParamsMailer.deliveries.clear | ||
|
||
ActionMailer::Base.delivery_method = @original_delivery_method | ||
|
||
ActionMailer::Base.delivery_method = @previous_delivery_method | ||
ActionMailer::Base.deliver_later_queue_name = @previous_deliver_later_queue_name | ||
end | ||
|
||
test "parameterized headers" do | ||
assert_equal(["jason@basecamp.com"], @mail.to) | ||
assert_equal(["david@basecamp.com"], @mail.from) | ||
assert_equal("So says david@basecamp.com", @mail.body.encoded) | ||
end | ||
|
||
test "should enqueue the email with params" do | ||
assert_performed_with(job: ActionMailer::Parameterized::DeliveryJob, args: ["ParamsMailer", "invitation", "deliver_now", { inviter: "david@basecamp.com", invitee: "jason@basecamp.com" } ]) do | ||
@mail.deliver_later | ||
end | ||
end | ||
end |