mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Offer the option to use parameterization for shared processing of headers and ivars (#27825)
Offer the option to use parameterization for shared processing of headers and ivars
This commit is contained in:
parent
05112b2131
commit
1cec84ad2d
6 changed files with 212 additions and 7 deletions
|
@ -1,3 +1,12 @@
|
|||
* Add parameterized invocation of mailers as a way to share before filters and defaults between actions.
|
||||
See ActionMailer::Parameterized for a full example of the benefit.
|
||||
|
||||
*DHH*
|
||||
|
||||
* Allow lambdas to be used as lazy defaults in addition to procs.
|
||||
|
||||
*DHH*
|
||||
|
||||
* Mime type: allow to custom content type when setting body in headers
|
||||
and attachments.
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ module ActionMailer
|
|||
autoload :DeliveryMethods
|
||||
autoload :InlinePreviewInterceptor
|
||||
autoload :MailHelper
|
||||
autoload :Parameterized
|
||||
autoload :Preview
|
||||
autoload :Previews, "action_mailer/preview"
|
||||
autoload :TestCase
|
||||
|
|
|
@ -288,20 +288,19 @@ module ActionMailer
|
|||
# content_description: 'This is a description'
|
||||
# end
|
||||
#
|
||||
# Finally, Action Mailer also supports passing <tt>Proc</tt> objects into the default hash, so you
|
||||
# can define methods that evaluate as the message is being generated:
|
||||
# Finally, Action Mailer also supports passing <tt>Proc</tt> and <tt>Lambda</tt> objects into the default hash,
|
||||
# so you can define methods that evaluate as the message is being generated:
|
||||
#
|
||||
# class NotifierMailer < ApplicationMailer
|
||||
# default 'X-Special-Header' => Proc.new { my_method }
|
||||
# default 'X-Special-Header' => Proc.new { my_method }, to: -> { @inviter.email_address }
|
||||
#
|
||||
# private
|
||||
#
|
||||
# def my_method
|
||||
# 'some complex call'
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Note that the proc is evaluated right at the start of the mail message generation, so if you
|
||||
# Note that the proc/lambda is evaluated right at the start of the mail message generation, so if you
|
||||
# set something in the default hash using a proc, and then set the same thing inside of your
|
||||
# mailer method, it will get overwritten by the mailer method.
|
||||
#
|
||||
|
@ -324,7 +323,6 @@ module ActionMailer
|
|||
# end
|
||||
#
|
||||
# private
|
||||
#
|
||||
# def add_inline_attachment!
|
||||
# attachments.inline["footer.jpg"] = File.read('/path/to/filename.jpg')
|
||||
# end
|
||||
|
@ -434,6 +432,7 @@ module ActionMailer
|
|||
class Base < AbstractController::Base
|
||||
include DeliveryMethods
|
||||
include Rescuable
|
||||
include Parameterized
|
||||
include Previews
|
||||
|
||||
abstract!
|
||||
|
@ -888,7 +887,7 @@ module ActionMailer
|
|||
default_values = self.class.default.map do |key, value|
|
||||
[
|
||||
key,
|
||||
value.is_a?(Proc) ? instance_eval(&value) : value
|
||||
value.is_a?(Proc) ? instance_exec(&value) : value
|
||||
]
|
||||
end.to_h
|
||||
|
||||
|
|
141
actionmailer/lib/action_mailer/parameterized.rb
Normal file
141
actionmailer/lib/action_mailer/parameterized.rb
Normal file
|
@ -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
|
11
actionmailer/test/mailers/params_mailer.rb
Normal file
11
actionmailer/test/mailers/params_mailer.rb
Normal file
|
@ -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
|
44
actionmailer/test/parameterized_test.rb
Normal file
44
actionmailer/test/parameterized_test.rb
Normal file
|
@ -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
|
Loading…
Reference in a new issue