actionmailer 3.0.4 → 7.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +219 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +49 -55
  5. data/lib/action_mailer/base.rb +688 -387
  6. data/lib/action_mailer/callbacks.rb +31 -0
  7. data/lib/action_mailer/collector.rb +11 -9
  8. data/lib/action_mailer/delivery_methods.rb +35 -37
  9. data/lib/action_mailer/deprecator.rb +7 -0
  10. data/lib/action_mailer/form_builder.rb +37 -0
  11. data/lib/action_mailer/gem_version.rb +17 -0
  12. data/lib/action_mailer/inline_preview_interceptor.rb +59 -0
  13. data/lib/action_mailer/log_subscriber.rb +30 -8
  14. data/lib/action_mailer/mail_delivery_job.rb +48 -0
  15. data/lib/action_mailer/mail_helper.rb +59 -18
  16. data/lib/action_mailer/message_delivery.rb +156 -0
  17. data/lib/action_mailer/parameterized.rb +156 -0
  18. data/lib/action_mailer/preview.rb +166 -0
  19. data/lib/action_mailer/queued_delivery.rb +12 -0
  20. data/lib/action_mailer/railtie.rb +75 -7
  21. data/lib/action_mailer/rescuable.rb +33 -0
  22. data/lib/action_mailer/test_case.rb +75 -25
  23. data/lib/action_mailer/test_helper.rb +238 -15
  24. data/lib/action_mailer/version.rb +8 -7
  25. data/lib/action_mailer.rb +45 -18
  26. data/lib/rails/generators/mailer/USAGE +13 -8
  27. data/lib/rails/generators/mailer/mailer_generator.rb +26 -4
  28. data/lib/rails/generators/mailer/templates/application_mailer.rb.tt +6 -0
  29. data/lib/rails/generators/mailer/templates/mailer.rb.tt +17 -0
  30. metadata +175 -87
  31. data/CHANGELOG +0 -424
  32. data/lib/action_mailer/adv_attr_accessor.rb +0 -26
  33. data/lib/action_mailer/deprecated_api.rb +0 -147
  34. data/lib/action_mailer/old_api.rb +0 -259
  35. data/lib/action_mailer/tmail_compat.rb +0 -34
  36. data/lib/rails/generators/mailer/templates/mailer.rb +0 -16
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMailer
4
+ module Callbacks
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ include ActiveSupport::Callbacks
9
+ define_callbacks :deliver, skip_after_callbacks_if_terminated: true
10
+ end
11
+
12
+ module ClassMethods
13
+ # Defines a callback that will get called right before the
14
+ # message is sent to the delivery method.
15
+ def before_deliver(*filters, &blk)
16
+ set_callback(:deliver, :before, *filters, &blk)
17
+ end
18
+
19
+ # Defines a callback that will get called right after the
20
+ # message's delivery method is finished.
21
+ def after_deliver(*filters, &blk)
22
+ set_callback(:deliver, :after, *filters, &blk)
23
+ end
24
+
25
+ # Defines a callback that will get called around the message's deliver method.
26
+ def around_deliver(*filters, &blk)
27
+ set_callback(:deliver, :around, *filters, &blk)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,8 +1,10 @@
1
- require 'abstract_controller/collector'
2
- require 'active_support/core_ext/hash/reverse_merge'
3
- require 'active_support/core_ext/array/extract_options'
1
+ # frozen_string_literal: true
4
2
 
5
- module ActionMailer #:nodoc:
3
+ require "abstract_controller/collector"
4
+ require "active_support/core_ext/hash/reverse_merge"
5
+ require "active_support/core_ext/array/extract_options"
6
+
7
+ module ActionMailer
6
8
  class Collector
7
9
  include AbstractController::Collector
8
10
  attr_reader :responses
@@ -15,16 +17,16 @@ module ActionMailer #:nodoc:
15
17
 
16
18
  def any(*args, &block)
17
19
  options = args.extract_options!
18
- raise "You have to supply at least one format" if args.empty?
20
+ raise ArgumentError, "You have to supply at least one format" if args.empty?
19
21
  args.each { |type| send(type, options.dup, &block) }
20
22
  end
21
23
  alias :all :any
22
24
 
23
- def custom(mime, options={})
24
- options.reverse_merge!(:content_type => mime.to_s)
25
- @context.freeze_formats([mime.to_sym])
25
+ def custom(mime, options = {})
26
+ options.reverse_merge!(content_type: mime.to_s)
27
+ @context.formats = [mime.to_sym]
26
28
  options[:body] = block_given? ? yield : @default_render.call
27
29
  @responses << options
28
30
  end
29
31
  end
30
- end
32
+ end
@@ -1,72 +1,70 @@
1
- require 'tmpdir'
1
+ # frozen_string_literal: true
2
+
3
+ require "tmpdir"
2
4
 
3
5
  module ActionMailer
4
- # This modules handles everything related to the delivery, from registering new
5
- # delivery methods to configuring the mail object to be sent.
6
+ # = Action Mailer \DeliveryMethods
7
+ #
8
+ # This module handles everything related to mail delivery, from registering
9
+ # new delivery methods to configuring the mail object to be sent.
6
10
  module DeliveryMethods
7
11
  extend ActiveSupport::Concern
8
12
 
9
13
  included do
10
- class_attribute :delivery_methods, :delivery_method
11
-
12
14
  # Do not make this inheritable, because we always want it to propagate
13
- cattr_accessor :raise_delivery_errors
14
- self.raise_delivery_errors = true
15
-
16
- cattr_accessor :perform_deliveries
17
- self.perform_deliveries = true
15
+ cattr_accessor :raise_delivery_errors, default: true
16
+ cattr_accessor :perform_deliveries, default: true
18
17
 
19
- self.delivery_methods = {}.freeze
20
- self.delivery_method = :smtp
18
+ class_attribute :delivery_methods, default: {}.freeze
19
+ class_attribute :delivery_method, default: :smtp
21
20
 
22
21
  add_delivery_method :smtp, Mail::SMTP,
23
- :address => "localhost",
24
- :port => 25,
25
- :domain => 'localhost.localdomain',
26
- :user_name => nil,
27
- :password => nil,
28
- :authentication => nil,
29
- :enable_starttls_auto => true
22
+ address: "localhost",
23
+ port: 25,
24
+ domain: "localhost.localdomain",
25
+ user_name: nil,
26
+ password: nil,
27
+ authentication: nil,
28
+ enable_starttls_auto: true
30
29
 
31
30
  add_delivery_method :file, Mail::FileDelivery,
32
- :location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"
31
+ location: defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"
33
32
 
34
33
  add_delivery_method :sendmail, Mail::Sendmail,
35
- :location => '/usr/sbin/sendmail',
36
- :arguments => '-i -t'
34
+ location: "/usr/sbin/sendmail",
35
+ # See breaking change in the mail gem - https://github.com/mikel/mail/commit/7e1196bd29815a0901d7290c82a332c0959b163a
36
+ arguments: Gem::Version.new(Mail::VERSION.version) >= Gem::Version.new("2.8.0") ? %w[-i] : "-i"
37
37
 
38
38
  add_delivery_method :test, Mail::TestMailer
39
39
  end
40
40
 
41
+ # Helpers for creating and wrapping delivery behavior, used by DeliveryMethods.
41
42
  module ClassMethods
42
43
  # Provides a list of emails that have been delivered by Mail::TestMailer
43
- delegate :deliveries, :deliveries=, :to => Mail::TestMailer
44
+ delegate :deliveries, :deliveries=, to: Mail::TestMailer
44
45
 
45
- # Adds a new delivery method through the given class using the given symbol
46
- # as alias and the default options supplied:
47
- #
48
- # Example:
46
+ # Adds a new delivery method through the given class using the given
47
+ # symbol as alias and the default options supplied.
49
48
  #
50
49
  # add_delivery_method :sendmail, Mail::Sendmail,
51
- # :location => '/usr/sbin/sendmail',
52
- # :arguments => '-i -t'
53
- #
54
- def add_delivery_method(symbol, klass, default_options={})
50
+ # location: '/usr/sbin/sendmail',
51
+ # arguments: %w[ -i ]
52
+ def add_delivery_method(symbol, klass, default_options = {})
55
53
  class_attribute(:"#{symbol}_settings") unless respond_to?(:"#{symbol}_settings")
56
- send(:"#{symbol}_settings=", default_options)
54
+ public_send(:"#{symbol}_settings=", default_options)
57
55
  self.delivery_methods = delivery_methods.merge(symbol.to_sym => klass).freeze
58
56
  end
59
57
 
60
- def wrap_delivery_behavior(mail, method=nil) #:nodoc:
61
- method ||= self.delivery_method
58
+ def wrap_delivery_behavior(mail, method = nil, options = nil) # :nodoc:
59
+ method ||= delivery_method
62
60
  mail.delivery_handler = self
63
61
 
64
62
  case method
65
63
  when NilClass
66
64
  raise "Delivery method cannot be nil"
67
65
  when Symbol
68
- if klass = delivery_methods[method.to_sym]
69
- mail.delivery_method(klass, send(:"#{method}_settings"))
66
+ if klass = delivery_methods[method]
67
+ mail.delivery_method(klass, (send(:"#{method}_settings") || {}).merge(options || {}))
70
68
  else
71
69
  raise "Invalid delivery method #{method.inspect}"
72
70
  end
@@ -79,7 +77,7 @@ module ActionMailer
79
77
  end
80
78
  end
81
79
 
82
- def wrap_delivery_behavior!(*args) #:nodoc:
80
+ def wrap_delivery_behavior!(*args) # :nodoc:
83
81
  self.class.wrap_delivery_behavior(message, *args)
84
82
  end
85
83
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMailer
4
+ def self.deprecator # :nodoc:
5
+ @deprecator ||= ActiveSupport::Deprecation.new
6
+ end
7
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMailer
4
+ # = Action Mailer Form Builder
5
+ #
6
+ # Override the default form builder for all views rendered by this
7
+ # mailer and any of its descendants. Accepts a subclass of
8
+ # ActionView::Helpers::FormBuilder.
9
+ #
10
+ # While emails typically will not include forms, this can be used
11
+ # by views that are shared between controllers and mailers.
12
+ #
13
+ # For more information, see +ActionController::FormBuilder+.
14
+ module FormBuilder
15
+ extend ActiveSupport::Concern
16
+
17
+ included do
18
+ class_attribute :_default_form_builder, instance_accessor: false
19
+ end
20
+
21
+ module ClassMethods
22
+ # Set the form builder to be used as the default for all forms
23
+ # in the views rendered by this mailer and its subclasses.
24
+ #
25
+ # ==== Parameters
26
+ # * <tt>builder</tt> - Default form builder, an instance of ActionView::Helpers::FormBuilder
27
+ def default_form_builder(builder)
28
+ self._default_form_builder = builder
29
+ end
30
+ end
31
+
32
+ # Default form builder for the mailer
33
+ def default_form_builder
34
+ self.class._default_form_builder
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMailer
4
+ # Returns the currently loaded version of Action Mailer as a +Gem::Version+.
5
+ def self.gem_version
6
+ Gem::Version.new VERSION::STRING
7
+ end
8
+
9
+ module VERSION
10
+ MAJOR = 7
11
+ MINOR = 1
12
+ TINY = 6
13
+ PRE = nil
14
+
15
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
+ end
17
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+
5
+ module ActionMailer
6
+ # = Action Mailer \InlinePreviewInterceptor
7
+ #
8
+ # Implements a mailer preview interceptor that converts image tag src attributes
9
+ # that use inline cid: style URLs to data: style URLs so that they are visible
10
+ # when previewing an HTML email in a web browser.
11
+ #
12
+ # This interceptor is enabled by default. To disable it, delete it from the
13
+ # <tt>ActionMailer::Base.preview_interceptors</tt> array:
14
+ #
15
+ # ActionMailer::Base.preview_interceptors.delete(ActionMailer::InlinePreviewInterceptor)
16
+ #
17
+ class InlinePreviewInterceptor
18
+ PATTERN = /src=(?:"cid:[^"]+"|'cid:[^']+')/i
19
+
20
+ include Base64
21
+
22
+ def self.previewing_email(message) # :nodoc:
23
+ new(message).transform!
24
+ end
25
+
26
+ def initialize(message) # :nodoc:
27
+ @message = message
28
+ end
29
+
30
+ def transform! # :nodoc:
31
+ return message if html_part.blank?
32
+
33
+ html_part.body = html_part.decoded.gsub(PATTERN) do |match|
34
+ if part = find_part(match[9..-2])
35
+ %[src="#{data_url(part)}"]
36
+ else
37
+ match
38
+ end
39
+ end
40
+
41
+ message
42
+ end
43
+
44
+ private
45
+ attr_reader :message
46
+
47
+ def html_part
48
+ @html_part ||= message.html_part
49
+ end
50
+
51
+ def data_url(part)
52
+ "data:#{part.mime_type};base64,#{strict_encode64(part.body.raw_source)}"
53
+ end
54
+
55
+ def find_part(cid)
56
+ message.all_parts.find { |p| p.attachment? && p.cid == cid }
57
+ end
58
+ end
59
+ end
@@ -1,22 +1,44 @@
1
- require 'active_support/core_ext/array/wrap'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/log_subscriber"
2
4
 
3
5
  module ActionMailer
6
+ # = Action Mailer \LogSubscriber
7
+ #
8
+ # Implements the ActiveSupport::LogSubscriber for logging notifications when
9
+ # email is delivered or received.
4
10
  class LogSubscriber < ActiveSupport::LogSubscriber
11
+ # An email was delivered.
5
12
  def deliver(event)
6
- recipients = Array.wrap(event.payload[:to]).join(', ')
7
- info("\nSent mail to #{recipients} (%1.fms)" % event.duration)
8
- debug(event.payload[:mail])
13
+ info do
14
+ if exception = event.payload[:exception_object]
15
+ "Failed delivery of mail #{event.payload[:message_id]} error_class=#{exception.class} error_message=#{exception.message.inspect}"
16
+ elsif event.payload[:perform_deliveries]
17
+ "Delivered mail #{event.payload[:message_id]} (#{event.duration.round(1)}ms)"
18
+ else
19
+ "Skipped delivery of mail #{event.payload[:message_id]} as `perform_deliveries` is false"
20
+ end
21
+ end
22
+
23
+ debug { event.payload[:mail] }
9
24
  end
25
+ subscribe_log_level :deliver, :debug
10
26
 
11
- def receive(event)
12
- info("\nReceived mail (%.1fms)" % event.duration)
13
- debug(event.payload[:mail])
27
+ # An email was generated.
28
+ def process(event)
29
+ debug do
30
+ mailer = event.payload[:mailer]
31
+ action = event.payload[:action]
32
+ "#{mailer}##{action}: processed outbound mail in #{event.duration.round(1)}ms"
33
+ end
14
34
  end
35
+ subscribe_log_level :process, :debug
15
36
 
37
+ # Use the logger configured for ActionMailer::Base.
16
38
  def logger
17
39
  ActionMailer::Base.logger
18
40
  end
19
41
  end
20
42
  end
21
43
 
22
- ActionMailer::LogSubscriber.attach_to :action_mailer
44
+ ActionMailer::LogSubscriber.attach_to :action_mailer
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_job"
4
+
5
+ module ActionMailer
6
+ # = Action Mailer \MailDeliveryJob
7
+ #
8
+ # The +ActionMailer::MailDeliveryJob+ class is used when you
9
+ # want to send emails outside of the request-response cycle. It supports
10
+ # sending either parameterized or normal mail.
11
+ #
12
+ # Exceptions are rescued and handled by the mailer class.
13
+ class MailDeliveryJob < ActiveJob::Base # :nodoc:
14
+ queue_as do
15
+ mailer_class = arguments.first.constantize
16
+ mailer_class.deliver_later_queue_name
17
+ end
18
+
19
+ rescue_from StandardError, with: :handle_exception_with_mailer_class
20
+
21
+ def perform(mailer, mail_method, delivery_method, args:, kwargs: nil, params: nil)
22
+ mailer_class = params ? mailer.constantize.with(params) : mailer.constantize
23
+ message = if kwargs
24
+ mailer_class.public_send(mail_method, *args, **kwargs)
25
+ else
26
+ mailer_class.public_send(mail_method, *args)
27
+ end
28
+ message.send(delivery_method)
29
+ end
30
+
31
+ private
32
+ # "Deserialize" the mailer class name by hand in case another argument
33
+ # (like a Global ID reference) raised DeserializationError.
34
+ def mailer_class
35
+ if mailer = Array(@serialized_arguments).first || Array(arguments).first
36
+ mailer.constantize
37
+ end
38
+ end
39
+
40
+ def handle_exception_with_mailer_class(exception)
41
+ if klass = mailer_class
42
+ klass.handle_exception exception
43
+ else
44
+ raise exception
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,26 +1,42 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionMailer
4
+ # = Action Mailer \MailHelper
5
+ #
6
+ # Provides helper methods for ActionMailer::Base that can be used for easily
7
+ # formatting messages, accessing mailer or message instances, and the
8
+ # attachments list.
2
9
  module MailHelper
3
- # Uses Text::Format to take the text and format it, indented two spaces for
4
- # each line, and wrapped at 72 columns.
10
+ # Take the text and format it, indented two spaces for each line, and
11
+ # wrapped at 72 columns:
12
+ #
13
+ # text = <<-TEXT
14
+ # This is
15
+ # the paragraph.
16
+ #
17
+ # * item1 * item2
18
+ # TEXT
19
+ #
20
+ # block_format text
21
+ # # => " This is the paragraph.\n\n * item1\n * item2\n"
5
22
  def block_format(text)
6
- begin
7
- require 'text/format'
8
- rescue LoadError => e
9
- $stderr.puts "You don't have text-format installed in your application. Please add it to your Gemfile and run bundle install"
10
- raise e
11
- end unless defined?(Text::Format)
12
-
13
- formatted = text.split(/\n\r\n/).collect { |paragraph|
14
- Text::Format.new(
15
- :columns => 72, :first_indent => 2, :body_indent => 2, :text => paragraph
16
- ).format
17
- }.join("\n")
23
+ formatted = text.split(/\n\r?\n/).collect { |paragraph|
24
+ format_paragraph(paragraph)
25
+ }.join("\n\n")
18
26
 
19
27
  # Make list points stand on their own line
20
- formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { |s| " #{$1} #{$2.strip}\n" }
21
- formatted.gsub!(/[ ]*([#]+) ([^#]*)/) { |s| " #{$1} #{$2.strip}\n" }
28
+ output = +""
29
+ splits = formatted.split(/(\*+|\#+)/)
30
+ while line = splits.shift
31
+ if line.start_with?("*", "#") && splits.first&.start_with?(" ")
32
+ output.chomp!(" ") while output.end_with?(" ")
33
+ output << " #{line} #{splits.shift.strip}\n"
34
+ else
35
+ output << line
36
+ end
37
+ end
22
38
 
23
- formatted
39
+ output
24
40
  end
25
41
 
26
42
  # Access the mailer instance.
@@ -35,7 +51,32 @@ module ActionMailer
35
51
 
36
52
  # Access the message attachments list.
37
53
  def attachments
38
- @_message.attachments
54
+ mailer.attachments
55
+ end
56
+
57
+ # Returns +text+ wrapped at +len+ columns and indented +indent+ spaces.
58
+ # By default column length +len+ equals 72 characters and indent
59
+ # +indent+ equal two spaces.
60
+ #
61
+ # my_text = 'Here is a sample text with more than 40 characters'
62
+ #
63
+ # format_paragraph(my_text, 25, 4)
64
+ # # => " Here is a sample text with\n more than 40 characters"
65
+ def format_paragraph(text, len = 72, indent = 2)
66
+ sentences = [[]]
67
+
68
+ text.split.each do |word|
69
+ if sentences.first.present? && (sentences.last + [word]).join(" ").length > len
70
+ sentences << [word]
71
+ else
72
+ sentences.last << word
73
+ end
74
+ end
75
+
76
+ indentation = " " * indent
77
+ sentences.map! { |sentence|
78
+ "#{indentation}#{sentence.join(' ')}"
79
+ }.join "\n"
39
80
  end
40
81
  end
41
82
  end
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "delegate"
4
+
5
+ module ActionMailer
6
+ # = Action Mailer \MessageDelivery
7
+ #
8
+ # The +ActionMailer::MessageDelivery+ class is used by
9
+ # ActionMailer::Base when creating a new mailer.
10
+ # <tt>MessageDelivery</tt> is a wrapper (+Delegator+ subclass) around a lazy
11
+ # created +Mail::Message+. You can get direct access to the
12
+ # +Mail::Message+, deliver the email or schedule the email to be sent
13
+ # through Active Job.
14
+ #
15
+ # Notifier.welcome(User.first) # an ActionMailer::MessageDelivery object
16
+ # Notifier.welcome(User.first).deliver_now # sends the email
17
+ # Notifier.welcome(User.first).deliver_later # enqueue email delivery as a job through Active Job
18
+ # Notifier.welcome(User.first).message # a Mail::Message object
19
+ class MessageDelivery < Delegator
20
+ def initialize(mailer_class, action, *args) # :nodoc:
21
+ @mailer_class, @action, @args = mailer_class, action, args
22
+
23
+ # The mail is only processed if we try to call any methods on it.
24
+ # Typical usage will leave it unloaded and call deliver_later.
25
+ @processed_mailer = nil
26
+ @mail_message = nil
27
+ end
28
+ ruby2_keywords(:initialize)
29
+
30
+ # Method calls are delegated to the Mail::Message that's ready to deliver.
31
+ def __getobj__ # :nodoc:
32
+ @mail_message ||= processed_mailer.message
33
+ end
34
+
35
+ # Unused except for delegator internals (dup, marshalling).
36
+ def __setobj__(mail_message) # :nodoc:
37
+ @mail_message = mail_message
38
+ end
39
+
40
+ # Returns the resulting Mail::Message
41
+ def message
42
+ __getobj__
43
+ end
44
+
45
+ # Was the delegate loaded, causing the mailer action to be processed?
46
+ def processed?
47
+ @processed_mailer || @mail_message
48
+ end
49
+
50
+ # Enqueues the email to be delivered through Active Job. When the
51
+ # job runs it will send the email using +deliver_now!+. That means
52
+ # that the message will be sent bypassing checking +perform_deliveries+
53
+ # and +raise_delivery_errors+, so use with caution.
54
+ #
55
+ # Notifier.welcome(User.first).deliver_later!
56
+ # Notifier.welcome(User.first).deliver_later!(wait: 1.hour)
57
+ # Notifier.welcome(User.first).deliver_later!(wait_until: 10.hours.from_now)
58
+ # Notifier.welcome(User.first).deliver_later!(priority: 10)
59
+ #
60
+ # Options:
61
+ #
62
+ # * <tt>:wait</tt> - Enqueue the email to be delivered with a delay
63
+ # * <tt>:wait_until</tt> - Enqueue the email to be delivered at (after) a specific date / time
64
+ # * <tt>:queue</tt> - Enqueue the email on the specified queue
65
+ # * <tt>:priority</tt> - Enqueues the email with the specified priority
66
+ #
67
+ # By default, the email will be enqueued using ActionMailer::MailDeliveryJob on
68
+ # the default queue. Mailer classes can customize the queue name used for the default
69
+ # job by assigning a +deliver_later_queue_name+ class variable, or provide a custom job
70
+ # by assigning a +delivery_job+. When a custom job is used, it controls the queue name.
71
+ #
72
+ # class AccountRegistrationMailer < ApplicationMailer
73
+ # self.delivery_job = RegistrationDeliveryJob
74
+ # end
75
+ def deliver_later!(options = {})
76
+ enqueue_delivery :deliver_now!, options
77
+ end
78
+
79
+ # Enqueues the email to be delivered through Active Job. When the
80
+ # job runs it will send the email using +deliver_now+.
81
+ #
82
+ # Notifier.welcome(User.first).deliver_later
83
+ # Notifier.welcome(User.first).deliver_later(wait: 1.hour)
84
+ # Notifier.welcome(User.first).deliver_later(wait_until: 10.hours.from_now)
85
+ # Notifier.welcome(User.first).deliver_later(priority: 10)
86
+ #
87
+ # Options:
88
+ #
89
+ # * <tt>:wait</tt> - Enqueue the email to be delivered with a delay.
90
+ # * <tt>:wait_until</tt> - Enqueue the email to be delivered at (after) a specific date / time.
91
+ # * <tt>:queue</tt> - Enqueue the email on the specified queue.
92
+ # * <tt>:priority</tt> - Enqueues the email with the specified priority
93
+ #
94
+ # By default, the email will be enqueued using ActionMailer::MailDeliveryJob on
95
+ # the default queue. Mailer classes can customize the queue name used for the default
96
+ # job by assigning a +deliver_later_queue_name+ class variable, or provide a custom job
97
+ # by assigning a +delivery_job+. When a custom job is used, it controls the queue name.
98
+ #
99
+ # class AccountRegistrationMailer < ApplicationMailer
100
+ # self.delivery_job = RegistrationDeliveryJob
101
+ # end
102
+ def deliver_later(options = {})
103
+ enqueue_delivery :deliver_now, options
104
+ end
105
+
106
+ # Delivers an email without checking +perform_deliveries+ and +raise_delivery_errors+,
107
+ # so use with caution.
108
+ #
109
+ # Notifier.welcome(User.first).deliver_now!
110
+ #
111
+ def deliver_now!
112
+ processed_mailer.handle_exceptions do
113
+ processed_mailer.run_callbacks(:deliver) do
114
+ message.deliver!
115
+ end
116
+ end
117
+ end
118
+
119
+ # Delivers an email:
120
+ #
121
+ # Notifier.welcome(User.first).deliver_now
122
+ #
123
+ def deliver_now
124
+ processed_mailer.handle_exceptions do
125
+ processed_mailer.run_callbacks(:deliver) do
126
+ message.deliver
127
+ end
128
+ end
129
+ end
130
+
131
+ private
132
+ # Returns the processed Mailer instance. We keep this instance
133
+ # on hand so we can run callbacks and delegate exception handling to it.
134
+ def processed_mailer
135
+ @processed_mailer ||= @mailer_class.new.tap do |mailer|
136
+ mailer.process @action, *@args
137
+ end
138
+ end
139
+
140
+ def enqueue_delivery(delivery_method, options = {})
141
+ if processed?
142
+ ::Kernel.raise "You've accessed the message before asking to " \
143
+ "deliver it later, so you may have made local changes that would " \
144
+ "be silently lost if we enqueued a job to deliver it. Why? Only " \
145
+ "the mailer method *arguments* are passed with the delivery job! " \
146
+ "Do not access the message in any way if you mean to deliver it " \
147
+ "later. Workarounds: 1. don't touch the message before calling " \
148
+ "#deliver_later, 2. only touch the message *within your mailer " \
149
+ "method*, or 3. use a custom Active Job instead of #deliver_later."
150
+ else
151
+ @mailer_class.delivery_job.set(options).perform_later(
152
+ @mailer_class.name, @action.to_s, delivery_method.to_s, args: @args)
153
+ end
154
+ end
155
+ end
156
+ end