actionmailer 7.0.8 → 7.1.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +97 -65
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -2
- data/lib/action_mailer/base.rb +58 -46
- data/lib/action_mailer/callbacks.rb +31 -0
- data/lib/action_mailer/delivery_methods.rb +5 -3
- data/lib/action_mailer/deprecator.rb +7 -0
- data/lib/action_mailer/form_builder.rb +37 -0
- data/lib/action_mailer/gem_version.rb +4 -4
- data/lib/action_mailer/inline_preview_interceptor.rb +2 -0
- data/lib/action_mailer/log_subscriber.rb +7 -2
- data/lib/action_mailer/mail_delivery_job.rb +7 -2
- data/lib/action_mailer/mail_helper.rb +2 -0
- data/lib/action_mailer/message_delivery.rb +20 -12
- data/lib/action_mailer/parameterized.rb +7 -1
- data/lib/action_mailer/preview.rb +32 -8
- data/lib/action_mailer/queued_delivery.rb +12 -0
- data/lib/action_mailer/railtie.rb +11 -16
- data/lib/action_mailer/rescuable.rb +2 -0
- data/lib/action_mailer/test_case.rb +9 -4
- data/lib/action_mailer/test_helper.rb +134 -11
- data/lib/action_mailer/version.rb +1 -1
- data/lib/action_mailer.rb +11 -2
- data/lib/rails/generators/mailer/USAGE +12 -8
- metadata +20 -16
@@ -3,6 +3,8 @@
|
|
3
3
|
require "tmpdir"
|
4
4
|
|
5
5
|
module ActionMailer
|
6
|
+
# = Action Mailer \DeliveryMethods
|
7
|
+
#
|
6
8
|
# This module handles everything related to mail delivery, from registering
|
7
9
|
# new delivery methods to configuring the mail object to be sent.
|
8
10
|
module DeliveryMethods
|
@@ -12,7 +14,6 @@ module ActionMailer
|
|
12
14
|
# Do not make this inheritable, because we always want it to propagate
|
13
15
|
cattr_accessor :raise_delivery_errors, default: true
|
14
16
|
cattr_accessor :perform_deliveries, default: true
|
15
|
-
cattr_accessor :deliver_later_queue_name, default: :mailers
|
16
17
|
|
17
18
|
class_attribute :delivery_methods, default: {}.freeze
|
18
19
|
class_attribute :delivery_method, default: :smtp
|
@@ -31,7 +32,8 @@ module ActionMailer
|
|
31
32
|
|
32
33
|
add_delivery_method :sendmail, Mail::Sendmail,
|
33
34
|
location: "/usr/sbin/sendmail",
|
34
|
-
|
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"
|
35
37
|
|
36
38
|
add_delivery_method :test, Mail::TestMailer
|
37
39
|
end
|
@@ -46,7 +48,7 @@ module ActionMailer
|
|
46
48
|
#
|
47
49
|
# add_delivery_method :sendmail, Mail::Sendmail,
|
48
50
|
# location: '/usr/sbin/sendmail',
|
49
|
-
# arguments:
|
51
|
+
# arguments: %w[ -i ]
|
50
52
|
def add_delivery_method(symbol, klass, default_options = {})
|
51
53
|
class_attribute(:"#{symbol}_settings") unless respond_to?(:"#{symbol}_settings")
|
52
54
|
public_send(:"#{symbol}_settings=", default_options)
|
@@ -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
|
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 =
|
12
|
-
TINY =
|
13
|
-
PRE =
|
11
|
+
MINOR = 1
|
12
|
+
TINY = 3
|
13
|
+
PRE = "2"
|
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
|
-
|
13
|
-
|
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
|
-
#
|
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
|
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
|
|
@@ -3,11 +3,13 @@
|
|
3
3
|
require "delegate"
|
4
4
|
|
5
5
|
module ActionMailer
|
6
|
-
#
|
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
|
10
|
-
#
|
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
|
66
|
-
#
|
67
|
-
# +
|
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
|
92
|
-
#
|
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.
|
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
|
-
|
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
|
-
|
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
|
-
|
93
|
+
attr_writer :params
|
94
|
+
|
95
|
+
def params
|
96
|
+
@params ||= {}
|
97
|
+
end
|
92
98
|
end
|
93
99
|
|
94
100
|
module ClassMethods
|
@@ -7,11 +7,11 @@ module ActionMailer
|
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
9
|
included do
|
10
|
-
#
|
10
|
+
# Add the location of mailer previews through app configuration:
|
11
11
|
#
|
12
|
-
# config.action_mailer.
|
12
|
+
# config.action_mailer.preview_paths << "#{Rails.root}/lib/mailer_previews"
|
13
13
|
#
|
14
|
-
mattr_accessor :
|
14
|
+
mattr_accessor :preview_paths, instance_writer: false, default: []
|
15
15
|
|
16
16
|
# Enable or disable mailer previews through app configuration:
|
17
17
|
#
|
@@ -25,7 +25,31 @@ module ActionMailer
|
|
25
25
|
mattr_accessor :preview_interceptors, instance_writer: false, default: [ActionMailer::InlinePreviewInterceptor]
|
26
26
|
end
|
27
27
|
|
28
|
+
def preview_path
|
29
|
+
ActionMailer.deprecator.warn(<<-MSG.squish)
|
30
|
+
Using preview_path option is deprecated and will be removed in Rails 7.2.
|
31
|
+
Please use preview_paths instead.
|
32
|
+
MSG
|
33
|
+
self.class.preview_paths.first
|
34
|
+
end
|
35
|
+
|
28
36
|
module ClassMethods
|
37
|
+
def preview_path=(value)
|
38
|
+
ActionMailer.deprecator.warn(<<-MSG.squish)
|
39
|
+
Using preview_path= option is deprecated and will be removed in Rails 7.2.
|
40
|
+
Please use preview_paths= instead.
|
41
|
+
MSG
|
42
|
+
self.preview_paths << value
|
43
|
+
end
|
44
|
+
|
45
|
+
def preview_path
|
46
|
+
ActionMailer.deprecator.warn(<<-MSG.squish)
|
47
|
+
Using preview_path option is deprecated and will be removed in Rails 7.2.
|
48
|
+
Please use preview_paths instead.
|
49
|
+
MSG
|
50
|
+
self.preview_paths.first
|
51
|
+
end
|
52
|
+
|
29
53
|
# Register one or more Interceptors which will be called before mail is previewed.
|
30
54
|
def register_preview_interceptors(*interceptors)
|
31
55
|
interceptors.flatten.compact.each { |interceptor| register_preview_interceptor(interceptor) }
|
@@ -79,7 +103,7 @@ module ActionMailer
|
|
79
103
|
# Returns all mailer preview classes.
|
80
104
|
def all
|
81
105
|
load_previews if descendants.empty?
|
82
|
-
descendants
|
106
|
+
descendants.sort_by { |mailer| mailer.name.titleize }
|
83
107
|
end
|
84
108
|
|
85
109
|
# Returns the mail object for the given email name. The registered preview
|
@@ -119,13 +143,13 @@ module ActionMailer
|
|
119
143
|
|
120
144
|
private
|
121
145
|
def load_previews
|
122
|
-
|
123
|
-
Dir["#{preview_path}/**/*_preview.rb"].sort.each { |file|
|
146
|
+
preview_paths.each do |preview_path|
|
147
|
+
Dir["#{preview_path}/**/*_preview.rb"].sort.each { |file| require file }
|
124
148
|
end
|
125
149
|
end
|
126
150
|
|
127
|
-
def
|
128
|
-
Base.
|
151
|
+
def preview_paths
|
152
|
+
Base.preview_paths
|
129
153
|
end
|
130
154
|
|
131
155
|
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"
|
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
|
-
|
37
|
-
|
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,43 @@ 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:
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
-
|
162
|
+
|
163
|
+
if args.is_a?(Hash)
|
164
|
+
ActionMailer.deprecator.warn <<~MSG
|
165
|
+
Passing a Hash to the assert_enqueued_email_with :args kwarg causes the
|
166
|
+
Hash to be treated as params. This behavior is deprecated and will be
|
167
|
+
removed in Rails 7.2.
|
168
|
+
|
169
|
+
To specify a params Hash, use the :params kwarg:
|
170
|
+
|
171
|
+
assert_enqueued_email_with MyMailer, :my_method, params: { my_param: "value" }
|
172
|
+
|
173
|
+
Or, to specify named mailer args as a Hash, wrap the Hash in an array:
|
174
|
+
|
175
|
+
assert_enqueued_email_with MyMailer, :my_method, args: [{ my_arg: "value" }]
|
176
|
+
# OR
|
177
|
+
assert_enqueued_email_with MyMailer, :my_method, args: [my_arg: "value"]
|
178
|
+
MSG
|
179
|
+
|
180
|
+
params, args = args, nil
|
181
|
+
end
|
182
|
+
|
183
|
+
args = Array(args) unless args.is_a?(Proc)
|
184
|
+
queue ||= mailer.deliver_later_queue_name || ActiveJob::Base.default_queue_name
|
185
|
+
|
186
|
+
expected = ->(job_args) do
|
187
|
+
job_kwargs = job_args.extract_options!
|
188
|
+
|
189
|
+
[mailer.to_s, method.to_s, "deliver_now"] == job_args &&
|
190
|
+
params === job_kwargs[:params] && args === job_kwargs[:args]
|
191
|
+
end
|
192
|
+
|
193
|
+
assert_enqueued_with(job: mailer.delivery_job, args: expected, queue: queue.to_s, &block)
|
133
194
|
end
|
134
195
|
|
135
196
|
# Asserts that no emails are enqueued for later delivery.
|
@@ -151,6 +212,68 @@ module ActionMailer
|
|
151
212
|
assert_enqueued_emails 0, &block
|
152
213
|
end
|
153
214
|
|
215
|
+
# Delivers all enqueued emails. If a block is given, delivers all of the emails
|
216
|
+
# that were enqueued throughout the duration of the block. If a block is
|
217
|
+
# not given, delivers all the enqueued emails up to this point in the test.
|
218
|
+
#
|
219
|
+
# def test_deliver_enqueued_emails
|
220
|
+
# deliver_enqueued_emails do
|
221
|
+
# ContactMailer.welcome.deliver_later
|
222
|
+
# end
|
223
|
+
#
|
224
|
+
# assert_emails 1
|
225
|
+
# end
|
226
|
+
#
|
227
|
+
# def test_deliver_enqueued_emails_without_block
|
228
|
+
# ContactMailer.welcome.deliver_later
|
229
|
+
#
|
230
|
+
# deliver_enqueued_emails
|
231
|
+
#
|
232
|
+
# assert_emails 1
|
233
|
+
# end
|
234
|
+
#
|
235
|
+
# If the +:queue+ option is specified,
|
236
|
+
# then only the emails(s) enqueued to a specific queue will be performed.
|
237
|
+
#
|
238
|
+
# def test_deliver_enqueued_emails_with_queue
|
239
|
+
# deliver_enqueued_emails queue: :external_mailers do
|
240
|
+
# CustomerMailer.deliver_later_queue_name = :external_mailers
|
241
|
+
# CustomerMailer.welcome.deliver_later # will be performed
|
242
|
+
# EmployeeMailer.deliver_later_queue_name = :internal_mailers
|
243
|
+
# EmployeeMailer.welcome.deliver_later # will not be performed
|
244
|
+
# end
|
245
|
+
#
|
246
|
+
# assert_emails 1
|
247
|
+
# end
|
248
|
+
#
|
249
|
+
# If the +:at+ option is specified, then only delivers emails enqueued to deliver
|
250
|
+
# immediately or before the given time.
|
251
|
+
def deliver_enqueued_emails(queue: nil, at: nil, &block)
|
252
|
+
perform_enqueued_jobs(only: ->(job) { delivery_job_filter(job) }, queue: queue, at: at, &block)
|
253
|
+
end
|
254
|
+
|
255
|
+
# Returns any emails that are sent in the block.
|
256
|
+
#
|
257
|
+
# def test_emails
|
258
|
+
# emails = capture_emails do
|
259
|
+
# ContactMailer.welcome.deliver_now
|
260
|
+
# end
|
261
|
+
# assert_equal "Hi there", emails.first.subject
|
262
|
+
#
|
263
|
+
# emails = capture_emails do
|
264
|
+
# ContactMailer.welcome.deliver_now
|
265
|
+
# ContactMailer.welcome.deliver_later
|
266
|
+
# end
|
267
|
+
# assert_equal "Hi there", emails.first.subject
|
268
|
+
# end
|
269
|
+
def capture_emails(&block)
|
270
|
+
original_count = ActionMailer::Base.deliveries.size
|
271
|
+
deliver_enqueued_emails(&block)
|
272
|
+
new_count = ActionMailer::Base.deliveries.size
|
273
|
+
diff = new_count - original_count
|
274
|
+
ActionMailer::Base.deliveries.last(diff)
|
275
|
+
end
|
276
|
+
|
154
277
|
private
|
155
278
|
def delivery_job_filter(job)
|
156
279
|
job_class = job.is_a?(Hash) ? job.fetch(:job) : job.class
|