diff options
Diffstat (limited to 'actionmailer/lib/action_mailer')
-rw-r--r-- | actionmailer/lib/action_mailer/base.rb | 15 | ||||
-rw-r--r-- | actionmailer/lib/action_mailer/parameterized.rb | 152 | ||||
-rw-r--r-- | actionmailer/lib/action_mailer/test_case.rb | 4 | ||||
-rw-r--r-- | actionmailer/lib/action_mailer/test_helper.rb | 4 |
4 files changed, 163 insertions, 12 deletions
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 8205743de3..c0c030ac3e 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -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! @@ -580,7 +579,7 @@ module ActionMailer end def respond_to_missing?(method, include_all = false) - action_methods.include?(method.to_s) + action_methods.include?(method.to_s) || super end end @@ -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 diff --git a/actionmailer/lib/action_mailer/parameterized.rb b/actionmailer/lib/action_mailer/parameterized.rb new file mode 100644 index 0000000000..3acacc1f14 --- /dev/null +++ b/actionmailer/lib/action_mailer/parameterized.rb @@ -0,0 +1,152 @@ +module ActionMailer + # Provides the option to parameterize mailers in order to share instance variable + # 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 + module Parameterized + extend ActiveSupport::Concern + + included do + attr_accessor :params + end + + module ClassMethods + # Provide the parameters to the mailer in order to use them in the instance methods and callbacks. + # + # InvitationsMailer.with(inviter: person_a, invitee: person_b).account_invitation.deliver_later + # + # See Parameterized documentation for full example. + def with(params) + ActionMailer::Parameterized::Mailer.new(self, params) + end + end + + class Mailer # :nodoc: + def initialize(mailer, params) + @mailer, @params = mailer, params + end + + private + def method_missing(method_name, *args) + if @mailer.action_methods.include?(method_name.to_s) + ActionMailer::Parameterized::MessageDelivery.new(@mailer, method_name, @params, *args) + else + super + end + end + + def respond_to_missing?(method, include_all = false) + @mailer.respond_to?(method, include_all) + end + end + + class MessageDelivery < ActionMailer::MessageDelivery # :nodoc: + def initialize(mailer_class, action, params, *args) + super(mailer_class, action, *args) + @params = params + end + + 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) + mailer.constantize.with(params).public_send(mail_method, *args).send(delivery_method) + end + end + end +end diff --git a/actionmailer/lib/action_mailer/test_case.rb b/actionmailer/lib/action_mailer/test_case.rb index cbd841466d..9ead03a40c 100644 --- a/actionmailer/lib/action_mailer/test_case.rb +++ b/actionmailer/lib/action_mailer/test_case.rb @@ -4,8 +4,8 @@ require "rails-dom-testing" module ActionMailer class NonInferrableMailerError < ::StandardError def initialize(name) - super "Unable to determine the mailer to test from #{name}. " + - "You'll need to specify it using tests YourMailer in your " + + super "Unable to determine the mailer to test from #{name}. " \ + "You'll need to specify it using tests YourMailer in your " \ "test case definition" end end diff --git a/actionmailer/lib/action_mailer/test_helper.rb b/actionmailer/lib/action_mailer/test_helper.rb index c17ecad4c6..c30fb1fc18 100644 --- a/actionmailer/lib/action_mailer/test_helper.rb +++ b/actionmailer/lib/action_mailer/test_helper.rb @@ -88,7 +88,7 @@ module ActionMailer # end # end def assert_enqueued_emails(number, &block) - assert_enqueued_jobs number, only: ActionMailer::DeliveryJob, &block + assert_enqueued_jobs number, only: [ ActionMailer::DeliveryJob, ActionMailer::Parameterized::DeliveryJob ], &block end # Asserts that no emails are enqueued for later delivery. @@ -107,7 +107,7 @@ module ActionMailer # end # end def assert_no_enqueued_emails(&block) - assert_no_enqueued_jobs only: ActionMailer::DeliveryJob, &block + assert_no_enqueued_jobs only: [ ActionMailer::DeliveryJob, ActionMailer::Parameterized::DeliveryJob ], &block end end end |