aboutsummaryrefslogtreecommitdiffstats
path: root/actionmailer
diff options
context:
space:
mode:
Diffstat (limited to 'actionmailer')
-rw-r--r--actionmailer/CHANGELOG.md12
-rw-r--r--actionmailer/lib/action_mailer.rb7
-rw-r--r--actionmailer/lib/action_mailer/base.rb24
-rw-r--r--actionmailer/lib/action_mailer/preview.rb27
-rw-r--r--actionmailer/lib/rails/generators/mailer/mailer_generator.rb2
-rw-r--r--actionmailer/test/base_test.rb95
-rw-r--r--actionmailer/test/caching_test.rb8
7 files changed, 151 insertions, 24 deletions
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index 9fb2e44210..88e8a36eaa 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -1,7 +1,19 @@
+* Ensure mail gem is eager autoloaded when eager load is true to prevent thread deadlocks.
+
+ *Samuel Cochran*
+
* Perform email jobs in `assert_emails`.
*Gannon McGibbon*
+* Add `Base.unregister_observer`, `Base.unregister_observers`,
+ `Base.unregister_interceptor`, `Base.unregister_interceptors`,
+ `Base.unregister_preview_interceptor` and `Base.unregister_preview_interceptors`.
+ This makes it possible to dynamically add and remove email observers and
+ interceptors at runtime in the same way they're registered.
+
+ *Claudio Ortolina*, *Kota Miyake*
+
* Rails 6 requires Ruby 2.4.1 or newer.
*Jeremy Daer*
diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb
index fabbdd1b25..69eae65d60 100644
--- a/actionmailer/lib/action_mailer.rb
+++ b/actionmailer/lib/action_mailer.rb
@@ -52,6 +52,13 @@ module ActionMailer
autoload :TestHelper
autoload :MessageDelivery
autoload :DeliveryJob
+
+ def self.eager_load!
+ super
+
+ require "mail"
+ Mail.eager_autoload!
+ end
end
autoload :Mime, "action_dispatch/http/mime_type"
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 3af95081ee..7f22af83b0 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -475,11 +475,21 @@ module ActionMailer
observers.flatten.compact.each { |observer| register_observer(observer) }
end
+ # Unregister one or more previously registered Observers.
+ def unregister_observers(*observers)
+ observers.flatten.compact.each { |observer| unregister_observer(observer) }
+ end
+
# Register one or more Interceptors which will be called before mail is sent.
def register_interceptors(*interceptors)
interceptors.flatten.compact.each { |interceptor| register_interceptor(interceptor) }
end
+ # Unregister one or more previously registered Interceptors.
+ def unregister_interceptors(*interceptors)
+ interceptors.flatten.compact.each { |interceptor| unregister_interceptor(interceptor) }
+ end
+
# Register an Observer which will be notified when mail is delivered.
# Either a class, string or symbol can be passed in as the Observer.
# If a string or symbol is passed in it will be camelized and constantized.
@@ -487,6 +497,13 @@ module ActionMailer
Mail.register_observer(observer_class_for(observer))
end
+ # Unregister a previously registered Observer.
+ # Either a class, string or symbol can be passed in as the Observer.
+ # If a string or symbol is passed in it will be camelized and constantized.
+ def unregister_observer(observer)
+ Mail.unregister_observer(observer_class_for(observer))
+ end
+
# Register an Interceptor which will be called before mail is sent.
# Either a class, string or symbol can be passed in as the Interceptor.
# If a string or symbol is passed in it will be camelized and constantized.
@@ -494,6 +511,13 @@ module ActionMailer
Mail.register_interceptor(observer_class_for(interceptor))
end
+ # Unregister a previously registered Interceptor.
+ # Either a class, string or symbol can be passed in as the Interceptor.
+ # If a string or symbol is passed in it will be camelized and constantized.
+ def unregister_interceptor(interceptor)
+ Mail.unregister_interceptor(observer_class_for(interceptor))
+ end
+
def observer_class_for(value) # :nodoc:
case value
when String, Symbol
diff --git a/actionmailer/lib/action_mailer/preview.rb b/actionmailer/lib/action_mailer/preview.rb
index 0aea84fd2b..500b3bede0 100644
--- a/actionmailer/lib/action_mailer/preview.rb
+++ b/actionmailer/lib/action_mailer/preview.rb
@@ -31,22 +31,39 @@ module ActionMailer
interceptors.flatten.compact.each { |interceptor| register_preview_interceptor(interceptor) }
end
+ # Unregister one or more previously registered Interceptors.
+ def unregister_preview_interceptors(*interceptors)
+ interceptors.flatten.compact.each { |interceptor| unregister_preview_interceptor(interceptor) }
+ end
+
# Register an Interceptor which will be called before mail is previewed.
# Either a class or a string can be passed in as the Interceptor. If a
# string is passed in it will be constantized.
def register_preview_interceptor(interceptor)
- preview_interceptor = \
+ preview_interceptor = interceptor_class_for(interceptor)
+
+ unless preview_interceptors.include?(preview_interceptor)
+ preview_interceptors << preview_interceptor
+ end
+ end
+
+ # Unregister a previously registered Interceptor.
+ # Either a class or a string can be passed in as the Interceptor. If a
+ # string is passed in it will be constantized.
+ def unregister_preview_interceptor(interceptor)
+ preview_interceptors.delete(interceptor_class_for(interceptor))
+ end
+
+ private
+
+ def interceptor_class_for(interceptor)
case interceptor
when String, Symbol
interceptor.to_s.camelize.constantize
else
interceptor
end
-
- unless preview_interceptors.include?(preview_interceptor)
- preview_interceptors << preview_interceptor
end
- end
end
end
diff --git a/actionmailer/lib/rails/generators/mailer/mailer_generator.rb b/actionmailer/lib/rails/generators/mailer/mailer_generator.rb
index 97eac30db1..c37a74c762 100644
--- a/actionmailer/lib/rails/generators/mailer/mailer_generator.rb
+++ b/actionmailer/lib/rails/generators/mailer/mailer_generator.rb
@@ -23,7 +23,7 @@ module Rails
private
def file_name # :doc:
- @_file_name ||= super.gsub(/_mailer/i, "")
+ @_file_name ||= super.sub(/_mailer\z/i, "")
end
def application_mailer_file_name
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index 4124aa00bd..7898996c30 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -618,37 +618,52 @@ class BaseTest < ActiveSupport::TestCase
end
end
- test "you can register an observer to the mail object that gets informed on email delivery" do
+ test "you can register and unregister an observer to the mail object that gets informed on email delivery" do
mail_side_effects do
ActionMailer::Base.register_observer(MyObserver)
mail = BaseMailer.welcome
assert_called_with(MyObserver, :delivered_email, [mail]) do
mail.deliver_now
end
+
+ ActionMailer::Base.unregister_observer(MyObserver)
+ assert_not_called(MyObserver, :delivered_email, returns: mail) do
+ mail.deliver_now
+ end
end
end
- test "you can register an observer using its stringified name to the mail object that gets informed on email delivery" do
+ test "you can register and unregister an observer using its stringified name to the mail object that gets informed on email delivery" do
mail_side_effects do
ActionMailer::Base.register_observer("BaseTest::MyObserver")
mail = BaseMailer.welcome
assert_called_with(MyObserver, :delivered_email, [mail]) do
mail.deliver_now
end
+
+ ActionMailer::Base.unregister_observer("BaseTest::MyObserver")
+ assert_not_called(MyObserver, :delivered_email, returns: mail) do
+ mail.deliver_now
+ end
end
end
- test "you can register an observer using its symbolized underscored name to the mail object that gets informed on email delivery" do
+ test "you can register and unregister an observer using its symbolized underscored name to the mail object that gets informed on email delivery" do
mail_side_effects do
ActionMailer::Base.register_observer(:"base_test/my_observer")
mail = BaseMailer.welcome
assert_called_with(MyObserver, :delivered_email, [mail]) do
mail.deliver_now
end
+
+ ActionMailer::Base.unregister_observer(:"base_test/my_observer")
+ assert_not_called(MyObserver, :delivered_email, returns: mail) do
+ mail.deliver_now
+ end
end
end
- test "you can register multiple observers to the mail object that both get informed on email delivery" do
+ test "you can register and unregister multiple observers to the mail object that both get informed on email delivery" do
mail_side_effects do
ActionMailer::Base.register_observers("BaseTest::MyObserver", MySecondObserver)
mail = BaseMailer.welcome
@@ -657,6 +672,14 @@ class BaseTest < ActiveSupport::TestCase
mail.deliver_now
end
end
+
+ ActionMailer::Base.unregister_observers("BaseTest::MyObserver", MySecondObserver)
+ assert_not_called(MyObserver, :delivered_email, returns: mail) do
+ mail.deliver_now
+ end
+ assert_not_called(MySecondObserver, :delivered_email, returns: mail) do
+ mail.deliver_now
+ end
end
end
@@ -670,37 +693,52 @@ class BaseTest < ActiveSupport::TestCase
def self.previewing_email(mail); end
end
- test "you can register an interceptor to the mail object that gets passed the mail object before delivery" do
+ test "you can register and unregister an interceptor to the mail object that gets passed the mail object before delivery" do
mail_side_effects do
ActionMailer::Base.register_interceptor(MyInterceptor)
mail = BaseMailer.welcome
assert_called_with(MyInterceptor, :delivering_email, [mail]) do
mail.deliver_now
end
+
+ ActionMailer::Base.unregister_interceptor(MyInterceptor)
+ assert_not_called(MyInterceptor, :delivering_email, returns: mail) do
+ mail.deliver_now
+ end
end
end
- test "you can register an interceptor using its stringified name to the mail object that gets passed the mail object before delivery" do
+ test "you can register and unregister an interceptor using its stringified name to the mail object that gets passed the mail object before delivery" do
mail_side_effects do
ActionMailer::Base.register_interceptor("BaseTest::MyInterceptor")
mail = BaseMailer.welcome
assert_called_with(MyInterceptor, :delivering_email, [mail]) do
mail.deliver_now
end
+
+ ActionMailer::Base.unregister_interceptor("BaseTest::MyInterceptor")
+ assert_not_called(MyInterceptor, :delivering_email, returns: mail) do
+ mail.deliver_now
+ end
end
end
- test "you can register an interceptor using its symbolized underscored name to the mail object that gets passed the mail object before delivery" do
+ test "you can register and unregister an interceptor using its symbolized underscored name to the mail object that gets passed the mail object before delivery" do
mail_side_effects do
ActionMailer::Base.register_interceptor(:"base_test/my_interceptor")
mail = BaseMailer.welcome
assert_called_with(MyInterceptor, :delivering_email, [mail]) do
mail.deliver_now
end
+
+ ActionMailer::Base.unregister_interceptor(:"base_test/my_interceptor")
+ assert_not_called(MyInterceptor, :delivering_email, returns: mail) do
+ mail.deliver_now
+ end
end
end
- test "you can register multiple interceptors to the mail object that both get passed the mail object before delivery" do
+ test "you can register and unregister multiple interceptors to the mail object that both get passed the mail object before delivery" do
mail_side_effects do
ActionMailer::Base.register_interceptors("BaseTest::MyInterceptor", MySecondInterceptor)
mail = BaseMailer.welcome
@@ -709,6 +747,14 @@ class BaseTest < ActiveSupport::TestCase
mail.deliver_now
end
end
+
+ ActionMailer::Base.unregister_interceptors("BaseTest::MyInterceptor", MySecondInterceptor)
+ assert_not_called(MyInterceptor, :delivering_email, returns: mail) do
+ mail.deliver_now
+ end
+ assert_not_called(MySecondInterceptor, :delivering_email, returns: mail) do
+ mail.deliver_now
+ end
end
end
@@ -888,8 +934,6 @@ class BaseTest < ActiveSupport::TestCase
klass.default_params = old
end
- # A simple hack to restore the observers and interceptors for Mail, as it
- # does not have an unregister API yet.
def mail_side_effects
old_observers = Mail.class_variable_get(:@@delivery_notification_observers)
old_delivery_interceptors = Mail.class_variable_get(:@@delivery_interceptors)
@@ -928,7 +972,7 @@ class BasePreviewInterceptorsTest < ActiveSupport::TestCase
def self.previewing_email(mail); end
end
- test "you can register a preview interceptor to the mail object that gets passed the mail object before previewing" do
+ test "you can register and unregister a preview interceptor to the mail object that gets passed the mail object before previewing" do
ActionMailer::Base.register_preview_interceptor(MyInterceptor)
mail = BaseMailer.welcome
stub_any_instance(BaseMailerPreview) do |instance|
@@ -938,9 +982,14 @@ class BasePreviewInterceptorsTest < ActiveSupport::TestCase
end
end
end
+
+ ActionMailer::Base.unregister_preview_interceptor(MyInterceptor)
+ assert_not_called(MyInterceptor, :previewing_email, returns: mail) do
+ BaseMailerPreview.call(:welcome)
+ end
end
- test "you can register a preview interceptor using its stringified name to the mail object that gets passed the mail object before previewing" do
+ test "you can register and unregister a preview interceptor using its stringified name to the mail object that gets passed the mail object before previewing" do
ActionMailer::Base.register_preview_interceptor("BasePreviewInterceptorsTest::MyInterceptor")
mail = BaseMailer.welcome
stub_any_instance(BaseMailerPreview) do |instance|
@@ -950,9 +999,14 @@ class BasePreviewInterceptorsTest < ActiveSupport::TestCase
end
end
end
+
+ ActionMailer::Base.unregister_preview_interceptor("BasePreviewInterceptorsTest::MyInterceptor")
+ assert_not_called(MyInterceptor, :previewing_email, returns: mail) do
+ BaseMailerPreview.call(:welcome)
+ end
end
- test "you can register an interceptor using its symbolized underscored name to the mail object that gets passed the mail object before previewing" do
+ test "you can register and unregister a preview interceptor using its symbolized underscored name to the mail object that gets passed the mail object before previewing" do
ActionMailer::Base.register_preview_interceptor(:"base_preview_interceptors_test/my_interceptor")
mail = BaseMailer.welcome
stub_any_instance(BaseMailerPreview) do |instance|
@@ -962,9 +1016,14 @@ class BasePreviewInterceptorsTest < ActiveSupport::TestCase
end
end
end
+
+ ActionMailer::Base.unregister_preview_interceptor(:"base_preview_interceptors_test/my_interceptor")
+ assert_not_called(MyInterceptor, :previewing_email, returns: mail) do
+ BaseMailerPreview.call(:welcome)
+ end
end
- test "you can register multiple preview interceptors to the mail object that both get passed the mail object before previewing" do
+ test "you can register and unregister multiple preview interceptors to the mail object that both get passed the mail object before previewing" do
ActionMailer::Base.register_preview_interceptors("BasePreviewInterceptorsTest::MyInterceptor", MySecondInterceptor)
mail = BaseMailer.welcome
stub_any_instance(BaseMailerPreview) do |instance|
@@ -976,6 +1035,14 @@ class BasePreviewInterceptorsTest < ActiveSupport::TestCase
end
end
end
+
+ ActionMailer::Base.unregister_preview_interceptors("BasePreviewInterceptorsTest::MyInterceptor", MySecondInterceptor)
+ assert_not_called(MyInterceptor, :previewing_email, returns: mail) do
+ BaseMailerPreview.call(:welcome)
+ end
+ assert_not_called(MySecondInterceptor, :previewing_email, returns: mail) do
+ BaseMailerPreview.call(:welcome)
+ end
end
end
diff --git a/actionmailer/test/caching_test.rb b/actionmailer/test/caching_test.rb
index 574840c30e..22f310f39f 100644
--- a/actionmailer/test/caching_test.rb
+++ b/actionmailer/test/caching_test.rb
@@ -40,14 +40,14 @@ class FragmentCachingTest < BaseCachingTest
def test_fragment_exist_with_caching_enabled
@store.write("views/name", "value")
assert @mailer.fragment_exist?("name")
- assert !@mailer.fragment_exist?("other_name")
+ assert_not @mailer.fragment_exist?("other_name")
end
def test_fragment_exist_with_caching_disabled
@mailer.perform_caching = false
@store.write("views/name", "value")
- assert !@mailer.fragment_exist?("name")
- assert !@mailer.fragment_exist?("other_name")
+ assert_not @mailer.fragment_exist?("name")
+ assert_not @mailer.fragment_exist?("other_name")
end
def test_write_fragment_with_caching_enabled
@@ -90,7 +90,7 @@ class FragmentCachingTest < BaseCachingTest
buffer = "generated till now -> ".html_safe
buffer << view_context.send(:fragment_for, "expensive") { fragment_computed = true }
- assert !fragment_computed
+ assert_not fragment_computed
assert_equal "generated till now -> fragment content", buffer
end