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
@@ -1,45 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/array/extract_options"
4
+ require "active_job"
5
+
1
6
  module ActionMailer
7
+ # Provides helper methods for testing Action Mailer, including #assert_emails
8
+ # and #assert_no_emails.
2
9
  module TestHelper
3
- extend ActiveSupport::Concern
10
+ include ActiveJob::TestHelper
4
11
 
5
12
  # Asserts that the number of emails sent matches the given number.
6
13
  #
7
14
  # def test_emails
8
15
  # assert_emails 0
9
- # ContactMailer.deliver_contact
16
+ # ContactMailer.welcome.deliver_now
10
17
  # assert_emails 1
11
- # ContactMailer.deliver_contact
18
+ # ContactMailer.welcome.deliver_now
12
19
  # assert_emails 2
13
20
  # end
14
21
  #
15
- # If a block is passed, that block should cause the specified number of emails to be sent.
22
+ # If a block is passed, that block should cause the specified number of
23
+ # emails to be sent.
16
24
  #
17
25
  # def test_emails_again
18
26
  # assert_emails 1 do
19
- # ContactMailer.deliver_contact
27
+ # ContactMailer.welcome.deliver_now
20
28
  # end
21
29
  #
22
30
  # assert_emails 2 do
23
- # ContactMailer.deliver_contact
24
- # ContactMailer.deliver_contact
31
+ # ContactMailer.welcome.deliver_now
32
+ # ContactMailer.welcome.deliver_later
25
33
  # end
26
34
  # end
27
- def assert_emails(number)
35
+ def assert_emails(number, &block)
28
36
  if block_given?
29
- original_count = ActionMailer::Base.deliveries.size
30
- yield
31
- new_count = ActionMailer::Base.deliveries.size
32
- assert_equal original_count + number, new_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"
33
39
  else
34
40
  assert_equal number, ActionMailer::Base.deliveries.size
35
41
  end
36
42
  end
37
43
 
38
- # Assert that no emails have been sent.
44
+ # Asserts that no emails have been sent.
39
45
  #
40
46
  # def test_emails
41
47
  # assert_no_emails
42
- # ContactMailer.deliver_contact
48
+ # ContactMailer.welcome.deliver_now
43
49
  # assert_emails 1
44
50
  # end
45
51
  #
@@ -53,9 +59,226 @@ module ActionMailer
53
59
  #
54
60
  # Note: This assertion is simply a shortcut for:
55
61
  #
56
- # assert_emails 0
62
+ # assert_emails 0, &block
57
63
  def assert_no_emails(&block)
58
64
  assert_emails 0, &block
59
65
  end
66
+
67
+ # Asserts that the number of emails enqueued for later delivery matches
68
+ # the given number.
69
+ #
70
+ # def test_emails
71
+ # assert_enqueued_emails 0
72
+ # ContactMailer.welcome.deliver_later
73
+ # assert_enqueued_emails 1
74
+ # ContactMailer.welcome.deliver_later
75
+ # assert_enqueued_emails 2
76
+ # end
77
+ #
78
+ # If a block is passed, that block should cause the specified number of
79
+ # emails to be enqueued.
80
+ #
81
+ # def test_emails_again
82
+ # assert_enqueued_emails 1 do
83
+ # ContactMailer.welcome.deliver_later
84
+ # end
85
+ #
86
+ # assert_enqueued_emails 2 do
87
+ # ContactMailer.welcome.deliver_later
88
+ # ContactMailer.welcome.deliver_later
89
+ # end
90
+ # end
91
+ def assert_enqueued_emails(number, &block)
92
+ assert_enqueued_jobs(number, only: ->(job) { delivery_job_filter(job) }, &block)
93
+ end
94
+
95
+ # Asserts that a specific email has been enqueued, optionally
96
+ # matching arguments and/or params.
97
+ #
98
+ # def test_email
99
+ # ContactMailer.welcome.deliver_later
100
+ # assert_enqueued_email_with ContactMailer, :welcome
101
+ # end
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
+ #
108
+ # def test_email_with_arguments
109
+ # ContactMailer.welcome("Hello", "Goodbye").deliver_later
110
+ # assert_enqueued_email_with ContactMailer, :welcome, args: ["Hello", "Goodbye"]
111
+ # end
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
+ #
140
+ # If a block is passed, that block should cause the specified email
141
+ # to be enqueued.
142
+ #
143
+ # def test_email_in_block
144
+ # assert_enqueued_email_with ContactMailer, :welcome do
145
+ # ContactMailer.welcome.deliver_later
146
+ # end
147
+ # end
148
+ #
149
+ # If +args+ is provided as a Hash, a parameterized email is matched.
150
+ #
151
+ # def test_parameterized_email
152
+ # assert_enqueued_email_with ContactMailer, :welcome,
153
+ # args: {email: 'user@example.com'} do
154
+ # ContactMailer.with(email: 'user@example.com').welcome.deliver_later
155
+ # end
156
+ # end
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)
161
+ end
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)
194
+ end
195
+
196
+ # Asserts that no emails are enqueued for later delivery.
197
+ #
198
+ # def test_no_emails
199
+ # assert_no_enqueued_emails
200
+ # ContactMailer.welcome.deliver_later
201
+ # assert_enqueued_emails 1
202
+ # end
203
+ #
204
+ # If a block is provided, it should not cause any emails to be enqueued.
205
+ #
206
+ # def test_no_emails
207
+ # assert_no_enqueued_emails do
208
+ # # No emails should be enqueued from this block
209
+ # end
210
+ # end
211
+ def assert_no_enqueued_emails(&block)
212
+ assert_enqueued_emails 0, &block
213
+ end
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
+
277
+ private
278
+ def delivery_job_filter(job)
279
+ job_class = job.is_a?(Hash) ? job.fetch(:job) : job.class
280
+
281
+ Base.descendants.map(&:delivery_job).include?(job_class)
282
+ end
60
283
  end
61
284
  end
@@ -1,10 +1,11 @@
1
- module ActionMailer
2
- module VERSION #:nodoc:
3
- MAJOR = 3
4
- MINOR = 0
5
- TINY = 4
6
- PRE = nil
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "gem_version"
7
4
 
8
- STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
5
+ module ActionMailer
6
+ # Returns the currently loaded version of Action Mailer as a
7
+ # +Gem::Version+.
8
+ def self.version
9
+ gem_version
9
10
  end
10
11
  end
data/lib/action_mailer.rb CHANGED
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #--
2
- # Copyright (c) 2004-2010 David Heinemeier Hansson
4
+ # Copyright (c) David Heinemeier Hansson
3
5
  #
4
6
  # Permission is hereby granted, free of charge, to any person obtaining
5
7
  # a copy of this software and associated documentation files (the
@@ -21,32 +23,57 @@
21
23
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
24
  #++
23
25
 
24
- actionpack_path = File.expand_path('../../../actionpack/lib', __FILE__)
25
- $:.unshift(actionpack_path) if File.directory?(actionpack_path) && !$:.include?(actionpack_path)
26
-
27
- require 'abstract_controller'
28
- require 'action_view'
29
- require 'action_mailer/version'
26
+ require "abstract_controller"
27
+ require "action_mailer/version"
28
+ require "action_mailer/deprecator"
30
29
 
31
30
  # Common Active Support usage in Action Mailer
32
- require 'active_support/core_ext/class'
33
- require 'active_support/core_ext/object/blank'
34
- require 'active_support/core_ext/array/uniq_by'
35
- require 'active_support/core_ext/module/attr_internal'
36
- require 'active_support/core_ext/module/delegation'
37
- require 'active_support/core_ext/string/inflections'
38
- require 'active_support/lazy_load_hooks'
31
+ require "active_support"
32
+ require "active_support/rails"
33
+ require "active_support/core_ext/class"
34
+ require "active_support/core_ext/module/attr_internal"
35
+ require "active_support/core_ext/string/inflections"
36
+ require "active_support/lazy_load_hooks"
39
37
 
38
+ # :include: ../README.rdoc
40
39
  module ActionMailer
41
40
  extend ::ActiveSupport::Autoload
42
41
 
43
- autoload :AdvAttrAccessor
44
- autoload :Collector
42
+ eager_autoload do
43
+ autoload :Collector
44
+ end
45
+
45
46
  autoload :Base
47
+ autoload :Callbacks
46
48
  autoload :DeliveryMethods
47
- autoload :DeprecatedApi
49
+ autoload :InlinePreviewInterceptor
48
50
  autoload :MailHelper
49
- autoload :OldApi
51
+ autoload :Parameterized
52
+ autoload :Preview
53
+ autoload :Previews, "action_mailer/preview"
50
54
  autoload :TestCase
51
55
  autoload :TestHelper
56
+ autoload :MessageDelivery
57
+ autoload :MailDeliveryJob
58
+ autoload :QueuedDelivery
59
+ autoload :FormBuilder
60
+
61
+ def self.eager_load!
62
+ super
63
+
64
+ require "mail"
65
+ Mail.eager_autoload!
66
+
67
+ Base.descendants.each do |mailer|
68
+ mailer.eager_load! unless mailer.abstract?
69
+ end
70
+ end
71
+ end
72
+
73
+ autoload :Mime, "action_dispatch/http/mime_type"
74
+
75
+ ActiveSupport.on_load(:action_view) do
76
+ ActionView::Base.default_formats ||= Mime::SET.symbols
77
+ ActionView::Template.mime_types_implementation = Mime
78
+ ActionView::LookupContext::DetailsKey.clear
52
79
  end
@@ -1,15 +1,20 @@
1
1
  Description:
2
- Stubs out a new mailer and its views. Pass the mailer name, either
2
+ Generates a new mailer and its views. Passes the mailer name, either
3
3
  CamelCased or under_scored, and an optional list of emails as arguments.
4
4
 
5
5
  This generates a mailer class in app/mailers and invokes your template
6
6
  engine and test framework generators.
7
7
 
8
- Example:
9
- `rails generate mailer Notifications signup forgot_password invoice`
8
+ Examples:
9
+ `bin/rails generate mailer sign_up`
10
+
11
+ creates a sign up mailer class, views, and test:
12
+ Mailer: app/mailers/sign_up_mailer.rb
13
+ Views: app/views/sign_up_mailer/signup.text.erb [...]
14
+ Test: test/mailers/sign_up_mailer_test.rb
15
+
16
+ `bin/rails generate mailer notifications sign_up forgot_password invoice`
17
+
18
+ creates a notifications mailer with sign_up, forgot_password, and invoice actions.
19
+
10
20
 
11
- creates a Notifications mailer class, views, test, and fixtures:
12
- Mailer: app/mailers/notifications.rb
13
- Views: app/views/notifications/signup.erb [...]
14
- Test: test/functional/notifications_test.rb
15
- Fixtures: test/fixtures/notifications/signup [...]
@@ -1,16 +1,38 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rails
2
4
  module Generators
3
5
  class MailerGenerator < NamedBase
4
- source_root File.expand_path("../templates", __FILE__)
6
+ source_root File.expand_path("templates", __dir__)
7
+
8
+ argument :actions, type: :array, default: [], banner: "method method"
5
9
 
6
- argument :actions, :type => :array, :default => [], :banner => "method method"
7
- check_class_collision
10
+ check_class_collision suffix: "Mailer"
8
11
 
9
12
  def create_mailer_file
10
- template "mailer.rb", File.join('app/mailers', class_path, "#{file_name}.rb")
13
+ template "mailer.rb", File.join("app/mailers", class_path, "#{file_name}_mailer.rb")
14
+
15
+ in_root do
16
+ if behavior == :invoke && !File.exist?(application_mailer_file_name)
17
+ template "application_mailer.rb", application_mailer_file_name
18
+ end
19
+ end
11
20
  end
12
21
 
13
22
  hook_for :template_engine, :test_framework
23
+
24
+ private
25
+ def file_name # :doc:
26
+ @_file_name ||= super.sub(/_mailer\z/i, "")
27
+ end
28
+
29
+ def application_mailer_file_name
30
+ @_application_mailer_file_name ||= if mountable_engine?
31
+ "app/mailers/#{namespaced_path}/application_mailer.rb"
32
+ else
33
+ "app/mailers/application_mailer.rb"
34
+ end
35
+ end
14
36
  end
15
37
  end
16
38
  end
@@ -0,0 +1,6 @@
1
+ <% module_namespacing do -%>
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ <% end %>
@@ -0,0 +1,17 @@
1
+ <% module_namespacing do -%>
2
+ class <%= class_name %>Mailer < ApplicationMailer
3
+ <% actions.each do |action| -%>
4
+
5
+ # Subject can be set in your I18n file at config/locales/en.yml
6
+ # with the following lookup:
7
+ #
8
+ # en.<%= file_path.tr("/",".") %>_mailer.<%= action %>.subject
9
+ #
10
+ def <%= action %>
11
+ @greeting = "Hi"
12
+
13
+ mail to: "to@example.org"
14
+ end
15
+ <% end -%>
16
+ end
17
+ <% end -%>