actionmailer 7.0.8.7 → 7.2.2.1

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.
@@ -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
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionMailer
4
- # Returns the currently loaded version of Action Mailer as a <tt>Gem::Version</tt>.
4
+ # Returns the currently loaded version of Action Mailer as a +Gem::Version+.
5
5
  def self.gem_version
6
6
  Gem::Version.new VERSION::STRING
7
7
  end
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 7
11
- MINOR = 0
12
- TINY = 8
13
- PRE = "7"
11
+ MINOR = 2
12
+ TINY = 2
13
+ PRE = "1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -3,6 +3,8 @@
3
3
  require "base64"
4
4
 
5
5
  module ActionMailer
6
+ # = Action Mailer \InlinePreviewInterceptor
7
+ #
6
8
  # Implements a mailer preview interceptor that converts image tag src attributes
7
9
  # that use inline cid: style URLs to data: style URLs so that they are visible
8
10
  # when previewing an HTML email in a web browser.
@@ -3,14 +3,17 @@
3
3
  require "active_support/log_subscriber"
4
4
 
5
5
  module ActionMailer
6
+ # = Action Mailer \LogSubscriber
7
+ #
6
8
  # Implements the ActiveSupport::LogSubscriber for logging notifications when
7
9
  # email is delivered or received.
8
10
  class LogSubscriber < ActiveSupport::LogSubscriber
9
11
  # An email was delivered.
10
12
  def deliver(event)
11
13
  info do
12
- perform_deliveries = event.payload[:perform_deliveries]
13
- if perform_deliveries
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]
14
17
  "Delivered mail #{event.payload[:message_id]} (#{event.duration.round(1)}ms)"
15
18
  else
16
19
  "Skipped delivery of mail #{event.payload[:message_id]} as `perform_deliveries` is false"
@@ -19,6 +22,7 @@ module ActionMailer
19
22
 
20
23
  debug { event.payload[:mail] }
21
24
  end
25
+ subscribe_log_level :deliver, :debug
22
26
 
23
27
  # An email was generated.
24
28
  def process(event)
@@ -28,6 +32,7 @@ module ActionMailer
28
32
  "#{mailer}##{action}: processed outbound mail in #{event.duration.round(1)}ms"
29
33
  end
30
34
  end
35
+ subscribe_log_level :process, :debug
31
36
 
32
37
  # Use the logger configured for ActionMailer::Base.
33
38
  def logger
@@ -3,13 +3,18 @@
3
3
  require "active_job"
4
4
 
5
5
  module ActionMailer
6
- # The <tt>ActionMailer::MailDeliveryJob</tt> class is used when you
6
+ # = Action Mailer \MailDeliveryJob
7
+ #
8
+ # The +ActionMailer::MailDeliveryJob+ class is used when you
7
9
  # want to send emails outside of the request-response cycle. It supports
8
10
  # sending either parameterized or normal mail.
9
11
  #
10
12
  # Exceptions are rescued and handled by the mailer class.
11
13
  class MailDeliveryJob < ActiveJob::Base # :nodoc:
12
- queue_as { ActionMailer::Base.deliver_later_queue_name }
14
+ queue_as do
15
+ mailer_class = arguments.first.constantize
16
+ mailer_class.deliver_later_queue_name
17
+ end
13
18
 
14
19
  rescue_from StandardError, with: :handle_exception_with_mailer_class
15
20
 
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionMailer
4
+ # = Action Mailer \MailHelper
5
+ #
4
6
  # Provides helper methods for ActionMailer::Base that can be used for easily
5
7
  # formatting messages, accessing mailer or message instances, and the
6
8
  # attachments list.
@@ -3,11 +3,13 @@
3
3
  require "delegate"
4
4
 
5
5
  module ActionMailer
6
- # The <tt>ActionMailer::MessageDelivery</tt> class is used by
6
+ # = Action Mailer \MessageDelivery
7
+ #
8
+ # The +ActionMailer::MessageDelivery+ class is used by
7
9
  # ActionMailer::Base when creating a new mailer.
8
10
  # <tt>MessageDelivery</tt> is a wrapper (+Delegator+ subclass) around a lazy
9
- # created <tt>Mail::Message</tt>. You can get direct access to the
10
- # <tt>Mail::Message</tt>, deliver the email or schedule the email to be sent
11
+ # created +Mail::Message+. You can get direct access to the
12
+ # +Mail::Message+, deliver the email or schedule the email to be sent
11
13
  # through Active Job.
12
14
  #
13
15
  # Notifier.welcome(User.first) # an ActionMailer::MessageDelivery object
@@ -62,9 +64,10 @@ module ActionMailer
62
64
  # * <tt>:queue</tt> - Enqueue the email on the specified queue
63
65
  # * <tt>:priority</tt> - Enqueues the email with the specified priority
64
66
  #
65
- # By default, the email will be enqueued using <tt>ActionMailer::MailDeliveryJob</tt>. Each
66
- # ActionMailer::Base class can specify the job to use by setting the class variable
67
- # +delivery_job+.
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.
68
71
  #
69
72
  # class AccountRegistrationMailer < ApplicationMailer
70
73
  # self.delivery_job = RegistrationDeliveryJob
@@ -88,9 +91,10 @@ module ActionMailer
88
91
  # * <tt>:queue</tt> - Enqueue the email on the specified queue.
89
92
  # * <tt>:priority</tt> - Enqueues the email with the specified priority
90
93
  #
91
- # By default, the email will be enqueued using <tt>ActionMailer::MailDeliveryJob</tt>. Each
92
- # ActionMailer::Base class can specify the job to use by setting the class variable
93
- # +delivery_job+.
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.
94
98
  #
95
99
  # class AccountRegistrationMailer < ApplicationMailer
96
100
  # self.delivery_job = RegistrationDeliveryJob
@@ -106,7 +110,9 @@ module ActionMailer
106
110
  #
107
111
  def deliver_now!
108
112
  processed_mailer.handle_exceptions do
109
- message.deliver!
113
+ processed_mailer.run_callbacks(:deliver) do
114
+ message.deliver!
115
+ end
110
116
  end
111
117
  end
112
118
 
@@ -116,13 +122,15 @@ module ActionMailer
116
122
  #
117
123
  def deliver_now
118
124
  processed_mailer.handle_exceptions do
119
- message.deliver
125
+ processed_mailer.run_callbacks(:deliver) do
126
+ message.deliver
127
+ end
120
128
  end
121
129
  end
122
130
 
123
131
  private
124
132
  # Returns the processed Mailer instance. We keep this instance
125
- # on hand so we can delegate exception handling to it.
133
+ # on hand so we can run callbacks and delegate exception handling to it.
126
134
  def processed_mailer
127
135
  @processed_mailer ||= @mailer_class.new.tap do |mailer|
128
136
  mailer.process @action, *@args
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionMailer
4
+ # = Action Mailer \Parameterized
5
+ #
4
6
  # Provides the option to parameterize mailers in order to share instance variable
5
7
  # setup, processing, and common headers.
6
8
  #
@@ -88,7 +90,11 @@ module ActionMailer
88
90
  extend ActiveSupport::Concern
89
91
 
90
92
  included do
91
- attr_accessor :params
93
+ attr_writer :params
94
+
95
+ def params
96
+ @params ||= {}
97
+ end
92
98
  end
93
99
 
94
100
  module ClassMethods
@@ -108,14 +114,13 @@ module ActionMailer
108
114
  end
109
115
 
110
116
  private
111
- def method_missing(method_name, *args)
112
- if @mailer.action_methods.include?(method_name.to_s)
113
- ActionMailer::Parameterized::MessageDelivery.new(@mailer, method_name, @params, *args)
117
+ def method_missing(method_name, ...)
118
+ if @mailer.action_methods.include?(method_name.name)
119
+ ActionMailer::Parameterized::MessageDelivery.new(@mailer, method_name, @params, ...)
114
120
  else
115
121
  super
116
122
  end
117
123
  end
118
- ruby2_keywords(:method_missing)
119
124
 
120
125
  def respond_to_missing?(method, include_all = false)
121
126
  @mailer.respond_to?(method, include_all)
@@ -123,11 +128,10 @@ module ActionMailer
123
128
  end
124
129
 
125
130
  class MessageDelivery < ActionMailer::MessageDelivery # :nodoc:
126
- def initialize(mailer_class, action, params, *args)
127
- super(mailer_class, action, *args)
131
+ def initialize(mailer_class, action, params, ...)
132
+ super(mailer_class, action, ...)
128
133
  @params = params
129
134
  end
130
- ruby2_keywords(:initialize)
131
135
 
132
136
  private
133
137
  def processed_mailer
@@ -7,11 +7,11 @@ module ActionMailer
7
7
  extend ActiveSupport::Concern
8
8
 
9
9
  included do
10
- # Set the location of mailer previews through app configuration:
10
+ # Add the location of mailer previews through app configuration:
11
11
  #
12
- # config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews"
12
+ # config.action_mailer.preview_paths << "#{Rails.root}/lib/mailer_previews"
13
13
  #
14
- mattr_accessor :preview_path, instance_writer: false
14
+ mattr_accessor :preview_paths, instance_writer: false, default: []
15
15
 
16
16
  # Enable or disable mailer previews through app configuration:
17
17
  #
@@ -79,7 +79,7 @@ module ActionMailer
79
79
  # Returns all mailer preview classes.
80
80
  def all
81
81
  load_previews if descendants.empty?
82
- descendants
82
+ descendants.sort_by { |mailer| mailer.name.titleize }
83
83
  end
84
84
 
85
85
  # Returns the mail object for the given email name. The registered preview
@@ -119,13 +119,13 @@ module ActionMailer
119
119
 
120
120
  private
121
121
  def load_previews
122
- if preview_path
123
- Dir["#{preview_path}/**/*_preview.rb"].sort.each { |file| require_dependency file }
122
+ preview_paths.each do |preview_path|
123
+ Dir["#{preview_path}/**/*_preview.rb"].sort.each { |file| require file }
124
124
  end
125
125
  end
126
126
 
127
- def preview_path
128
- Base.preview_path
127
+ def preview_paths
128
+ Base.preview_paths
129
129
  end
130
130
 
131
131
  def show_previews
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMailer
4
+ module QueuedDelivery
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ class_attribute :delivery_job, default: ::ActionMailer::MailDeliveryJob
9
+ class_attribute :deliver_later_queue_name, default: :mailers
10
+ end
11
+ end
12
+ end
@@ -8,8 +8,13 @@ require "abstract_controller/railties/routes_helpers"
8
8
  module ActionMailer
9
9
  class Railtie < Rails::Railtie # :nodoc:
10
10
  config.action_mailer = ActiveSupport::OrderedOptions.new
11
+ config.action_mailer.preview_paths = []
11
12
  config.eager_load_namespaces << ActionMailer
12
13
 
14
+ initializer "action_mailer.deprecator", before: :load_environment_config do |app|
15
+ app.deprecators[:action_mailer] = ActionMailer.deprecator
16
+ end
17
+
13
18
  initializer "action_mailer.logger" do
14
19
  ActiveSupport.on_load(:action_mailer) { self.logger ||= Rails.logger }
15
20
  end
@@ -23,10 +28,7 @@ module ActionMailer
23
28
  options.stylesheets_dir ||= paths["public/stylesheets"].first
24
29
  options.show_previews = Rails.env.development? if options.show_previews.nil?
25
30
  options.cache_store ||= Rails.cache
26
-
27
- if options.show_previews
28
- options.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/mailers/previews" : nil
29
- end
31
+ options.preview_paths |= ["#{Rails.root}/test/mailers/previews"]
30
32
 
31
33
  # make sure readers methods get compiled
32
34
  options.asset_host ||= app.config.asset_host
@@ -40,6 +42,7 @@ module ActionMailer
40
42
  register_interceptors(options.delete(:interceptors))
41
43
  register_preview_interceptors(options.delete(:preview_interceptors))
42
44
  register_observers(options.delete(:observers))
45
+ self.preview_paths |= options[:preview_paths]
43
46
 
44
47
  if delivery_job = options.delete(:delivery_job)
45
48
  self.delivery_job = delivery_job.constantize
@@ -65,12 +68,9 @@ module ActionMailer
65
68
  end
66
69
  end
67
70
 
68
- initializer "action_mailer.set_autoload_paths" do |app|
71
+ initializer "action_mailer.set_autoload_paths", before: :set_autoload_paths do |app|
69
72
  options = app.config.action_mailer
70
-
71
- if options.show_previews && options.preview_path
72
- ActiveSupport::Dependencies.autoload_paths << options.preview_path
73
- end
73
+ app.config.paths["test/mailers/previews"].concat(options.preview_paths)
74
74
  end
75
75
 
76
76
  initializer "action_mailer.compile_config_methods" do
@@ -79,18 +79,13 @@ module ActionMailer
79
79
  end
80
80
  end
81
81
 
82
- initializer "action_mailer.eager_load_actions" do
83
- ActiveSupport.on_load(:after_initialize) do
84
- ActionMailer::Base.descendants.each(&:action_methods) if config.eager_load
85
- end
86
- end
87
-
88
82
  config.after_initialize do |app|
89
83
  options = app.config.action_mailer
90
84
 
91
85
  if options.show_previews
92
86
  app.routes.prepend do
93
- get "/rails/mailers" => "rails/mailers#index", internal: true
87
+ get "/rails/mailers" => "rails/mailers#index", internal: true
88
+ get "/rails/mailers/download/*path" => "rails/mailers#download", internal: true
94
89
  get "/rails/mailers/*path" => "rails/mailers#preview", internal: true
95
90
  end
96
91
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionMailer # :nodoc:
4
+ # = Action Mailer \Rescuable
5
+ #
4
6
  # Provides
5
7
  # {rescue_from}[rdoc-ref:ActiveSupport::Rescuable::ClassMethods#rescue_from]
6
8
  # for mailers. Wraps mailer action processing, mail job processing, and mail
@@ -74,6 +74,15 @@ module ActionMailer
74
74
  end
75
75
  end
76
76
 
77
+ # Reads the fixture file for the given mailer.
78
+ #
79
+ # This is useful when testing mailers by being able to write the body of
80
+ # an email inside a fixture. See the testing guide for a concrete example:
81
+ # https://guides.rubyonrails.org/testing.html#revenge-of-the-fixtures
82
+ def read_fixture(action)
83
+ IO.readlines(File.join(Rails.root, "test", "fixtures", self.class.mailer_class.name.underscore, action))
84
+ end
85
+
77
86
  private
78
87
  def initialize_test_deliveries
79
88
  set_delivery_method :test
@@ -110,10 +119,6 @@ module ActionMailer
110
119
  def encode(subject)
111
120
  Mail::Encodings.q_value_encode(subject, charset)
112
121
  end
113
-
114
- def read_fixture(action)
115
- IO.readlines(File.join(Rails.root, "test", "fixtures", self.class.mailer_class.name.underscore, action))
116
- end
117
122
  end
118
123
 
119
124
  include Behavior
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/array/extract_options"
3
4
  require "active_job"
4
5
 
5
6
  module ActionMailer
@@ -33,10 +34,8 @@ module ActionMailer
33
34
  # end
34
35
  def assert_emails(number, &block)
35
36
  if block_given?
36
- original_count = ActionMailer::Base.deliveries.size
37
- perform_enqueued_jobs(only: ->(job) { delivery_job_filter(job) }, &block)
38
- new_count = ActionMailer::Base.deliveries.size
39
- assert_equal number, new_count - original_count, "#{number} emails expected, but #{new_count - original_count} were sent"
37
+ diff = capture_emails(&block).length
38
+ assert_equal number, diff, "#{number} emails expected, but #{diff} were sent"
40
39
  else
41
40
  assert_equal number, ActionMailer::Base.deliveries.size
42
41
  end
@@ -94,18 +93,50 @@ module ActionMailer
94
93
  end
95
94
 
96
95
  # Asserts that a specific email has been enqueued, optionally
97
- # matching arguments.
96
+ # matching arguments and/or params.
98
97
  #
99
98
  # def test_email
100
99
  # ContactMailer.welcome.deliver_later
101
100
  # assert_enqueued_email_with ContactMailer, :welcome
102
101
  # end
103
102
  #
103
+ # def test_email_with_parameters
104
+ # ContactMailer.with(greeting: "Hello").welcome.deliver_later
105
+ # assert_enqueued_email_with ContactMailer, :welcome, args: { greeting: "Hello" }
106
+ # end
107
+ #
104
108
  # def test_email_with_arguments
105
109
  # ContactMailer.welcome("Hello", "Goodbye").deliver_later
106
110
  # assert_enqueued_email_with ContactMailer, :welcome, args: ["Hello", "Goodbye"]
107
111
  # end
108
112
  #
113
+ # def test_email_with_named_arguments
114
+ # ContactMailer.welcome(greeting: "Hello", farewell: "Goodbye").deliver_later
115
+ # assert_enqueued_email_with ContactMailer, :welcome, args: [{ greeting: "Hello", farewell: "Goodbye" }]
116
+ # end
117
+ #
118
+ # def test_email_with_parameters_and_arguments
119
+ # ContactMailer.with(greeting: "Hello").welcome("Cheers", "Goodbye").deliver_later
120
+ # assert_enqueued_email_with ContactMailer, :welcome, params: { greeting: "Hello" }, args: ["Cheers", "Goodbye"]
121
+ # end
122
+ #
123
+ # def test_email_with_parameters_and_named_arguments
124
+ # ContactMailer.with(greeting: "Hello").welcome(farewell: "Goodbye").deliver_later
125
+ # assert_enqueued_email_with ContactMailer, :welcome, params: { greeting: "Hello" }, args: [{farewell: "Goodbye"}]
126
+ # end
127
+ #
128
+ # def test_email_with_parameterized_mailer
129
+ # ContactMailer.with(greeting: "Hello").welcome.deliver_later
130
+ # assert_enqueued_email_with ContactMailer.with(greeting: "Hello"), :welcome
131
+ # end
132
+ #
133
+ # def test_email_with_matchers
134
+ # ContactMailer.with(greeting: "Hello").welcome("Cheers", "Goodbye").deliver_later
135
+ # assert_enqueued_email_with ContactMailer, :welcome,
136
+ # params: ->(params) { /hello/i.match?(params[:greeting]) },
137
+ # args: ->(args) { /cheers/i.match?(args[0]) }
138
+ # end
139
+ #
109
140
  # If a block is passed, that block should cause the specified email
110
141
  # to be enqueued.
111
142
  #
@@ -123,13 +154,23 @@ module ActionMailer
123
154
  # ContactMailer.with(email: 'user@example.com').welcome.deliver_later
124
155
  # end
125
156
  # end
126
- def assert_enqueued_email_with(mailer, method, args: nil, queue: ActionMailer::Base.deliver_later_queue_name || "default", &block)
127
- args = if args.is_a?(Hash)
128
- [mailer.to_s, method.to_s, "deliver_now", params: args, args: []]
129
- else
130
- [mailer.to_s, method.to_s, "deliver_now", args: Array(args)]
157
+ def assert_enqueued_email_with(mailer, method, params: nil, args: nil, queue: nil, &block)
158
+ if mailer.is_a? ActionMailer::Parameterized::Mailer
159
+ params = mailer.instance_variable_get(:@params)
160
+ mailer = mailer.instance_variable_get(:@mailer)
131
161
  end
132
- assert_enqueued_with(job: mailer.delivery_job, args: args, queue: queue.to_s, &block)
162
+
163
+ args = Array(args) unless args.is_a?(Proc)
164
+ queue ||= mailer.deliver_later_queue_name || ActiveJob::Base.default_queue_name
165
+
166
+ expected = ->(job_args) do
167
+ job_kwargs = job_args.extract_options!
168
+
169
+ [mailer.to_s, method.to_s, "deliver_now"] == job_args &&
170
+ params === job_kwargs[:params] && args === job_kwargs[:args]
171
+ end
172
+
173
+ assert_enqueued_with(job: mailer.delivery_job, args: expected, queue: queue.to_s, &block)
133
174
  end
134
175
 
135
176
  # Asserts that no emails are enqueued for later delivery.
@@ -151,6 +192,68 @@ module ActionMailer
151
192
  assert_enqueued_emails 0, &block
152
193
  end
153
194
 
195
+ # Delivers all enqueued emails. If a block is given, delivers all of the emails
196
+ # that were enqueued throughout the duration of the block. If a block is
197
+ # not given, delivers all the enqueued emails up to this point in the test.
198
+ #
199
+ # def test_deliver_enqueued_emails
200
+ # deliver_enqueued_emails do
201
+ # ContactMailer.welcome.deliver_later
202
+ # end
203
+ #
204
+ # assert_emails 1
205
+ # end
206
+ #
207
+ # def test_deliver_enqueued_emails_without_block
208
+ # ContactMailer.welcome.deliver_later
209
+ #
210
+ # deliver_enqueued_emails
211
+ #
212
+ # assert_emails 1
213
+ # end
214
+ #
215
+ # If the +:queue+ option is specified,
216
+ # then only the emails(s) enqueued to a specific queue will be performed.
217
+ #
218
+ # def test_deliver_enqueued_emails_with_queue
219
+ # deliver_enqueued_emails queue: :external_mailers do
220
+ # CustomerMailer.deliver_later_queue_name = :external_mailers
221
+ # CustomerMailer.welcome.deliver_later # will be performed
222
+ # EmployeeMailer.deliver_later_queue_name = :internal_mailers
223
+ # EmployeeMailer.welcome.deliver_later # will not be performed
224
+ # end
225
+ #
226
+ # assert_emails 1
227
+ # end
228
+ #
229
+ # If the +:at+ option is specified, then only delivers emails enqueued to deliver
230
+ # immediately or before the given time.
231
+ def deliver_enqueued_emails(queue: nil, at: nil, &block)
232
+ perform_enqueued_jobs(only: ->(job) { delivery_job_filter(job) }, queue: queue, at: at, &block)
233
+ end
234
+
235
+ # Returns any emails that are sent in the block.
236
+ #
237
+ # def test_emails
238
+ # emails = capture_emails do
239
+ # ContactMailer.welcome.deliver_now
240
+ # end
241
+ # assert_equal "Hi there", emails.first.subject
242
+ #
243
+ # emails = capture_emails do
244
+ # ContactMailer.welcome.deliver_now
245
+ # ContactMailer.welcome.deliver_later
246
+ # end
247
+ # assert_equal "Hi there", emails.first.subject
248
+ # end
249
+ def capture_emails(&block)
250
+ original_count = ActionMailer::Base.deliveries.size
251
+ deliver_enqueued_emails(&block)
252
+ new_count = ActionMailer::Base.deliveries.size
253
+ diff = new_count - original_count
254
+ ActionMailer::Base.deliveries.last(diff)
255
+ end
256
+
154
257
  private
155
258
  def delivery_job_filter(job)
156
259
  job_class = job.is_a?(Hash) ? job.fetch(:job) : job.class
@@ -4,7 +4,7 @@ require_relative "gem_version"
4
4
 
5
5
  module ActionMailer
6
6
  # Returns the currently loaded version of Action Mailer as a
7
- # <tt>Gem::Version</tt>.
7
+ # +Gem::Version+.
8
8
  def self.version
9
9
  gem_version
10
10
  end
data/lib/action_mailer.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright (c) 2004-2022 David Heinemeier Hansson
4
+ # Copyright (c) David Heinemeier Hansson
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining
7
7
  # a copy of this software and associated documentation files (the
@@ -25,6 +25,7 @@
25
25
 
26
26
  require "abstract_controller"
27
27
  require "action_mailer/version"
28
+ require "action_mailer/deprecator"
28
29
 
29
30
  # Common Active Support usage in Action Mailer
30
31
  require "active_support"
@@ -34,6 +35,7 @@ require "active_support/core_ext/module/attr_internal"
34
35
  require "active_support/core_ext/string/inflections"
35
36
  require "active_support/lazy_load_hooks"
36
37
 
38
+ # :include: ../README.rdoc
37
39
  module ActionMailer
38
40
  extend ::ActiveSupport::Autoload
39
41
 
@@ -42,6 +44,7 @@ module ActionMailer
42
44
  end
43
45
 
44
46
  autoload :Base
47
+ autoload :Callbacks
45
48
  autoload :DeliveryMethods
46
49
  autoload :InlinePreviewInterceptor
47
50
  autoload :MailHelper
@@ -52,12 +55,18 @@ module ActionMailer
52
55
  autoload :TestHelper
53
56
  autoload :MessageDelivery
54
57
  autoload :MailDeliveryJob
58
+ autoload :QueuedDelivery
59
+ autoload :FormBuilder
55
60
 
56
61
  def self.eager_load!
57
62
  super
58
63
 
59
64
  require "mail"
60
65
  Mail.eager_autoload!
66
+
67
+ Base.descendants.each do |mailer|
68
+ mailer.eager_load! unless mailer.abstract?
69
+ end
61
70
  end
62
71
  end
63
72
 
@@ -65,6 +74,6 @@ autoload :Mime, "action_dispatch/http/mime_type"
65
74
 
66
75
  ActiveSupport.on_load(:action_view) do
67
76
  ActionView::Base.default_formats ||= Mime::SET.symbols
68
- ActionView::Template::Types.delegate_to Mime
77
+ ActionView::Template.mime_types_implementation = Mime
69
78
  ActionView::LookupContext::DetailsKey.clear
70
79
  end