aboutsummaryrefslogtreecommitdiffstats
path: root/actionmailer
diff options
context:
space:
mode:
Diffstat (limited to 'actionmailer')
-rw-r--r--actionmailer/CHANGELOG.md89
-rw-r--r--actionmailer/MIT-LICENSE2
-rw-r--r--actionmailer/README.rdoc3
-rw-r--r--actionmailer/Rakefile10
-rw-r--r--actionmailer/actionmailer.gemspec34
-rwxr-xr-xactionmailer/bin/test4
-rw-r--r--actionmailer/lib/action_mailer.rb25
-rw-r--r--actionmailer/lib/action_mailer/base.rb332
-rw-r--r--actionmailer/lib/action_mailer/collector.rb6
-rw-r--r--actionmailer/lib/action_mailer/delivery_job.rb23
-rw-r--r--actionmailer/lib/action_mailer/delivery_methods.rb14
-rw-r--r--actionmailer/lib/action_mailer/gem_version.rb4
-rw-r--r--actionmailer/lib/action_mailer/inline_preview_interceptor.rb8
-rw-r--r--actionmailer/lib/action_mailer/log_subscriber.rb12
-rw-r--r--actionmailer/lib/action_mailer/mail_helper.rb2
-rw-r--r--actionmailer/lib/action_mailer/message_delivery.rb71
-rw-r--r--actionmailer/lib/action_mailer/preview.rb35
-rw-r--r--actionmailer/lib/action_mailer/railtie.rb36
-rw-r--r--actionmailer/lib/action_mailer/rescuable.rb27
-rw-r--r--actionmailer/lib/action_mailer/test_case.rb33
-rw-r--r--actionmailer/lib/action_mailer/test_helper.rb6
-rw-r--r--actionmailer/lib/action_mailer/version.rb2
-rw-r--r--actionmailer/lib/rails/generators/mailer/mailer_generator.rb23
-rw-r--r--actionmailer/lib/rails/generators/mailer/templates/application_mailer.rb4
-rw-r--r--actionmailer/test/abstract_unit.rb36
-rw-r--r--actionmailer/test/assert_select_email_test.rb8
-rw-r--r--actionmailer/test/asset_host_test.rb18
-rw-r--r--actionmailer/test/base_test.rb295
-rw-r--r--actionmailer/test/caching_test.rb273
-rw-r--r--actionmailer/test/delivery_methods_test.rb60
-rw-r--r--actionmailer/test/fixtures/async_mailer/welcome.erb1
-rw-r--r--actionmailer/test/fixtures/caching_mailer/_partial.html.erb3
-rw-r--r--actionmailer/test/fixtures/caching_mailer/fragment_cache.html.erb3
-rw-r--r--actionmailer/test/fixtures/caching_mailer/fragment_cache_in_partials.html.erb1
-rw-r--r--actionmailer/test/fixtures/caching_mailer/fragment_caching_options.html.erb3
-rw-r--r--actionmailer/test/fixtures/caching_mailer/multipart_cache.html.erb3
-rw-r--r--actionmailer/test/fixtures/caching_mailer/multipart_cache.text.erb3
-rw-r--r--actionmailer/test/fixtures/caching_mailer/skip_fragment_cache_digesting.html.erb3
-rw-r--r--actionmailer/test/i18n_with_controller_test.rb41
-rw-r--r--actionmailer/test/log_subscriber_test.rb8
-rw-r--r--actionmailer/test/mail_helper_test.rb12
-rw-r--r--actionmailer/test/mail_layout_test.rb18
-rw-r--r--actionmailer/test/mailers/asset_mailer.rb2
-rw-r--r--actionmailer/test/mailers/base_mailer.rb46
-rw-r--r--actionmailer/test/mailers/caching_mailer.rb23
-rw-r--r--actionmailer/test/mailers/delayed_mailer.rb22
-rw-r--r--actionmailer/test/mailers/proc_mailer.rb13
-rw-r--r--actionmailer/test/message_delivery_test.rb102
-rw-r--r--actionmailer/test/test_case_test.rb42
-rw-r--r--actionmailer/test/test_helper_test.rb10
-rw-r--r--actionmailer/test/url_test.rb56
51 files changed, 1233 insertions, 677 deletions
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index 7c1b0b215a..3b9f503a0b 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -1,87 +1,6 @@
-* Add `config.action_mailer.deliver_later_queue_name` configuration to set the
- mailer queue name.
+* Exception handling: use `rescue_from` to handle exceptions raised by
+ mailer actions, by message delivery, and by deferred delivery jobs.
- *Chris McGrath*
+ *Jeremy Daer*
-* `assert_emails` in block form use the given number as expected value.
- This makes the error message much easier to understand.
-
- *Yuji Yaginuma*
-
-* Add support for inline images in mailer previews by using an interceptor
- class to convert cid: urls in image src attributes to data urls.
-
- *Andrew White*
-
-* Mailer preview now uses `url_for` to fix links to emails for apps running on
- a subdirectory.
-
- *Remo Mueller*
-
-* Mailer previews no longer crash when the `mail` method wasn't called
- (`NullMail`).
-
- Fixes #19849.
-
- *Yves Senn*
-
-* Make sure labels and values line up in mailer previews.
-
- *Yves Senn*
-
-* Add `assert_enqueued_emails` and `assert_no_enqueued_emails`.
-
- Example:
-
- def test_emails
- assert_enqueued_emails 2 do
- ContactMailer.welcome.deliver_later
- ContactMailer.welcome.deliver_later
- end
- end
-
- def test_no_emails
- assert_no_enqueued_emails do
- # No emails enqueued here
- end
- end
-
- *George Claghorn*
-
-* Add `_mailer` suffix to mailers created via generator, following the same
- naming convention used in controllers and jobs.
-
- *Carlos Souza*
-
-* Remove deprecate `*_path` helpers in email views.
-
- *Rafael Mendonça França*
-
-* Remove deprecated `deliver` and `deliver!` methods.
-
- *claudiob*
-
-* Template lookup now respects default locale and I18n fallbacks.
-
- Given the following templates:
-
- mailer/demo.html.erb
- mailer/demo.en.html.erb
- mailer/demo.pt.html.erb
-
- Before this change, for a locale that doesn't have its associated file, the
- `mailer/demo.html.erb` would be rendered even if `en` was the default locale.
-
- Now `mailer/demo.en.html.erb` has precedence over the file without locale.
-
- Also, it is possible to give a fallback.
-
- mailer/demo.pt.html.erb
- mailer/demo.pt-BR.html.erb
-
- So if the locale is `pt-PT`, `mailer/demo.pt.html.erb` will be rendered given
- the right I18n fallback configuration.
-
- *Rafael Mendonça França*
-
-Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/actionmailer/CHANGELOG.md) for previous changes.
+Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/actionmailer/CHANGELOG.md) for previous changes.
diff --git a/actionmailer/MIT-LICENSE b/actionmailer/MIT-LICENSE
index 3ec7a617cf..8573eb1225 100644
--- a/actionmailer/MIT-LICENSE
+++ b/actionmailer/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2015 David Heinemeier Hansson
+Copyright (c) 2004-2016 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/actionmailer/README.rdoc b/actionmailer/README.rdoc
index e5c2ed8c77..397ebe4201 100644
--- a/actionmailer/README.rdoc
+++ b/actionmailer/README.rdoc
@@ -146,7 +146,7 @@ The Base class has the full list of configuration options. Here's an example:
The latest version of Action Mailer can be installed with RubyGems:
- % gem install actionmailer
+ $ gem install actionmailer
Source code can be downloaded as part of the Rails project on GitHub
@@ -173,4 +173,3 @@ Bug reports can be filed for the Ruby on Rails project here:
Feature requests should be discussed on the rails-core mailing list here:
* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
-
diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile
index 7197ea5e27..6f05d236d9 100644
--- a/actionmailer/Rakefile
+++ b/actionmailer/Rakefile
@@ -1,12 +1,14 @@
-require 'rake/testtask'
+require "rake/testtask"
desc "Default Task"
task default: [ :test ]
+task :package
+
# Run the unit tests
Rake::TestTask.new { |t|
t.libs << "test"
- t.pattern = 'test/**/*_test.rb'
+ t.pattern = "test/**/*_test.rb"
t.warning = true
t.verbose = true
t.ruby_opts = ["--dev"] if defined?(JRUBY_VERSION)
@@ -15,7 +17,7 @@ Rake::TestTask.new { |t|
namespace :test do
task :isolated do
Dir.glob("test/**/*_test.rb").all? do |file|
- sh(Gem.ruby, '-w', '-Ilib:test', file)
- end or raise "Failures"
+ sh(Gem.ruby, "-w", "-Ilib:test", file)
+ end || raise("Failures")
end
end
diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec
index 782b208ef4..e75dae6cf9 100644
--- a/actionmailer/actionmailer.gemspec
+++ b/actionmailer/actionmailer.gemspec
@@ -1,28 +1,28 @@
-version = File.read(File.expand_path('../../RAILS_VERSION', __FILE__)).strip
+version = File.read(File.expand_path("../../RAILS_VERSION", __FILE__)).strip
Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
- s.name = 'actionmailer'
+ s.name = "actionmailer"
s.version = version
- s.summary = 'Email composition, delivery, and receiving framework (part of Rails).'
- s.description = 'Email on Rails. Compose, deliver, receive, and test emails using the familiar controller/view pattern. First-class support for multipart email and attachments.'
+ s.summary = "Email composition, delivery, and receiving framework (part of Rails)."
+ s.description = "Email on Rails. Compose, deliver, receive, and test emails using the familiar controller/view pattern. First-class support for multipart email and attachments."
- s.required_ruby_version = '>= 2.2.2'
+ s.required_ruby_version = ">= 2.2.2"
- s.license = 'MIT'
+ s.license = "MIT"
- s.author = 'David Heinemeier Hansson'
- s.email = 'david@loudthinking.com'
- s.homepage = 'http://www.rubyonrails.org'
+ s.author = "David Heinemeier Hansson"
+ s.email = "david@loudthinking.com"
+ s.homepage = "http://rubyonrails.org"
- s.files = Dir['CHANGELOG.md', 'README.rdoc', 'MIT-LICENSE', 'lib/**/*']
- s.require_path = 'lib'
- s.requirements << 'none'
+ s.files = Dir["CHANGELOG.md", "README.rdoc", "MIT-LICENSE", "lib/**/*"]
+ s.require_path = "lib"
+ s.requirements << "none"
- s.add_dependency 'actionpack', version
- s.add_dependency 'actionview', version
- s.add_dependency 'activejob', version
+ s.add_dependency "actionpack", version
+ s.add_dependency "actionview", version
+ s.add_dependency "activejob", version
- s.add_dependency 'mail', ['~> 2.5', '>= 2.5.4']
- s.add_dependency 'rails-dom-testing', '~> 1.0', '>= 1.0.5'
+ s.add_dependency "mail", ["~> 2.5", ">= 2.5.4"]
+ s.add_dependency "rails-dom-testing", "~> 2.0"
end
diff --git a/actionmailer/bin/test b/actionmailer/bin/test
index 404cabba51..a7beb14b27 100755
--- a/actionmailer/bin/test
+++ b/actionmailer/bin/test
@@ -1,4 +1,4 @@
#!/usr/bin/env ruby
-COMPONENT_ROOT = File.expand_path("../../", __FILE__)
+
+COMPONENT_ROOT = File.expand_path("..", __dir__)
require File.expand_path("../tools/test", COMPONENT_ROOT)
-exit Minitest.run(ARGV)
diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb
index 291a8c1e34..668bc99435 100644
--- a/actionmailer/lib/action_mailer.rb
+++ b/actionmailer/lib/action_mailer.rb
@@ -1,5 +1,5 @@
#--
-# Copyright (c) 2004-2015 David Heinemeier Hansson
+# Copyright (c) 2004-2016 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -21,15 +21,15 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-require 'abstract_controller'
-require 'action_mailer/version'
+require "abstract_controller"
+require "action_mailer/version"
# Common Active Support usage in Action Mailer
-require 'active_support/rails'
-require 'active_support/core_ext/class'
-require 'active_support/core_ext/module/attr_internal'
-require 'active_support/core_ext/string/inflections'
-require 'active_support/lazy_load_hooks'
+require "active_support/rails"
+require "active_support/core_ext/class"
+require "active_support/core_ext/module/attr_internal"
+require "active_support/core_ext/string/inflections"
+require "active_support/lazy_load_hooks"
module ActionMailer
extend ::ActiveSupport::Autoload
@@ -43,9 +43,16 @@ module ActionMailer
autoload :InlinePreviewInterceptor
autoload :MailHelper
autoload :Preview
- autoload :Previews, 'action_mailer/preview'
+ autoload :Previews, "action_mailer/preview"
autoload :TestCase
autoload :TestHelper
autoload :MessageDelivery
autoload :DeliveryJob
end
+
+autoload :Mime, "action_dispatch/http/mime_type"
+
+ActiveSupport.on_load(:action_view) do
+ ActionView::Base.default_formats ||= Mime::SET.symbols
+ ActionView::Template::Types.delegate_to Mime
+end
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 7022c04a9a..19408f2a48 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -1,10 +1,11 @@
-require 'mail'
-require 'action_mailer/collector'
-require 'active_support/core_ext/string/inflections'
-require 'active_support/core_ext/hash/except'
-require 'active_support/core_ext/module/anonymous'
+require "mail"
+require "action_mailer/collector"
+require "active_support/core_ext/string/inflections"
+require "active_support/core_ext/hash/except"
+require "active_support/core_ext/module/anonymous"
-require 'action_mailer/log_subscriber'
+require "action_mailer/log_subscriber"
+require "action_mailer/rescuable"
module ActionMailer
# Action Mailer allows you to send email from your application using a mailer model and views.
@@ -86,7 +87,7 @@ module ActionMailer
# Like Action Controller, each mailer class has a corresponding view directory in which each
# method of the class looks for a template with its name.
#
- # To define a template to be used with a mailing, create an <tt>.erb</tt> file with the same
+ # To define a template to be used with a mailer, create an <tt>.erb</tt> file with the same
# name as the method in your mailer model. For example, in the mailer defined above, the template at
# <tt>app/views/notifier_mailer/welcome.text.erb</tt> would be used to generate the email.
#
@@ -132,6 +133,8 @@ module ActionMailer
#
# config.action_mailer.default_url_options = { host: "example.com" }
#
+ # By default when <tt>config.force_ssl</tt> is true, URLs generated for hosts will use the HTTPS protocol.
+ #
# = Sending mail
#
# Once a mailer action and template are defined, you can deliver your message or defer its creation and
@@ -142,7 +145,7 @@ module ActionMailer
# mail.deliver_now # generates and sends the email now
#
# The <tt>ActionMailer::MessageDelivery</tt> class is a wrapper around a delegate that will call
- # your method to generate the mail. If you want direct access to delegator, or <tt>Mail::Message</tt>,
+ # your method to generate the mail. If you want direct access to the delegator, or <tt>Mail::Message</tt>,
# you can call the <tt>message</tt> method on the <tt>ActionMailer::MessageDelivery</tt> object.
#
# NotifierMailer.welcome(User.first).message # => a Mail::Message object
@@ -161,7 +164,7 @@ module ActionMailer
#
# Multipart messages can also be used implicitly because Action Mailer will automatically detect and use
# multipart templates, where each template is named after the name of the action, followed by the content
- # type. Each such detected template will be added as a separate part to the message.
+ # type. Each such detected template will be added to the message, as a separate part.
#
# For example, if the following templates exist:
# * signup_notification.text.erb
@@ -286,8 +289,8 @@ module ActionMailer
# end
#
# Note that the proc is evaluated right at the start of the mail message generation, so if you
- # set something in the default using a proc, and then set the same thing inside of your
- # mailer method, it will get over written by the mailer method.
+ # set something in the default hash using a proc, and then set the same thing inside of your
+ # mailer method, it will get overwritten by the mailer method.
#
# It is also possible to set these default options that will be used in all mailers through
# the <tt>default_options=</tt> configuration in <tt>config/application.rb</tt>:
@@ -387,13 +390,13 @@ module ActionMailer
# to use it. Defaults to <tt>true</tt>.
# * <tt>:openssl_verify_mode</tt> - When using TLS, you can set how OpenSSL checks the certificate. This is
# really useful if you need to validate a self-signed and/or a wildcard certificate. You can use the name
- # of an OpenSSL verify constant (<tt>'none'</tt>, <tt>'peer'</tt>, <tt>'client_once'</tt>,
- # <tt>'fail_if_no_peer_cert'</tt>) or directly the constant (<tt>OpenSSL::SSL::VERIFY_NONE</tt>,
- # <tt>OpenSSL::SSL::VERIFY_PEER</tt>, ...).
+ # of an OpenSSL verify constant (<tt>'none'</tt> or <tt>'peer'</tt>) or directly the constant
+ # (<tt>OpenSSL::SSL::VERIFY_NONE</tt> or <tt>OpenSSL::SSL::VERIFY_PEER</tt>).
+ # <tt>:ssl/:tls</tt> Enables the SMTP connection to use SMTP/TLS (SMTPS: SMTP over direct TLS connection)
#
# * <tt>sendmail_settings</tt> - Allows you to override options for the <tt>:sendmail</tt> delivery method.
# * <tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>.
- # * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i -t</tt> with <tt>-f sender@address</tt>
+ # * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i</tt> with <tt>-f sender@address</tt>
# added automatically before the message is sent.
#
# * <tt>file_settings</tt> - Allows you to override options for the <tt>:file</tt> delivery method.
@@ -414,9 +417,10 @@ module ActionMailer
# * <tt>deliveries</tt> - Keeps an array of all the emails sent out through the Action Mailer with
# <tt>delivery_method :test</tt>. Most useful for unit and functional testing.
#
- # * <tt>deliver_later_queue_name</tt> - The name of the queue used with <tt>deliver_later</tt>
+ # * <tt>deliver_later_queue_name</tt> - The name of the queue used with <tt>deliver_later</tt>.
class Base < AbstractController::Base
include DeliveryMethods
+ include Rescuable
include Previews
abstract!
@@ -428,6 +432,7 @@ module ActionMailer
include AbstractController::Translation
include AbstractController::AssetPaths
include AbstractController::Callbacks
+ include AbstractController::Caching
include ActionView::Layouts
@@ -439,8 +444,6 @@ module ActionMailer
helper ActionMailer::MailHelper
- private_class_method :new #:nodoc:
-
class_attribute :default_params
self.default_params = {
mime_version: "1.0",
@@ -464,31 +467,27 @@ module ActionMailer
# 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 register_observer(observer)
- delivery_observer = case observer
- when String, Symbol
- observer.to_s.camelize.constantize
- else
- observer
- end
-
- Mail.register_observer(delivery_observer)
+ Mail.register_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.
def register_interceptor(interceptor)
- delivery_interceptor = case interceptor
- when String, Symbol
- interceptor.to_s.camelize.constantize
- else
- interceptor
- end
+ Mail.register_interceptor(observer_class_for(interceptor))
+ end
- Mail.register_interceptor(delivery_interceptor)
+ def observer_class_for(value) # :nodoc:
+ case value
+ when String, Symbol
+ value.to_s.camelize.constantize
+ else
+ value
+ end
end
+ private :observer_class_for
- # Returns the name of current mailer. This method is also being used as a path for a view lookup.
+ # Returns the name of the current mailer. This method is also being used as a path for a view lookup.
# If this is an anonymous mailer, this method will return +anonymous+ instead.
def mailer_name
@mailer_name ||= anonymous? ? "anonymous" : name.underscore
@@ -545,13 +544,9 @@ module ActionMailer
end
end
- def respond_to?(method, include_private = false) #:nodoc:
- super || action_methods.include?(method.to_s)
- end
+ private
- protected
-
- def set_payload_for_mail(payload, mail) #:nodoc:
+ def set_payload_for_mail(payload, mail)
payload[:mailer] = name
payload[:message_id] = mail.message_id
payload[:subject] = mail.subject
@@ -563,13 +558,17 @@ module ActionMailer
payload[:mail] = mail.encoded
end
- def method_missing(method_name, *args) # :nodoc:
+ def method_missing(method_name, *args)
if action_methods.include?(method_name.to_s)
MessageDelivery.new(self, method_name, *args)
else
super
end
end
+
+ def respond_to_missing?(method, include_all = false)
+ action_methods.include?(method.to_s)
+ end
end
attr_internal :message
@@ -578,11 +577,10 @@ module ActionMailer
# will be initialized according to the named method. If not, the mailer will
# remain uninitialized (useful when you only need to invoke the "receive"
# method, for instance).
- def initialize(method_name=nil, *args)
+ def initialize
super()
@_mail_was_called = false
@_message = Mail.new
- process(method_name, *args) if method_name
end
def process(method_name, *args) #:nodoc:
@@ -598,10 +596,10 @@ module ActionMailer
end
class NullMail #:nodoc:
- def body; '' end
+ def body; "" end
def header; {} end
- def respond_to?(string, include_all=false)
+ def respond_to?(string, include_all = false)
true
end
@@ -661,21 +659,21 @@ module ActionMailer
#
# mail.attachments['filename.jpg'] = File.read('/path/to/filename.jpg')
#
- # If you do this, then Mail will take the file name and work out the mime type
- # set the Content-Type, Content-Disposition, Content-Transfer-Encoding and
- # base64 encode the contents of the attachment all for you.
+ # If you do this, then Mail will take the file name and work out the mime type.
+ # It will also set the Content-Type, Content-Disposition, Content-Transfer-Encoding
+ # and encode the contents of the attachment in Base64.
#
# You can also specify overrides if you want by passing a hash instead of a string:
#
- # mail.attachments['filename.jpg'] = {mime_type: 'application/x-gzip',
+ # mail.attachments['filename.jpg'] = {mime_type: 'application/gzip',
# content: File.read('/path/to/filename.jpg')}
#
- # If you want to use a different encoding than Base64, you can pass an encoding in,
- # but then it is up to you to pass in the content pre-encoded, and don't expect
- # Mail to know how to decode this data:
+ # If you want to use encoding other than Base64 then you will need to pass encoding
+ # type along with the pre-encoded content as Mail doesn't know how to decode the
+ # data:
#
# file_content = SpecialEncode(File.read('/path/to/filename.jpg'))
- # mail.attachments['filename.jpg'] = {mime_type: 'application/x-gzip',
+ # mail.attachments['filename.jpg'] = {mime_type: 'application/gzip',
# encoding: 'SpecialEncoding',
# content: file_content }
#
@@ -797,151 +795,169 @@ module ActionMailer
# end
#
def mail(headers = {}, &block)
- return @_message if @_mail_was_called && headers.blank? && !block
-
- m = @_message
+ return message if @_mail_was_called && headers.blank? && !block
# At the beginning, do not consider class default for content_type
content_type = headers[:content_type]
- # Call all the procs (if any)
- default_values = {}
- self.class.default.each do |k,v|
- default_values[k] = v.is_a?(Proc) ? instance_eval(&v) : v
- end
-
- # Handle defaults
- headers = headers.reverse_merge(default_values)
- headers[:subject] ||= default_i18n_subject
+ headers = apply_defaults(headers)
# Apply charset at the beginning so all fields are properly quoted
- m.charset = charset = headers[:charset]
+ message.charset = charset = headers[:charset]
# Set configure delivery behavior
- wrap_delivery_behavior!(headers.delete(:delivery_method), headers.delete(:delivery_method_options))
+ wrap_delivery_behavior!(headers[:delivery_method], headers[:delivery_method_options])
- # Assign all headers except parts_order, content_type and body
- assignable = headers.except(:parts_order, :content_type, :body, :template_name, :template_path)
- assignable.each { |k, v| m[k] = v }
+ assign_headers_to_message(message, headers)
# Render the templates and blocks
responses = collect_responses(headers, &block)
@_mail_was_called = true
- create_parts_from_responses(m, responses)
+ create_parts_from_responses(message, responses)
# Setup content type, reapply charset and handle parts order
- m.content_type = set_content_type(m, content_type, headers[:content_type])
- m.charset = charset
+ message.content_type = set_content_type(message, content_type, headers[:content_type])
+ message.charset = charset
- if m.multipart?
- m.body.set_sort_order(headers[:parts_order])
- m.body.sort_parts!
+ if message.multipart?
+ message.body.set_sort_order(headers[:parts_order])
+ message.body.sort_parts!
end
- m
+ message
end
- protected
-
- # Used by #mail to set the content type of the message.
- #
- # It will use the given +user_content_type+, or multipart if the mail
- # message has any attachments. If the attachments are inline, the content
- # type will be "multipart/related", otherwise "multipart/mixed".
- #
- # If there is no content type passed in via headers, and there are no
- # attachments, or the message is multipart, then the default content type is
- # used.
- def set_content_type(m, user_content_type, class_default)
- params = m.content_type_parameters || {}
- case
- when user_content_type.present?
- user_content_type
- when m.has_attachments?
- if m.attachments.detect(&:inline?)
- ["multipart", "related", params]
+ private
+
+ # Used by #mail to set the content type of the message.
+ #
+ # It will use the given +user_content_type+, or multipart if the mail
+ # message has any attachments. If the attachments are inline, the content
+ # type will be "multipart/related", otherwise "multipart/mixed".
+ #
+ # If there is no content type passed in via headers, and there are no
+ # attachments, or the message is multipart, then the default content type is
+ # used.
+ def set_content_type(m, user_content_type, class_default) # :doc:
+ params = m.content_type_parameters || {}
+ case
+ when user_content_type.present?
+ user_content_type
+ when m.has_attachments?
+ if m.attachments.detect(&:inline?)
+ ["multipart", "related", params]
+ else
+ ["multipart", "mixed", params]
+ end
+ when m.multipart?
+ ["multipart", "alternative", params]
else
- ["multipart", "mixed", params]
+ m.content_type || class_default
end
- when m.multipart?
- ["multipart", "alternative", params]
- else
- m.content_type || class_default
end
- end
- # Translates the +subject+ using Rails I18n class under <tt>[mailer_scope, action_name]</tt> scope.
- # If it does not find a translation for the +subject+ under the specified scope it will default to a
- # humanized version of the <tt>action_name</tt>.
- # If the subject has interpolations, you can pass them through the +interpolations+ parameter.
- def default_i18n_subject(interpolations = {})
- mailer_scope = self.class.mailer_name.tr('/', '.')
- I18n.t(:subject, interpolations.merge(scope: [mailer_scope, action_name], default: action_name.humanize))
- end
+ # Translates the +subject+ using Rails I18n class under <tt>[mailer_scope, action_name]</tt> scope.
+ # If it does not find a translation for the +subject+ under the specified scope it will default to a
+ # humanized version of the <tt>action_name</tt>.
+ # If the subject has interpolations, you can pass them through the +interpolations+ parameter.
+ def default_i18n_subject(interpolations = {}) # :doc:
+ mailer_scope = self.class.mailer_name.tr("/", ".")
+ I18n.t(:subject, interpolations.merge(scope: [mailer_scope, action_name], default: action_name.humanize))
+ end
- def collect_responses(headers) #:nodoc:
- responses = []
-
- if block_given?
- collector = ActionMailer::Collector.new(lookup_context) { render(action_name) }
- yield(collector)
- responses = collector.responses
- elsif headers[:body]
- responses << {
- body: headers.delete(:body),
- content_type: self.class.default[:content_type] || "text/plain"
- }
- else
- templates_path = headers.delete(:template_path) || self.class.mailer_name
- templates_name = headers.delete(:template_name) || action_name
+ # Emails do not support relative path links.
+ def self.supports_path? # :doc:
+ false
+ end
- each_template(Array(templates_path), templates_name) do |template|
- self.formats = template.formats
+ def apply_defaults(headers)
+ default_values = self.class.default.map do |key, value|
+ [
+ key,
+ value.is_a?(Proc) ? instance_eval(&value) : value
+ ]
+ end.to_h
+
+ headers_with_defaults = headers.reverse_merge(default_values)
+ headers_with_defaults[:subject] ||= default_i18n_subject
+ headers_with_defaults
+ end
+
+ def assign_headers_to_message(message, headers)
+ assignable = headers.except(:parts_order, :content_type, :body, :template_name,
+ :template_path, :delivery_method, :delivery_method_options)
+ assignable.each { |k, v| message[k] = v }
+ end
+
+ def collect_responses(headers)
+ if block_given?
+ collector = ActionMailer::Collector.new(lookup_context) { render(action_name) }
+ yield(collector)
+ collector.responses
+ elsif headers[:body]
+ [{
+ body: headers.delete(:body),
+ content_type: self.class.default[:content_type] || "text/plain"
+ }]
+ else
+ collect_responses_from_templates(headers)
+ end
+ end
+
+ def collect_responses_from_templates(headers)
+ templates_path = headers[:template_path] || self.class.mailer_name
+ templates_name = headers[:template_name] || action_name
- responses << {
+ each_template(Array(templates_path), templates_name).map do |template|
+ self.formats = template.formats
+ {
body: render(template: template),
content_type: template.type.to_s
}
end
end
- responses
- end
+ def each_template(paths, name, &block)
+ templates = lookup_context.find_all(name, paths)
+ if templates.empty?
+ raise ActionView::MissingTemplate.new(paths, name, paths, false, "mailer")
+ else
+ templates.uniq(&:formats).each(&block)
+ end
+ end
- def each_template(paths, name, &block) #:nodoc:
- templates = lookup_context.find_all(name, paths)
- if templates.empty?
- raise ActionView::MissingTemplate.new(paths, name, paths, false, 'mailer')
- else
- templates.uniq(&:formats).each(&block)
+ def create_parts_from_responses(m, responses)
+ if responses.size == 1 && !m.has_attachments?
+ responses[0].each { |k, v| m[k] = v }
+ elsif responses.size > 1 && m.has_attachments?
+ container = Mail::Part.new
+ container.content_type = "multipart/alternative"
+ responses.each { |r| insert_part(container, r, m.charset) }
+ m.add_part(container)
+ else
+ responses.each { |r| insert_part(m, r, m.charset) }
+ end
end
- end
- def create_parts_from_responses(m, responses) #:nodoc:
- if responses.size == 1 && !m.has_attachments?
- responses[0].each { |k,v| m[k] = v }
- elsif responses.size > 1 && m.has_attachments?
- container = Mail::Part.new
- container.content_type = "multipart/alternative"
- responses.each { |r| insert_part(container, r, m.charset) }
- m.add_part(container)
- else
- responses.each { |r| insert_part(m, r, m.charset) }
+ def insert_part(container, response, charset)
+ response[:charset] ||= charset
+ part = Mail::Part.new(response)
+ container.add_part(part)
end
- end
- def insert_part(container, response, charset) #:nodoc:
- response[:charset] ||= charset
- part = Mail::Part.new(response)
- container.add_part(part)
- end
+ # This and #instrument_name is for caching instrument
+ def instrument_payload(key)
+ {
+ mailer: mailer_name,
+ key: key
+ }
+ end
- # Emails do not support relative path links.
- def self.supports_path?
- false
- end
+ def instrument_name
+ "action_mailer"
+ end
- ActiveSupport.run_load_hooks(:action_mailer, self)
+ ActiveSupport.run_load_hooks(:action_mailer, self)
end
end
diff --git a/actionmailer/lib/action_mailer/collector.rb b/actionmailer/lib/action_mailer/collector.rb
index e8883a8235..d97a73d65a 100644
--- a/actionmailer/lib/action_mailer/collector.rb
+++ b/actionmailer/lib/action_mailer/collector.rb
@@ -1,6 +1,6 @@
-require 'abstract_controller/collector'
-require 'active_support/core_ext/hash/reverse_merge'
-require 'active_support/core_ext/array/extract_options'
+require "abstract_controller/collector"
+require "active_support/core_ext/hash/reverse_merge"
+require "active_support/core_ext/array/extract_options"
module ActionMailer
class Collector
diff --git a/actionmailer/lib/action_mailer/delivery_job.rb b/actionmailer/lib/action_mailer/delivery_job.rb
index 52772af2d3..a617daa87e 100644
--- a/actionmailer/lib/action_mailer/delivery_job.rb
+++ b/actionmailer/lib/action_mailer/delivery_job.rb
@@ -1,13 +1,34 @@
-require 'active_job'
+require "active_job"
module ActionMailer
# The <tt>ActionMailer::DeliveryJob</tt> class is used when you
# want to send emails outside of the request-response cycle.
+ #
+ # Exceptions are rescued and handled by the mailer class.
class DeliveryJob < ActiveJob::Base # :nodoc:
queue_as { ActionMailer::Base.deliver_later_queue_name }
+ rescue_from StandardError, with: :handle_exception_with_mailer_class
+
def perform(mailer, mail_method, delivery_method, *args) #:nodoc:
mailer.constantize.public_send(mail_method, *args).send(delivery_method)
end
+
+ private
+ # "Deserialize" the mailer class name by hand in case another argument
+ # (like a Global ID reference) raised DeserializationError.
+ def mailer_class
+ if mailer = Array(@serialized_arguments).first || Array(arguments).first
+ mailer.constantize
+ end
+ end
+
+ def handle_exception_with_mailer_class(exception)
+ if klass = mailer_class
+ klass.handle_exception exception
+ else
+ raise exception
+ end
+ end
end
end
diff --git a/actionmailer/lib/action_mailer/delivery_methods.rb b/actionmailer/lib/action_mailer/delivery_methods.rb
index 4758b55a2a..be98f4c65e 100644
--- a/actionmailer/lib/action_mailer/delivery_methods.rb
+++ b/actionmailer/lib/action_mailer/delivery_methods.rb
@@ -1,4 +1,4 @@
-require 'tmpdir'
+require "tmpdir"
module ActionMailer
# This module handles everything related to mail delivery, from registering
@@ -25,7 +25,7 @@ module ActionMailer
add_delivery_method :smtp, Mail::SMTP,
address: "localhost",
port: 25,
- domain: 'localhost.localdomain',
+ domain: "localhost.localdomain",
user_name: nil,
password: nil,
authentication: nil,
@@ -35,8 +35,8 @@ module ActionMailer
location: defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"
add_delivery_method :sendmail, Mail::Sendmail,
- location: '/usr/sbin/sendmail',
- arguments: '-i -t'
+ location: "/usr/sbin/sendmail",
+ arguments: "-i"
add_delivery_method :test, Mail::TestMailer
end
@@ -51,14 +51,14 @@ module ActionMailer
#
# add_delivery_method :sendmail, Mail::Sendmail,
# location: '/usr/sbin/sendmail',
- # arguments: '-i -t'
- def add_delivery_method(symbol, klass, default_options={})
+ # arguments: '-i'
+ def add_delivery_method(symbol, klass, default_options = {})
class_attribute(:"#{symbol}_settings") unless respond_to?(:"#{symbol}_settings")
send(:"#{symbol}_settings=", default_options)
self.delivery_methods = delivery_methods.merge(symbol.to_sym => klass).freeze
end
- def wrap_delivery_behavior(mail, method=nil, options=nil) # :nodoc:
+ def wrap_delivery_behavior(mail, method = nil, options = nil) # :nodoc:
method ||= self.delivery_method
mail.delivery_handler = self
diff --git a/actionmailer/lib/action_mailer/gem_version.rb b/actionmailer/lib/action_mailer/gem_version.rb
index ac79788cf0..7dafceef2b 100644
--- a/actionmailer/lib/action_mailer/gem_version.rb
+++ b/actionmailer/lib/action_mailer/gem_version.rb
@@ -1,12 +1,12 @@
module ActionMailer
- # Returns the version of the currently loaded Action Mailer as a <tt>Gem::Version</tt>
+ # Returns the version of the currently loaded Action Mailer as a <tt>Gem::Version</tt>.
def self.gem_version
Gem::Version.new VERSION::STRING
end
module VERSION
MAJOR = 5
- MINOR = 0
+ MINOR = 1
TINY = 0
PRE = "alpha"
diff --git a/actionmailer/lib/action_mailer/inline_preview_interceptor.rb b/actionmailer/lib/action_mailer/inline_preview_interceptor.rb
index 6d02b39225..9087d335fa 100644
--- a/actionmailer/lib/action_mailer/inline_preview_interceptor.rb
+++ b/actionmailer/lib/action_mailer/inline_preview_interceptor.rb
@@ -1,9 +1,9 @@
-require 'base64'
+require "base64"
module ActionMailer
# Implements a mailer preview interceptor that converts image tag src attributes
# that use inline cid: style urls to data: style urls so that they are visible
- # when previewing a HTML email in a web browser.
+ # when previewing an HTML email in a web browser.
#
# This interceptor is enabled by default. To disable it, delete it from the
# <tt>ActionMailer::Base.preview_interceptors</tt> array:
@@ -11,7 +11,7 @@ module ActionMailer
# ActionMailer::Base.preview_interceptors.delete(ActionMailer::InlinePreviewInterceptor)
#
class InlinePreviewInterceptor
- PATTERN = /src=(?:"cid:[^"]+"|'cid:[^']+')/i
+ PATTERN = /src=(?:"cid:[^"]+"|'cid:[^']+')/i
include Base64
@@ -55,7 +55,7 @@ module ActionMailer
end
def find_part(cid)
- message.all_parts.find{ |p| p.attachment? && p.cid == cid }
+ message.all_parts.find { |p| p.attachment? && p.cid == cid }
end
end
end
diff --git a/actionmailer/lib/action_mailer/log_subscriber.rb b/actionmailer/lib/action_mailer/log_subscriber.rb
index c2f671fdac..2c058ccf66 100644
--- a/actionmailer/lib/action_mailer/log_subscriber.rb
+++ b/actionmailer/lib/action_mailer/log_subscriber.rb
@@ -1,4 +1,4 @@
-require 'active_support/log_subscriber'
+require "active_support/log_subscriber"
module ActionMailer
# Implements the ActiveSupport::LogSubscriber for logging notifications when
@@ -7,8 +7,8 @@ module ActionMailer
# An email was delivered.
def deliver(event)
info do
- recipients = Array(event.payload[:to]).join(', ')
- "\nSent mail to #{recipients} (#{event.duration.round(1)}ms)"
+ recipients = Array(event.payload[:to]).join(", ")
+ "Sent mail to #{recipients} (#{event.duration.round(1)}ms)"
end
debug { event.payload[:mail] }
@@ -16,7 +16,7 @@ module ActionMailer
# An email was received.
def receive(event)
- info { "\nReceived mail (#{event.duration.round(1)}ms)" }
+ info { "Received mail (#{event.duration.round(1)}ms)" }
debug { event.payload[:mail] }
end
@@ -25,11 +25,11 @@ module ActionMailer
debug do
mailer = event.payload[:mailer]
action = event.payload[:action]
- "\n#{mailer}##{action}: processed outbound mail in #{event.duration.round(1)}ms"
+ "#{mailer}##{action}: processed outbound mail in #{event.duration.round(1)}ms"
end
end
- # Use the logger configured for ActionMailer::Base
+ # Use the logger configured for ActionMailer::Base.
def logger
ActionMailer::Base.logger
end
diff --git a/actionmailer/lib/action_mailer/mail_helper.rb b/actionmailer/lib/action_mailer/mail_helper.rb
index 239974e7b1..e04fc08866 100644
--- a/actionmailer/lib/action_mailer/mail_helper.rb
+++ b/actionmailer/lib/action_mailer/mail_helper.rb
@@ -54,7 +54,7 @@ module ActionMailer
sentences = [[]]
text.split.each do |word|
- if sentences.first.present? && (sentences.last + [word]).join(' ').length > len
+ if sentences.first.present? && (sentences.last + [word]).join(" ").length > len
sentences << [word]
else
sentences.last << word
diff --git a/actionmailer/lib/action_mailer/message_delivery.rb b/actionmailer/lib/action_mailer/message_delivery.rb
index ff2cb0fd01..cf7c57e6bf 100644
--- a/actionmailer/lib/action_mailer/message_delivery.rb
+++ b/actionmailer/lib/action_mailer/message_delivery.rb
@@ -1,7 +1,6 @@
-require 'delegate'
+require "delegate"
module ActionMailer
-
# The <tt>ActionMailer::MessageDelivery</tt> class is used by
# <tt>ActionMailer::Base</tt> when creating a new mailer.
# <tt>MessageDelivery</tt> is a wrapper (+Delegator+ subclass) around a lazy
@@ -14,25 +13,35 @@ module ActionMailer
# Notifier.welcome(User.first).deliver_later # enqueue email delivery as a job through Active Job
# Notifier.welcome(User.first).message # a Mail::Message object
class MessageDelivery < Delegator
- def initialize(mailer, mail_method, *args) #:nodoc:
- @mailer = mailer
- @mail_method = mail_method
- @args = args
+ def initialize(mailer_class, action, *args) #:nodoc:
+ @mailer_class, @action, @args = mailer_class, action, args
+
+ # The mail is only processed if we try to call any methods on it.
+ # Typical usage will leave it unloaded and call deliver_later.
+ @processed_mailer = nil
+ @mail_message = nil
end
+ # Method calls are delegated to the Mail::Message that's ready to deliver.
def __getobj__ #:nodoc:
- @obj ||= @mailer.send(:new, @mail_method, *@args).message
+ @mail_message ||= processed_mailer.message
end
- def __setobj__(obj) #:nodoc:
- @obj = obj
+ # Unused except for delegator internals (dup, marshaling).
+ def __setobj__(mail_message) #:nodoc:
+ @mail_message = mail_message
end
- # Returns the Mail::Message object
+ # Returns the resulting Mail::Message
def message
__getobj__
end
+ # Was the delegate loaded, causing the mailer action to be processed?
+ def processed?
+ @processed_mailer || @mail_message
+ end
+
# Enqueues the email to be delivered through Active Job. When the
# job runs it will send the email using +deliver_now!+. That means
# that the message will be sent bypassing checking +perform_deliveries+
@@ -47,7 +56,7 @@ module ActionMailer
# * <tt>:wait</tt> - Enqueue the email to be delivered with a delay
# * <tt>:wait_until</tt> - Enqueue the email to be delivered at (after) a specific date / time
# * <tt>:queue</tt> - Enqueue the email on the specified queue
- def deliver_later!(options={})
+ def deliver_later!(options = {})
enqueue_delivery :deliver_now!, options
end
@@ -60,10 +69,10 @@ module ActionMailer
#
# Options:
#
- # * <tt>:wait</tt> - Enqueue the email to be delivered with a delay
- # * <tt>:wait_until</tt> - Enqueue the email to be delivered at (after) a specific date / time
- # * <tt>:queue</tt> - Enqueue the email on the specified queue
- def deliver_later(options={})
+ # * <tt>:wait</tt> - Enqueue the email to be delivered with a delay.
+ # * <tt>:wait_until</tt> - Enqueue the email to be delivered at (after) a specific date / time.
+ # * <tt>:queue</tt> - Enqueue the email on the specified queue.
+ def deliver_later(options = {})
enqueue_delivery :deliver_now, options
end
@@ -73,7 +82,9 @@ module ActionMailer
# Notifier.welcome(User.first).deliver_now!
#
def deliver_now!
- message.deliver!
+ processed_mailer.handle_exceptions do
+ message.deliver!
+ end
end
# Delivers an email:
@@ -81,14 +92,34 @@ module ActionMailer
# Notifier.welcome(User.first).deliver_now
#
def deliver_now
- message.deliver
+ processed_mailer.handle_exceptions do
+ message.deliver
+ end
end
private
+ # Returns the processed Mailer instance. We keep this instance
+ # on hand so we can delegate exception handling to it.
+ def processed_mailer
+ @processed_mailer ||= @mailer_class.new.tap do |mailer|
+ mailer.process @action, *@args
+ end
+ end
- def enqueue_delivery(delivery_method, options={})
- args = @mailer.name, @mail_method.to_s, delivery_method.to_s, *@args
- ActionMailer::DeliveryJob.set(options).perform_later(*args)
+ def enqueue_delivery(delivery_method, options = {})
+ if processed?
+ ::Kernel.raise "You've accessed the message before asking to " \
+ "deliver it later, so you may have made local changes that would " \
+ "be silently lost if we enqueued a job to deliver it. Why? Only " \
+ "the mailer method *arguments* are passed with the delivery job! " \
+ "Do not access the message in any way if you mean to deliver it " \
+ "later. Workarounds: 1. don't touch the message before calling " \
+ "#deliver_later, 2. only touch the message *within your mailer " \
+ "method*, or 3. use a custom Active Job instead of #deliver_later."
+ else
+ args = @mailer_class.name, @action.to_s, delivery_method.to_s, *@args
+ ::ActionMailer::DeliveryJob.set(options).perform_later(*args)
+ end
end
end
end
diff --git a/actionmailer/lib/action_mailer/preview.rb b/actionmailer/lib/action_mailer/preview.rb
index 25ad7ee721..c147ca78d0 100644
--- a/actionmailer/lib/action_mailer/preview.rb
+++ b/actionmailer/lib/action_mailer/preview.rb
@@ -1,4 +1,4 @@
-require 'active_support/descendants_tracker'
+require "active_support/descendants_tracker"
module ActionMailer
module Previews #:nodoc:
@@ -34,7 +34,8 @@ module ActionMailer
# Either a class or a string can be passed in as the Interceptor. If a
# string is passed in it will be <tt>constantize</tt>d.
def register_preview_interceptor(interceptor)
- preview_interceptor = case interceptor
+ preview_interceptor = \
+ case interceptor
when String, Symbol
interceptor.to_s.camelize.constantize
else
@@ -52,7 +53,7 @@ module ActionMailer
extend ActiveSupport::DescendantsTracker
class << self
- # Returns all mailer preview classes
+ # Returns all mailer preview classes.
def all
load_previews if descendants.empty?
descendants
@@ -68,47 +69,47 @@ module ActionMailer
message
end
- # Returns all of the available email previews
+ # Returns all of the available email previews.
def emails
public_instance_methods(false).map(&:to_s).sort
end
- # Returns true if the email exists
+ # Returns true if the email exists.
def email_exists?(email)
emails.include?(email)
end
- # Returns true if the preview exists
+ # Returns true if the preview exists.
def exists?(preview)
- all.any?{ |p| p.preview_name == preview }
+ all.any? { |p| p.preview_name == preview }
end
- # Find a mailer preview by its underscored class name
+ # Find a mailer preview by its underscored class name.
def find(preview)
- all.find{ |p| p.preview_name == preview }
+ all.find { |p| p.preview_name == preview }
end
- # Returns the underscored name of the mailer preview without the suffix
+ # Returns the underscored name of the mailer preview without the suffix.
def preview_name
- name.sub(/Preview$/, '').underscore
+ name.sub(/Preview$/, "").underscore
end
- protected
- def load_previews #:nodoc:
+ private
+ def load_previews
if preview_path
- Dir["#{preview_path}/**/*_preview.rb"].each{ |file| require_dependency file }
+ Dir["#{preview_path}/**/*_preview.rb"].each { |file| require_dependency file }
end
end
- def preview_path #:nodoc:
+ def preview_path
Base.preview_path
end
- def show_previews #:nodoc:
+ def show_previews
Base.show_previews
end
- def inform_preview_interceptors(message) #:nodoc:
+ def inform_preview_interceptors(message)
Base.preview_interceptors.each do |interceptor|
interceptor.previewing_email(message)
end
diff --git a/actionmailer/lib/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb
index bebcf4de01..913df8cf93 100644
--- a/actionmailer/lib/action_mailer/railtie.rb
+++ b/actionmailer/lib/action_mailer/railtie.rb
@@ -1,4 +1,4 @@
-require 'active_job/railtie'
+require "active_job/railtie"
require "action_mailer"
require "rails"
require "abstract_controller/railties/routes_helpers"
@@ -16,13 +16,19 @@ module ActionMailer
paths = app.config.paths
options = app.config.action_mailer
+ if app.config.force_ssl
+ options.default_url_options ||= {}
+ options.default_url_options[:protocol] ||= "https"
+ end
+
options.assets_dir ||= paths["public"].first
options.javascripts_dir ||= paths["public/javascripts"].first
options.stylesheets_dir ||= paths["public/stylesheets"].first
options.show_previews = Rails.env.development? if options.show_previews.nil?
+ options.cache_store ||= Rails.cache
if options.show_previews
- options.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/mailers/previews" : nil
+ options.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/mailers/previews" : nil
end
# make sure readers methods get compiled
@@ -38,15 +44,10 @@ module ActionMailer
register_preview_interceptors(options.delete(:preview_interceptors))
register_observers(options.delete(:observers))
- options.each { |k,v| send("#{k}=", v) }
-
- if options.show_previews
- app.routes.prepend do
- get '/rails/mailers' => "rails/mailers#index"
- get '/rails/mailers/*path' => "rails/mailers#preview"
- end
- end
+ options.each { |k, v| send("#{k}=", v) }
end
+
+ ActiveSupport.on_load(:action_dispatch_integration_test) { include ActionMailer::TestCase::ClearTestDeliveries }
end
initializer "action_mailer.compile_config_methods" do
@@ -55,9 +56,18 @@ module ActionMailer
end
end
- config.after_initialize do
- if ActionMailer::Base.preview_path
- ActiveSupport::Dependencies.autoload_paths << ActionMailer::Base.preview_path
+ config.after_initialize do |app|
+ options = app.config.action_mailer
+
+ if options.show_previews
+ app.routes.prepend do
+ get "/rails/mailers" => "rails/mailers#index", internal: true
+ get "/rails/mailers/*path" => "rails/mailers#preview", internal: true
+ end
+
+ if options.preview_path
+ ActiveSupport::Dependencies.autoload_paths << options.preview_path
+ end
end
end
end
diff --git a/actionmailer/lib/action_mailer/rescuable.rb b/actionmailer/lib/action_mailer/rescuable.rb
new file mode 100644
index 0000000000..f2eabfa057
--- /dev/null
+++ b/actionmailer/lib/action_mailer/rescuable.rb
@@ -0,0 +1,27 @@
+module ActionMailer #:nodoc:
+ # Provides `rescue_from` for mailers. Wraps mailer action processing,
+ # mail job processing, and mail delivery.
+ module Rescuable
+ extend ActiveSupport::Concern
+ include ActiveSupport::Rescuable
+
+ class_methods do
+ def handle_exception(exception) #:nodoc:
+ rescue_with_handler(exception) || raise(exception)
+ end
+ end
+
+ def handle_exceptions #:nodoc:
+ yield
+ rescue => exception
+ rescue_with_handler(exception) || raise
+ end
+
+ private
+ def process(*)
+ handle_exceptions do
+ super
+ end
+ end
+ end
+end
diff --git a/actionmailer/lib/action_mailer/test_case.rb b/actionmailer/lib/action_mailer/test_case.rb
index 766215ce96..ebc7b37961 100644
--- a/actionmailer/lib/action_mailer/test_case.rb
+++ b/actionmailer/lib/action_mailer/test_case.rb
@@ -1,5 +1,5 @@
-require 'active_support/test_case'
-require 'rails-dom-testing'
+require "active_support/test_case"
+require "rails-dom-testing"
module ActionMailer
class NonInferrableMailerError < ::StandardError
@@ -11,6 +11,23 @@ module ActionMailer
end
class TestCase < ActiveSupport::TestCase
+ module ClearTestDeliveries
+ extend ActiveSupport::Concern
+
+ included do
+ setup :clear_test_deliveries
+ teardown :clear_test_deliveries
+ end
+
+ private
+
+ def clear_test_deliveries
+ if ActionMailer::Base.delivery_method == :test
+ ActionMailer::Base.deliveries.clear
+ end
+ end
+ end
+
module Behavior
extend ActiveSupport::Concern
@@ -24,6 +41,7 @@ module ActionMailer
setup :initialize_test_deliveries
setup :set_expected_mail
teardown :restore_test_deliveries
+ ActiveSupport.run_load_hooks(:action_mailer_test_case, self)
end
module ClassMethods
@@ -55,18 +73,18 @@ module ActionMailer
end
end
- protected
+ private
def initialize_test_deliveries
set_delivery_method :test
@old_perform_deliveries = ActionMailer::Base.perform_deliveries
ActionMailer::Base.perform_deliveries = true
+ ActionMailer::Base.deliveries.clear
end
def restore_test_deliveries
restore_delivery_method
ActionMailer::Base.perform_deliveries = @old_perform_deliveries
- ActionMailer::Base.deliveries.clear
end
def set_delivery_method(method)
@@ -75,17 +93,16 @@ module ActionMailer
end
def restore_delivery_method
+ ActionMailer::Base.deliveries.clear
ActionMailer::Base.delivery_method = @old_delivery_method
end
def set_expected_mail
@expected = Mail.new
@expected.content_type ["text", "plain", { "charset" => charset }]
- @expected.mime_version = '1.0'
+ @expected.mime_version = "1.0"
end
- private
-
def charset
"UTF-8"
end
@@ -95,7 +112,7 @@ module ActionMailer
end
def read_fixture(action)
- IO.readlines(File.join(Rails.root, 'test', 'fixtures', self.class.mailer_class.name.underscore, action))
+ IO.readlines(File.join(Rails.root, "test", "fixtures", self.class.mailer_class.name.underscore, action))
end
end
diff --git a/actionmailer/lib/action_mailer/test_helper.rb b/actionmailer/lib/action_mailer/test_helper.rb
index 4d03a616d2..c17ecad4c6 100644
--- a/actionmailer/lib/action_mailer/test_helper.rb
+++ b/actionmailer/lib/action_mailer/test_helper.rb
@@ -1,8 +1,8 @@
-require 'active_job'
+require "active_job"
module ActionMailer
# Provides helper methods for testing Action Mailer, including #assert_emails
- # and #assert_no_emails
+ # and #assert_no_emails.
module TestHelper
include ActiveJob::TestHelper
@@ -40,7 +40,7 @@ module ActionMailer
end
end
- # Assert that no emails have been sent.
+ # Asserts that no emails have been sent.
#
# def test_emails
# assert_no_emails
diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb
index 06f80a8fdc..8452d6370e 100644
--- a/actionmailer/lib/action_mailer/version.rb
+++ b/actionmailer/lib/action_mailer/version.rb
@@ -1,4 +1,4 @@
-require_relative 'gem_version'
+require_relative "gem_version"
module ActionMailer
# Returns the version of the currently loaded Action Mailer as a
diff --git a/actionmailer/lib/rails/generators/mailer/mailer_generator.rb b/actionmailer/lib/rails/generators/mailer/mailer_generator.rb
index 3ec7d3d896..4a8d0178de 100644
--- a/actionmailer/lib/rails/generators/mailer/mailer_generator.rb
+++ b/actionmailer/lib/rails/generators/mailer/mailer_generator.rb
@@ -8,17 +8,28 @@ module Rails
check_class_collision suffix: "Mailer"
def create_mailer_file
- template "mailer.rb", File.join('app/mailers', class_path, "#{file_name}_mailer.rb")
- if self.behavior == :invoke
- template "application_mailer.rb", 'app/mailers/application_mailer.rb'
+ template "mailer.rb", File.join("app/mailers", class_path, "#{file_name}_mailer.rb")
+
+ in_root do
+ if self.behavior == :invoke && !File.exist?(application_mailer_file_name)
+ template "application_mailer.rb", application_mailer_file_name
+ end
end
end
hook_for :template_engine, :test_framework
- protected
- def file_name
- @_file_name ||= super.gsub(/\_mailer/i, '')
+ private
+ def file_name # :doc:
+ @_file_name ||= super.gsub(/_mailer/i, "")
+ end
+
+ def application_mailer_file_name
+ @_application_mailer_file_name ||= if mountable_engine?
+ "app/mailers/#{namespaced_path}/application_mailer.rb"
+ else
+ "app/mailers/application_mailer.rb"
+ end
end
end
end
diff --git a/actionmailer/lib/rails/generators/mailer/templates/application_mailer.rb b/actionmailer/lib/rails/generators/mailer/templates/application_mailer.rb
index d25d8892dd..00fb9bd48f 100644
--- a/actionmailer/lib/rails/generators/mailer/templates/application_mailer.rb
+++ b/actionmailer/lib/rails/generators/mailer/templates/application_mailer.rb
@@ -1,4 +1,6 @@
+<% module_namespacing do -%>
class ApplicationMailer < ActionMailer::Base
- default from: "from@example.com"
+ default from: 'from@example.com'
layout 'mailer'
end
+<% end %>
diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb
index 706249a2f6..82f03f5cba 100644
--- a/actionmailer/test/abstract_unit.rb
+++ b/actionmailer/test/abstract_unit.rb
@@ -1,5 +1,4 @@
-require File.expand_path('../../../load_paths', __FILE__)
-require 'active_support/core_ext/kernel/reporting'
+require "active_support/core_ext/kernel/reporting"
# These are the normal settings that will be set up by Railties
# TODO: Have these tests support other combinations of these values
@@ -8,12 +7,19 @@ silence_warnings do
Encoding.default_external = "UTF-8"
end
-require 'active_support/testing/autorun'
-require 'action_mailer'
-require 'action_mailer/test_case'
+module Rails
+ def self.root
+ File.expand_path("../", File.dirname(__FILE__))
+ end
+end
+
+require "active_support/testing/autorun"
+require "active_support/testing/method_call_assertions"
+require "action_mailer"
+require "action_mailer/test_case"
# Emulate AV railtie
-require 'action_view'
+require "action_view"
ActionMailer::Base.include(ActionView::Layouts)
# Show backtraces for deprecated behavior for quicker cleanup.
@@ -22,22 +28,18 @@ ActiveSupport::Deprecation.debug = true
# Disable available locale checks to avoid warnings running the test suite.
I18n.enforce_available_locales = false
-FIXTURE_LOAD_PATH = File.expand_path('fixtures', File.dirname(__FILE__))
+FIXTURE_LOAD_PATH = File.expand_path("fixtures", File.dirname(__FILE__))
ActionMailer::Base.view_paths = FIXTURE_LOAD_PATH
-module Rails
- def self.root
- File.expand_path('../', File.dirname(__FILE__))
- end
-end
-
# Skips the current run on Rubinius using Minitest::Assertions#skip
-def rubinius_skip(message = '')
- skip message if RUBY_ENGINE == 'rbx'
+def rubinius_skip(message = "")
+ skip message if RUBY_ENGINE == "rbx"
end
# Skips the current run on JRuby using Minitest::Assertions#skip
-def jruby_skip(message = '')
+def jruby_skip(message = "")
skip message if defined?(JRUBY_VERSION)
end
-require 'mocha/setup' # FIXME: stop using mocha
+class ActiveSupport::TestCase
+ include ActiveSupport::Testing::MethodCallAssertions
+end
diff --git a/actionmailer/test/assert_select_email_test.rb b/actionmailer/test/assert_select_email_test.rb
index cae2e20abd..bf14fe0853 100644
--- a/actionmailer/test/assert_select_email_test.rb
+++ b/actionmailer/test/assert_select_email_test.rb
@@ -1,4 +1,4 @@
-require 'abstract_unit'
+require "abstract_unit"
class AssertSelectEmailTest < ActionMailer::TestCase
class AssertSelectMailer < ActionMailer::Base
@@ -11,8 +11,8 @@ class AssertSelectEmailTest < ActionMailer::TestCase
class AssertMultipartSelectMailer < ActionMailer::Base
def test(options)
mail subject: "Test e-mail", from: "test@test.host", to: "test <test@test.host>" do |format|
- format.text { render text: options[:text] }
- format.html { render text: options[:html] }
+ format.text { render plain: options[:text] }
+ format.html { render plain: options[:html] }
end
end
end
@@ -36,7 +36,7 @@ class AssertSelectEmailTest < ActionMailer::TestCase
end
def test_assert_select_email_multipart
- AssertMultipartSelectMailer.test(html: "<div><p>foo</p><p>bar</p></div>", text: 'foo bar').deliver_now
+ AssertMultipartSelectMailer.test(html: "<div><p>foo</p><p>bar</p></div>", text: "foo bar").deliver_now
assert_select_email do
assert_select "div:root" do
assert_select "p:first-child", "foo"
diff --git a/actionmailer/test/asset_host_test.rb b/actionmailer/test/asset_host_test.rb
index 10cfdcf693..812df01a34 100644
--- a/actionmailer/test/asset_host_test.rb
+++ b/actionmailer/test/asset_host_test.rb
@@ -1,11 +1,11 @@
-require 'abstract_unit'
-require 'action_controller'
+require "abstract_unit"
+require "action_controller"
class AssetHostMailer < ActionMailer::Base
def email_with_asset
- mail to: 'test@localhost',
- subject: 'testing email containing asset path while asset_host is set',
- from: 'tester@example.com'
+ mail to: "test@localhost",
+ subject: "testing email containing asset path while asset_host is set",
+ from: "tester@example.com"
end
end
@@ -22,16 +22,16 @@ class AssetHostTest < ActionMailer::TestCase
def test_asset_host_as_string
mail = AssetHostMailer.email_with_asset
- assert_dom_equal %Q{<img alt="Somelogo" src="http://www.example.com/images/somelogo.png" />}, mail.body.to_s.strip
+ assert_dom_equal '<img alt="Somelogo" src="http://www.example.com/images/somelogo.png" />', mail.body.to_s.strip
end
def test_asset_host_as_one_argument_proc
AssetHostMailer.config.asset_host = Proc.new { |source|
- if source.starts_with?('/images')
- 'http://images.example.com'
+ if source.starts_with?("/images")
+ "http://images.example.com"
end
}
mail = AssetHostMailer.email_with_asset
- assert_dom_equal %Q{<img alt="Somelogo" src="http://images.example.com/images/somelogo.png" />}, mail.body.to_s.strip
+ assert_dom_equal '<img alt="Somelogo" src="http://images.example.com/images/somelogo.png" />', mail.body.to_s.strip
end
end
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index 59c5638f96..91049e33f9 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -1,12 +1,12 @@
-require 'abstract_unit'
-require 'set'
+require "abstract_unit"
+require "set"
-require 'action_dispatch'
-require 'active_support/time'
+require "action_dispatch"
+require "active_support/time"
-require 'mailers/base_mailer'
-require 'mailers/proc_mailer'
-require 'mailers/asset_mailer'
+require "mailers/base_mailer"
+require "mailers/proc_mailer"
+require "mailers/asset_mailer"
class BaseTest < ActiveSupport::TestCase
include Rails::Dom::Testing::Assertions::DomAssertions
@@ -32,33 +32,33 @@ class BaseTest < ActiveSupport::TestCase
# Basic mail usage without block
test "mail() should set the headers of the mail message" do
email = BaseMailer.welcome
- assert_equal(['system@test.lindsaar.net'], email.to)
- assert_equal(['jose@test.plataformatec.com'], email.from)
- assert_equal('The first email on new API!', email.subject)
+ assert_equal(["system@test.lindsaar.net"], email.to)
+ assert_equal(["jose@test.plataformatec.com"], email.from)
+ assert_equal("The first email on new API!", email.subject)
end
test "mail() with from overwrites the class level default" do
- email = BaseMailer.welcome(from: 'someone@example.com',
- to: 'another@example.org')
- assert_equal(['someone@example.com'], email.from)
- assert_equal(['another@example.org'], email.to)
+ email = BaseMailer.welcome(from: "someone@example.com",
+ to: "another@example.org")
+ assert_equal(["someone@example.com"], email.from)
+ assert_equal(["another@example.org"], email.to)
end
test "mail() with bcc, cc, content_type, charset, mime_version, reply_to and date" do
time = Time.now.beginning_of_day.to_datetime
- email = BaseMailer.welcome(bcc: 'bcc@test.lindsaar.net',
- cc: 'cc@test.lindsaar.net',
- content_type: 'multipart/mixed',
- charset: 'iso-8559-1',
- mime_version: '2.0',
- reply_to: 'reply-to@test.lindsaar.net',
+ email = BaseMailer.welcome(bcc: "bcc@test.lindsaar.net",
+ cc: "cc@test.lindsaar.net",
+ content_type: "multipart/mixed",
+ charset: "iso-8559-1",
+ mime_version: "2.0",
+ reply_to: "reply-to@test.lindsaar.net",
date: time)
- assert_equal(['bcc@test.lindsaar.net'], email.bcc)
- assert_equal(['cc@test.lindsaar.net'], email.cc)
- assert_equal('multipart/mixed; charset=iso-8559-1', email.content_type)
- assert_equal('iso-8559-1', email.charset)
- assert_equal('2.0', email.mime_version)
- assert_equal(['reply-to@test.lindsaar.net'], email.reply_to)
+ assert_equal(["bcc@test.lindsaar.net"], email.bcc)
+ assert_equal(["cc@test.lindsaar.net"], email.cc)
+ assert_equal("multipart/mixed; charset=iso-8559-1", email.content_type)
+ assert_equal("iso-8559-1", email.charset)
+ assert_equal("2.0", email.mime_version)
+ assert_equal(["reply-to@test.lindsaar.net"], email.reply_to)
assert_equal(time, email.date)
end
@@ -75,63 +75,63 @@ class BaseTest < ActiveSupport::TestCase
test "should set template content type if mail has only one part" do
mail = BaseMailer.html_only
- assert_equal('text/html', mail.mime_type)
+ assert_equal("text/html", mail.mime_type)
mail = BaseMailer.plain_text_only
- assert_equal('text/plain', mail.mime_type)
+ assert_equal("text/plain", mail.mime_type)
end
# Custom headers
test "custom headers" do
email = BaseMailer.welcome
- assert_equal("Not SPAM", email['X-SPAM'].decoded)
+ assert_equal("Not SPAM", email["X-SPAM"].decoded)
end
test "can pass random headers in as a hash to mail" do
- hash = {'X-Special-Domain-Specific-Header' => "SecretValue",
- 'In-Reply-To' => '1234@mikel.me.com' }
+ hash = { "X-Special-Domain-Specific-Header" => "SecretValue",
+ "In-Reply-To" => "1234@mikel.me.com" }
mail = BaseMailer.welcome(hash)
- assert_equal('SecretValue', mail['X-Special-Domain-Specific-Header'].decoded)
- assert_equal('1234@mikel.me.com', mail['In-Reply-To'].decoded)
+ assert_equal("SecretValue", mail["X-Special-Domain-Specific-Header"].decoded)
+ assert_equal("1234@mikel.me.com", mail["In-Reply-To"].decoded)
end
test "can pass random headers in as a hash to headers" do
- hash = {'X-Special-Domain-Specific-Header' => "SecretValue",
- 'In-Reply-To' => '1234@mikel.me.com' }
+ hash = { "X-Special-Domain-Specific-Header" => "SecretValue",
+ "In-Reply-To" => "1234@mikel.me.com" }
mail = BaseMailer.welcome_with_headers(hash)
- assert_equal('SecretValue', mail['X-Special-Domain-Specific-Header'].decoded)
- assert_equal('1234@mikel.me.com', mail['In-Reply-To'].decoded)
+ assert_equal("SecretValue", mail["X-Special-Domain-Specific-Header"].decoded)
+ assert_equal("1234@mikel.me.com", mail["In-Reply-To"].decoded)
end
# Attachments
test "attachment with content" do
email = BaseMailer.attachment_with_content
assert_equal(1, email.attachments.length)
- assert_equal('invoice.pdf', email.attachments[0].filename)
- assert_equal('This is test File content', email.attachments['invoice.pdf'].decoded)
+ assert_equal("invoice.pdf", email.attachments[0].filename)
+ assert_equal("This is test File content", email.attachments["invoice.pdf"].decoded)
end
test "attachment gets content type from filename" do
email = BaseMailer.attachment_with_content
- assert_equal('invoice.pdf', email.attachments[0].filename)
- assert_equal('application/pdf', email.attachments[0].mime_type)
+ assert_equal("invoice.pdf", email.attachments[0].filename)
+ assert_equal("application/pdf", email.attachments[0].mime_type)
end
test "attachment with hash" do
email = BaseMailer.attachment_with_hash
assert_equal(1, email.attachments.length)
- assert_equal('invoice.jpg', email.attachments[0].filename)
+ assert_equal("invoice.jpg", email.attachments[0].filename)
expected = "\312\213\254\232)b"
expected.force_encoding(Encoding::BINARY)
- assert_equal expected, email.attachments['invoice.jpg'].decoded
+ assert_equal expected, email.attachments["invoice.jpg"].decoded
end
test "attachment with hash using default mail encoding" do
email = BaseMailer.attachment_with_hash_default_encoding
assert_equal(1, email.attachments.length)
- assert_equal('invoice.jpg', email.attachments[0].filename)
+ assert_equal("invoice.jpg", email.attachments[0].filename)
expected = "\312\213\254\232)b"
expected.force_encoding(Encoding::BINARY)
- assert_equal expected, email.attachments['invoice.jpg'].decoded
+ assert_equal expected, email.attachments["invoice.jpg"].decoded
end
test "sets mime type to multipart/mixed when attachment is included" do
@@ -215,24 +215,24 @@ class BaseTest < ActiveSupport::TestCase
email = BaseMailer.welcome(subject: nil)
assert_equal "Welcome", email.subject
- with_translation 'en', base_mailer: {welcome: {subject: "New Subject!"}} do
+ with_translation "en", base_mailer: { welcome: { subject: "New Subject!" } } do
email = BaseMailer.welcome(subject: nil)
assert_equal "New Subject!", email.subject
end
end
end
- test 'default subject can have interpolations' do
- with_translation 'en', base_mailer: {with_subject_interpolations: {subject: 'Will the real %{rapper_or_impersonator} please stand up?'}} do
+ test "default subject can have interpolations" do
+ with_translation "en", base_mailer: { with_subject_interpolations: { subject: "Will the real %{rapper_or_impersonator} please stand up?" } } do
email = BaseMailer.with_subject_interpolations
- assert_equal 'Will the real Slim Shady please stand up?', email.subject
+ assert_equal "Will the real Slim Shady please stand up?", email.subject
end
end
test "translations are scoped properly" do
- with_translation 'en', base_mailer: {email_with_translations: {greet_user: "Hello %{name}!"}} do
+ with_translation "en", base_mailer: { email_with_translations: { greet_user: "Hello %{name}!" } } do
email = BaseMailer.email_with_translations
- assert_equal 'Hello lifo!', email.body.encoded
+ assert_equal "Hello lifo!", email.body.encoded
end
end
@@ -240,7 +240,7 @@ class BaseTest < ActiveSupport::TestCase
class LateAttachmentMailer < ActionMailer::Base
def welcome
mail body: "yay", from: "welcome@example.com", to: "to@example.com"
- attachments['invoice.pdf'] = 'This is test File content'
+ attachments["invoice.pdf"] = "This is test File content"
end
end
@@ -252,7 +252,7 @@ class BaseTest < ActiveSupport::TestCase
class LateInlineAttachmentMailer < ActionMailer::Base
def welcome
mail body: "yay", from: "welcome@example.com", to: "to@example.com"
- attachments.inline['invoice.pdf'] = 'This is test File content'
+ attachments.inline["invoice.pdf"] = "This is test File content"
end
end
@@ -271,13 +271,13 @@ class BaseTest < ActiveSupport::TestCase
assert_nothing_raised { mail.message }
assert_equal ["image/jpeg; filename=controller_attachments.jpg",
- "image/jpeg; filename=attachments.jpg"], mail.attachments.inline.map {|a| a['Content-Type'].to_s }
+ "image/jpeg; filename=attachments.jpg"], mail.attachments.inline.map { |a| a["Content-Type"].to_s }
end
test "accessing attachments works after mail was called" do
class LateAttachmentAccessorMailer < ActionMailer::Base
def welcome
- attachments['invoice.pdf'] = 'This is test File content'
+ attachments["invoice.pdf"] = "This is test File content"
mail body: "yay", from: "welcome@example.com", to: "to@example.com"
unless attachments.map(&:filename) == ["invoice.pdf"]
@@ -366,7 +366,7 @@ class BaseTest < ActiveSupport::TestCase
I18n.backend = fallback_backend.new
I18n.fallbacks[:"de-AT"] = [:de]
- swap I18n, locale: 'de-AT' do
+ swap I18n, locale: "de-AT" do
email = BaseMailer.implicit_with_locale
assert_equal(2, email.parts.size)
assert_equal("multipart/alternative", email.mime_type)
@@ -380,7 +380,6 @@ class BaseTest < ActiveSupport::TestCase
end
end
-
test "implicit multipart with several view paths uses the first one with template" do
old = BaseMailer.view_paths
begin
@@ -449,6 +448,13 @@ class BaseTest < ActiveSupport::TestCase
assert_equal("Format with any!", email.parts[1].body.encoded)
end
+ test "explicit without specifying format with format.any" do
+ error = assert_raises(ArgumentError) do
+ BaseMailer.explicit_without_specifying_format_with_any.parts
+ end
+ assert_equal "You have to supply at least one format", error.message
+ end
+
test "explicit multipart with format(Hash)" do
email = BaseMailer.explicit_multipart_with_options(true)
email.ready_to_send!
@@ -501,13 +507,14 @@ class BaseTest < ActiveSupport::TestCase
test "calling just the action should return the generated mail object" do
email = BaseMailer.welcome
assert_equal(0, BaseMailer.deliveries.length)
- assert_equal('The first email on new API!', email.subject)
+ assert_equal("The first email on new API!", email.subject)
end
test "calling deliver on the action should deliver the mail object" do
- BaseMailer.expects(:deliver_mail).once
- mail = BaseMailer.welcome.deliver_now
- assert_equal 'The first email on new API!', mail.subject
+ assert_called(BaseMailer, :deliver_mail) do
+ mail = BaseMailer.welcome.deliver_now
+ assert_equal "The first email on new API!", mail.subject
+ end
end
test "calling deliver on the action should increment the deliveries collection if using the test mailer" do
@@ -517,42 +524,44 @@ class BaseTest < ActiveSupport::TestCase
test "calling deliver, ActionMailer should yield back to mail to let it call :do_delivery on itself" do
mail = Mail::Message.new
- mail.expects(:do_delivery).once
- BaseMailer.expects(:welcome).returns(mail)
- BaseMailer.welcome.deliver
+ assert_called(mail, :do_delivery) do
+ assert_called(BaseMailer, :welcome, returns: mail) do
+ BaseMailer.welcome.deliver
+ end
+ end
end
# Rendering
test "you can specify a different template for implicit render" do
- mail = BaseMailer.implicit_different_template('implicit_multipart').deliver_now
+ mail = BaseMailer.implicit_different_template("implicit_multipart").deliver_now
assert_equal("HTML Implicit Multipart", mail.html_part.body.decoded)
assert_equal("TEXT Implicit Multipart", mail.text_part.body.decoded)
end
test "should raise if missing template in implicit render" do
assert_raises ActionView::MissingTemplate do
- BaseMailer.implicit_different_template('missing_template').deliver_now
+ BaseMailer.implicit_different_template("missing_template").deliver_now
end
assert_equal(0, BaseMailer.deliveries.length)
end
test "you can specify a different template for explicit render" do
- mail = BaseMailer.explicit_different_template('explicit_multipart_templates').deliver_now
+ mail = BaseMailer.explicit_different_template("explicit_multipart_templates").deliver_now
assert_equal("HTML Explicit Multipart Templates", mail.html_part.body.decoded)
assert_equal("TEXT Explicit Multipart Templates", mail.text_part.body.decoded)
end
test "you can specify a different layout" do
- mail = BaseMailer.different_layout('different_layout').deliver_now
+ mail = BaseMailer.different_layout("different_layout").deliver_now
assert_equal("HTML -- HTML", mail.html_part.body.decoded)
assert_equal("PLAIN -- PLAIN", mail.text_part.body.decoded)
end
test "you can specify the template path for implicit lookup" do
- mail = BaseMailer.welcome_from_another_path('another.path/base_mailer').deliver_now
+ mail = BaseMailer.welcome_from_another_path("another.path/base_mailer").deliver_now
assert_equal("Welcome from another path", mail.body.encoded)
- mail = BaseMailer.welcome_from_another_path(['unknown/invalid', 'another.path/base_mailer']).deliver_now
+ mail = BaseMailer.welcome_from_another_path(["unknown/invalid", "another.path/base_mailer"]).deliver_now
assert_equal("Welcome from another path", mail.body.encoded)
end
@@ -579,15 +588,15 @@ class BaseTest < ActiveSupport::TestCase
assert_dom_equal(%{<img alt="Dummy" src="http://local.com/images/dummy.png" />}, mail.body.to_s.strip)
end
- test 'the view is not rendered when mail was never called' do
+ test "the view is not rendered when mail was never called" do
mail = BaseMailer.without_mail_call
- assert_equal('', mail.body.to_s.strip)
+ assert_equal("", mail.body.to_s.strip)
mail.deliver_now
end
- test 'the return value of mailer methods is not relevant' do
+ test "the return value of mailer methods is not relevant" do
mail = BaseMailer.with_nil_as_return_value
- assert_equal('Welcome', mail.body.to_s.strip)
+ assert_equal("Welcome", mail.body.to_s.strip)
mail.deliver_now
end
@@ -607,8 +616,9 @@ class BaseTest < ActiveSupport::TestCase
mail_side_effects do
ActionMailer::Base.register_observer(MyObserver)
mail = BaseMailer.welcome
- MyObserver.expects(:delivered_email).with(mail)
- mail.deliver_now
+ assert_called_with(MyObserver, :delivered_email, [mail]) do
+ mail.deliver_now
+ end
end
end
@@ -616,8 +626,9 @@ class BaseTest < ActiveSupport::TestCase
mail_side_effects do
ActionMailer::Base.register_observer("BaseTest::MyObserver")
mail = BaseMailer.welcome
- MyObserver.expects(:delivered_email).with(mail)
- mail.deliver_now
+ assert_called_with(MyObserver, :delivered_email, [mail]) do
+ mail.deliver_now
+ end
end
end
@@ -625,8 +636,9 @@ class BaseTest < ActiveSupport::TestCase
mail_side_effects do
ActionMailer::Base.register_observer(:"base_test/my_observer")
mail = BaseMailer.welcome
- MyObserver.expects(:delivered_email).with(mail)
- mail.deliver_now
+ assert_called_with(MyObserver, :delivered_email, [mail]) do
+ mail.deliver_now
+ end
end
end
@@ -634,9 +646,11 @@ class BaseTest < ActiveSupport::TestCase
mail_side_effects do
ActionMailer::Base.register_observers("BaseTest::MyObserver", MySecondObserver)
mail = BaseMailer.welcome
- MyObserver.expects(:delivered_email).with(mail)
- MySecondObserver.expects(:delivered_email).with(mail)
- mail.deliver_now
+ assert_called_with(MyObserver, :delivered_email, [mail]) do
+ assert_called_with(MySecondObserver, :delivered_email, [mail]) do
+ mail.deliver_now
+ end
+ end
end
end
@@ -654,8 +668,9 @@ class BaseTest < ActiveSupport::TestCase
mail_side_effects do
ActionMailer::Base.register_interceptor(MyInterceptor)
mail = BaseMailer.welcome
- MyInterceptor.expects(:delivering_email).with(mail)
- mail.deliver_now
+ assert_called_with(MyInterceptor, :delivering_email, [mail]) do
+ mail.deliver_now
+ end
end
end
@@ -663,8 +678,9 @@ class BaseTest < ActiveSupport::TestCase
mail_side_effects do
ActionMailer::Base.register_interceptor("BaseTest::MyInterceptor")
mail = BaseMailer.welcome
- MyInterceptor.expects(:delivering_email).with(mail)
- mail.deliver_now
+ assert_called_with(MyInterceptor, :delivering_email, [mail]) do
+ mail.deliver_now
+ end
end
end
@@ -672,8 +688,9 @@ class BaseTest < ActiveSupport::TestCase
mail_side_effects do
ActionMailer::Base.register_interceptor(:"base_test/my_interceptor")
mail = BaseMailer.welcome
- MyInterceptor.expects(:delivering_email).with(mail)
- mail.deliver_now
+ assert_called_with(MyInterceptor, :delivering_email, [mail]) do
+ mail.deliver_now
+ end
end
end
@@ -681,22 +698,25 @@ class BaseTest < ActiveSupport::TestCase
mail_side_effects do
ActionMailer::Base.register_interceptors("BaseTest::MyInterceptor", MySecondInterceptor)
mail = BaseMailer.welcome
- MyInterceptor.expects(:delivering_email).with(mail)
- MySecondInterceptor.expects(:delivering_email).with(mail)
- mail.deliver_now
+ assert_called_with(MyInterceptor, :delivering_email, [mail]) do
+ assert_called_with(MySecondInterceptor, :delivering_email, [mail]) do
+ mail.deliver_now
+ end
+ end
end
end
test "being able to put proc's into the defaults hash and they get evaluated on mail sending" do
- mail1 = ProcMailer.welcome['X-Proc-Method']
+ mail1 = ProcMailer.welcome["X-Proc-Method"]
yesterday = 1.day.ago
- Time.stubs(:now).returns(yesterday)
- mail2 = ProcMailer.welcome['X-Proc-Method']
- assert(mail1.to_s.to_i > mail2.to_s.to_i)
+ Time.stub(:now, yesterday) do
+ mail2 = ProcMailer.welcome["X-Proc-Method"]
+ assert(mail1.to_s.to_i > mail2.to_s.to_i)
+ end
end
- test 'default values which have to_proc (e.g. symbols) should not be considered procs' do
- assert(ProcMailer.welcome['x-has-to-proc'].to_s == 'symbol')
+ test "default values which have to_proc (e.g. symbols) should not be considered procs" do
+ assert(ProcMailer.welcome["x-has-to-proc"].to_s == "symbol")
end
test "we can call other defined methods on the class as needed" do
@@ -711,12 +731,12 @@ class BaseTest < ActiveSupport::TestCase
def welcome ; mail ; end
private
- def add_special_header!
- headers('X-Special-Header' => 'Wow, so special')
- end
+ def add_special_header!
+ headers("X-Special-Header" => "Wow, so special")
+ end
end
- assert_equal('Wow, so special', BeforeActionMailer.welcome['X-Special-Header'].to_s)
+ assert_equal("Wow, so special", BeforeActionMailer.welcome["X-Special-Header"].to_s)
end
test "modifying the mail message with an after_action" do
@@ -726,12 +746,12 @@ class BaseTest < ActiveSupport::TestCase
def welcome ; mail ; end
private
- def add_special_header!
- headers('X-Special-Header' => 'Testing')
- end
+ def add_special_header!
+ headers("X-Special-Header" => "Testing")
+ end
end
- assert_equal('Testing', AfterActionMailer.welcome['X-Special-Header'].to_s)
+ assert_equal("Testing", AfterActionMailer.welcome["X-Special-Header"].to_s)
end
test "adding an inline attachment using a before_action" do
@@ -741,19 +761,19 @@ class BaseTest < ActiveSupport::TestCase
def welcome ; mail ; end
private
- def add_inline_attachment!
- attachments.inline["footer.jpg"] = 'hey there'
- end
+ def add_inline_attachment!
+ attachments.inline["footer.jpg"] = "hey there"
+ end
end
mail = DefaultInlineAttachmentMailer.welcome
- assert_equal('image/jpeg; filename=footer.jpg', mail.attachments.inline.first['Content-Type'].to_s)
+ assert_equal("image/jpeg; filename=footer.jpg", mail.attachments.inline.first["Content-Type"].to_s)
end
test "action methods should be refreshed after defining new method" do
class FooMailer < ActionMailer::Base
- # this triggers action_methods
- self.respond_to?(:foo)
+ # This triggers action_methods.
+ respond_to?(:foo)
def notify
end
@@ -777,8 +797,8 @@ class BaseTest < ActiveSupport::TestCase
test "default_from can be set" do
class DefaultFromMailer < ActionMailer::Base
- default to: 'system@test.lindsaar.net'
- self.default_options = {from: "robert.pankowecki@gmail.com"}
+ default to: "system@test.lindsaar.net"
+ self.default_options = { from: "robert.pankowecki@gmail.com" }
def welcome
mail(subject: "subject", body: "hello world")
@@ -793,7 +813,7 @@ class BaseTest < ActiveSupport::TestCase
after_action :a_callback
def welcome
- headers('X-Special-Header' => 'special indeed!')
+ headers("X-Special-Header" => "special indeed!")
mail subject: "subject", body: "hello world", to: ["joe@example.com"]
end
@@ -809,7 +829,7 @@ class BaseTest < ActiveSupport::TestCase
assert_equal "special indeed!", mail["X-Special-Header"].to_s
end
- protected
+ private
# Execute the block setting the given values and restoring old values after
# the block is executed.
@@ -877,33 +897,50 @@ class BasePreviewInterceptorsTest < ActiveSupport::TestCase
test "you can register 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
- BaseMailerPreview.any_instance.stubs(:welcome).returns(mail)
- MyInterceptor.expects(:previewing_email).with(mail)
- BaseMailerPreview.call(:welcome)
+ stub_any_instance(BaseMailerPreview) do |instance|
+ instance.stub(:welcome, mail) do
+ assert_called_with(MyInterceptor, :previewing_email, [mail]) do
+ BaseMailerPreview.call(:welcome)
+ end
+ end
+ 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
ActionMailer::Base.register_preview_interceptor("BasePreviewInterceptorsTest::MyInterceptor")
mail = BaseMailer.welcome
- BaseMailerPreview.any_instance.stubs(:welcome).returns(mail)
- MyInterceptor.expects(:previewing_email).with(mail)
- BaseMailerPreview.call(:welcome)
+ stub_any_instance(BaseMailerPreview) do |instance|
+ instance.stub(:welcome, mail) do
+ assert_called_with(MyInterceptor, :previewing_email, [mail]) do
+ BaseMailerPreview.call(:welcome)
+ end
+ 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 previewing" do
ActionMailer::Base.register_preview_interceptor(:"base_preview_interceptors_test/my_interceptor")
mail = BaseMailer.welcome
- BaseMailerPreview.any_instance.stubs(:welcome).returns(mail)
- MyInterceptor.expects(:previewing_email).with(mail)
- BaseMailerPreview.call(:welcome)
+ stub_any_instance(BaseMailerPreview) do |instance|
+ instance.stub(:welcome, mail) do
+ assert_called_with(MyInterceptor, :previewing_email, [mail]) do
+ BaseMailerPreview.call(:welcome)
+ end
+ end
+ end
end
test "you can register 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
- BaseMailerPreview.any_instance.stubs(:welcome).returns(mail)
- MyInterceptor.expects(:previewing_email).with(mail)
- MySecondInterceptor.expects(:previewing_email).with(mail)
- BaseMailerPreview.call(:welcome)
+ stub_any_instance(BaseMailerPreview) do |instance|
+ instance.stub(:welcome, mail) do
+ assert_called_with(MyInterceptor, :previewing_email, [mail]) do
+ assert_called_with(MySecondInterceptor, :previewing_email, [mail]) do
+ BaseMailerPreview.call(:welcome)
+ end
+ end
+ end
+ end
end
end
diff --git a/actionmailer/test/caching_test.rb b/actionmailer/test/caching_test.rb
new file mode 100644
index 0000000000..cff49c8894
--- /dev/null
+++ b/actionmailer/test/caching_test.rb
@@ -0,0 +1,273 @@
+require "fileutils"
+require "abstract_unit"
+require "mailers/base_mailer"
+require "mailers/caching_mailer"
+
+CACHE_DIR = "test_cache"
+# Don't change '/../temp/' cavalierly or you might hose something you don't want hosed
+FILE_STORE_PATH = File.join(File.dirname(__FILE__), "/../temp/", CACHE_DIR)
+
+class FragmentCachingMailer < ActionMailer::Base
+ abstract!
+
+ def some_action; end
+end
+
+class BaseCachingTest < ActiveSupport::TestCase
+ def setup
+ super
+ @store = ActiveSupport::Cache::MemoryStore.new
+ @mailer = FragmentCachingMailer.new
+ @mailer.perform_caching = true
+ @mailer.cache_store = @store
+ end
+
+ def test_fragment_cache_key
+ assert_equal "views/what a key", @mailer.fragment_cache_key("what a key")
+ end
+end
+
+class FragmentCachingTest < BaseCachingTest
+ def test_read_fragment_with_caching_enabled
+ @store.write("views/name", "value")
+ assert_equal "value", @mailer.read_fragment("name")
+ end
+
+ def test_read_fragment_with_caching_disabled
+ @mailer.perform_caching = false
+ @store.write("views/name", "value")
+ assert_nil @mailer.read_fragment("name")
+ end
+
+ def test_fragment_exist_with_caching_enabled
+ @store.write("views/name", "value")
+ assert @mailer.fragment_exist?("name")
+ assert !@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")
+ end
+
+ def test_write_fragment_with_caching_enabled
+ assert_nil @store.read("views/name")
+ assert_equal "value", @mailer.write_fragment("name", "value")
+ assert_equal "value", @store.read("views/name")
+ end
+
+ def test_write_fragment_with_caching_disabled
+ assert_nil @store.read("views/name")
+ @mailer.perform_caching = false
+ assert_equal "value", @mailer.write_fragment("name", "value")
+ assert_nil @store.read("views/name")
+ end
+
+ def test_expire_fragment_with_simple_key
+ @store.write("views/name", "value")
+ @mailer.expire_fragment "name"
+ assert_nil @store.read("views/name")
+ end
+
+ def test_expire_fragment_with_regexp
+ @store.write("views/name", "value")
+ @store.write("views/another_name", "another_value")
+ @store.write("views/primalgrasp", "will not expire ;-)")
+
+ @mailer.expire_fragment(/name/)
+
+ assert_nil @store.read("views/name")
+ assert_nil @store.read("views/another_name")
+ assert_equal "will not expire ;-)", @store.read("views/primalgrasp")
+ end
+
+ def test_fragment_for
+ @store.write("views/expensive", "fragment content")
+ fragment_computed = false
+
+ view_context = @mailer.view_context
+
+ buffer = "generated till now -> ".html_safe
+ buffer << view_context.send(:fragment_for, "expensive") { fragment_computed = true }
+
+ assert !fragment_computed
+ assert_equal "generated till now -> fragment content", buffer
+ end
+
+ def test_html_safety
+ assert_nil @store.read("views/name")
+ content = "value".html_safe
+ assert_equal content, @mailer.write_fragment("name", content)
+
+ cached = @store.read("views/name")
+ assert_equal content, cached
+ assert_equal String, cached.class
+
+ html_safe = @mailer.read_fragment("name")
+ assert_equal content, html_safe
+ assert html_safe.html_safe?
+ end
+end
+
+class FunctionalFragmentCachingTest < BaseCachingTest
+ def setup
+ super
+ @store = ActiveSupport::Cache::MemoryStore.new
+ @mailer = CachingMailer.new
+ @mailer.perform_caching = true
+ @mailer.cache_store = @store
+ end
+
+ def test_fragment_caching
+ email = @mailer.fragment_cache
+ expected_body = "\"Welcome\""
+
+ assert_match expected_body, email.body.encoded
+ assert_match expected_body,
+ @store.read("views/caching/#{template_digest("caching_mailer/fragment_cache")}")
+ end
+
+ def test_fragment_caching_in_partials
+ email = @mailer.fragment_cache_in_partials
+ expected_body = "Old fragment caching in a partial"
+ assert_match(expected_body, email.body.encoded)
+
+ assert_match(expected_body,
+ @store.read("views/caching/#{template_digest("caching_mailer/_partial")}"))
+ end
+
+ def test_skip_fragment_cache_digesting
+ email = @mailer.skip_fragment_cache_digesting
+ expected_body = "No Digest"
+
+ assert_match expected_body, email.body.encoded
+ assert_match expected_body, @store.read("views/no_digest")
+ end
+
+ def test_fragment_caching_options
+ time = Time.now
+ email = @mailer.fragment_caching_options
+ expected_body = "No Digest"
+
+ assert_match expected_body, email.body.encoded
+ Time.stub(:now, time + 11) do
+ assert_nil @store.read("views/no_digest")
+ end
+ end
+
+ def test_multipart_fragment_caching
+ email = @mailer.multipart_cache
+
+ expected_text_body = "\"Welcome text\""
+ expected_html_body = "\"Welcome html\""
+ encoded_body = email.body.encoded
+ assert_match expected_text_body, encoded_body
+ assert_match expected_html_body, encoded_body
+ assert_match expected_text_body,
+ @store.read("views/text_caching")
+ assert_match expected_html_body,
+ @store.read("views/html_caching")
+ end
+
+ def test_fragment_cache_instrumentation
+ @mailer.enable_fragment_cache_logging = true
+ payload = nil
+
+ subscriber = proc do |*args|
+ event = ActiveSupport::Notifications::Event.new(*args)
+ payload = event.payload
+ end
+
+ ActiveSupport::Notifications.subscribed(subscriber, "read_fragment.action_mailer") do
+ @mailer.fragment_cache
+ end
+
+ assert_equal "caching_mailer", payload[:mailer]
+ assert_equal "views/caching/#{template_digest("caching_mailer/fragment_cache")}", payload[:key]
+ ensure
+ @mailer.enable_fragment_cache_logging = true
+ end
+
+ private
+
+ def template_digest(name)
+ ActionView::Digestor.digest(name: name, finder: @mailer.lookup_context)
+ end
+end
+
+class CacheHelperOutputBufferTest < BaseCachingTest
+ class MockController
+ def read_fragment(name, options)
+ return false
+ end
+
+ def write_fragment(name, fragment, options)
+ fragment
+ end
+ end
+
+ def setup
+ super
+ end
+
+ def test_output_buffer
+ output_buffer = ActionView::OutputBuffer.new
+ controller = MockController.new
+ cache_helper = Class.new do
+ def self.controller; end;
+ def self.output_buffer; end;
+ def self.output_buffer=; end;
+ end
+ cache_helper.extend(ActionView::Helpers::CacheHelper)
+
+ cache_helper.stub :controller, controller do
+ cache_helper.stub :output_buffer, output_buffer do
+ assert_called_with cache_helper, :output_buffer=, [output_buffer.class.new(output_buffer)] do
+ assert_nothing_raised do
+ cache_helper.send :fragment_for, "Test fragment name", "Test fragment", &Proc.new { nil }
+ end
+ end
+ end
+ end
+ end
+
+ def test_safe_buffer
+ output_buffer = ActiveSupport::SafeBuffer.new
+ controller = MockController.new
+ cache_helper = Class.new do
+ def self.controller; end;
+ def self.output_buffer; end;
+ def self.output_buffer=; end;
+ end
+ cache_helper.extend(ActionView::Helpers::CacheHelper)
+
+ cache_helper.stub :controller, controller do
+ cache_helper.stub :output_buffer, output_buffer do
+ assert_called_with cache_helper, :output_buffer=, [output_buffer.class.new(output_buffer)] do
+ assert_nothing_raised do
+ cache_helper.send :fragment_for, "Test fragment name", "Test fragment", &Proc.new { nil }
+ end
+ end
+ end
+ end
+ end
+end
+
+class ViewCacheDependencyTest < BaseCachingTest
+ class NoDependenciesMailer < ActionMailer::Base
+ end
+ class HasDependenciesMailer < ActionMailer::Base
+ view_cache_dependency { "trombone" }
+ view_cache_dependency { "flute" }
+ end
+
+ def test_view_cache_dependencies_are_empty_by_default
+ assert NoDependenciesMailer.new.view_cache_dependencies.empty?
+ end
+
+ def test_view_cache_dependencies_are_listed_in_declaration_order
+ assert_equal %w(trombone flute), HasDependenciesMailer.new.view_cache_dependencies
+ end
+end
diff --git a/actionmailer/test/delivery_methods_test.rb b/actionmailer/test/delivery_methods_test.rb
index 78507ce7dc..f64a69019f 100644
--- a/actionmailer/test/delivery_methods_test.rb
+++ b/actionmailer/test/delivery_methods_test.rb
@@ -1,4 +1,4 @@
-require 'abstract_unit'
+require "abstract_unit"
class MyCustomDelivery
end
@@ -23,7 +23,7 @@ class DefaultsDeliveryMethodsTest < ActiveSupport::TestCase
test "default smtp settings" do
settings = { address: "localhost",
port: 25,
- domain: 'localhost.localdomain',
+ domain: "localhost.localdomain",
user_name: nil,
password: nil,
authentication: nil,
@@ -31,15 +31,15 @@ class DefaultsDeliveryMethodsTest < ActiveSupport::TestCase
assert_equal settings, ActionMailer::Base.smtp_settings
end
- test "default file delivery settings" do
- settings = {location: "#{Dir.tmpdir}/mails"}
+ test "default file delivery settings (with Rails.root)" do
+ settings = { location: "#{Rails.root}/tmp/mails" }
assert_equal settings, ActionMailer::Base.file_settings
end
test "default sendmail settings" do
settings = {
- location: '/usr/sbin/sendmail',
- arguments: '-i -t'
+ location: "/usr/sbin/sendmail",
+ arguments: "-i"
}
assert_equal settings, ActionMailer::Base.sendmail_settings
end
@@ -83,11 +83,11 @@ end
class MailDeliveryTest < ActiveSupport::TestCase
class DeliveryMailer < ActionMailer::Base
DEFAULT_HEADERS = {
- to: 'mikel@test.lindsaar.net',
- from: 'jose@test.plataformatec.com'
+ to: "mikel@test.lindsaar.net",
+ from: "jose@test.plataformatec.com"
}
- def welcome(hash={})
+ def welcome(hash = {})
mail(DEFAULT_HEADERS.merge(hash))
end
end
@@ -102,16 +102,21 @@ class MailDeliveryTest < ActiveSupport::TestCase
end
test "ActionMailer should be told when Mail gets delivered" do
- DeliveryMailer.expects(:deliver_mail).once
- DeliveryMailer.welcome.deliver_now
+ DeliveryMailer.delivery_method = :test
+ assert_called(DeliveryMailer, :deliver_mail) do
+ DeliveryMailer.welcome.deliver_now
+ end
end
test "delivery method can be customized per instance" do
- Mail::SMTP.any_instance.expects(:deliver!)
- email = DeliveryMailer.welcome.deliver_now
- assert_instance_of Mail::SMTP, email.delivery_method
- email = DeliveryMailer.welcome(delivery_method: :test).deliver_now
- assert_instance_of Mail::TestMailer, email.delivery_method
+ stub_any_instance(Mail::SMTP, instance: Mail::SMTP.new({})) do |instance|
+ assert_called(instance, :deliver!) do
+ email = DeliveryMailer.welcome.deliver_now
+ assert_instance_of Mail::SMTP, email.delivery_method
+ email = DeliveryMailer.welcome(delivery_method: :test).deliver_now
+ assert_instance_of Mail::TestMailer, email.delivery_method
+ end
+ end
end
test "delivery method can be customized in subclasses not changing the parent" do
@@ -122,16 +127,16 @@ class MailDeliveryTest < ActiveSupport::TestCase
end
test "delivery method options default to class level options" do
- default_options = {a: "b"}
+ default_options = { a: "b" }
ActionMailer::Base.add_delivery_method :optioned, MyOptionedDelivery, default_options
mail_instance = DeliveryMailer.welcome(delivery_method: :optioned)
assert_equal default_options, mail_instance.delivery_method.options
end
test "delivery method options can be overridden per mail instance" do
- default_options = {a: "b"}
+ default_options = { a: "b" }
ActionMailer::Base.add_delivery_method :optioned, MyOptionedDelivery, default_options
- overridden_options = {a: "a"}
+ overridden_options = { a: "a" }
mail_instance = DeliveryMailer.welcome(delivery_method: :optioned, delivery_method_options: overridden_options)
assert_equal overridden_options, mail_instance.delivery_method.options
end
@@ -140,14 +145,14 @@ class MailDeliveryTest < ActiveSupport::TestCase
settings = {
address: "localhost",
port: 25,
- domain: 'localhost.localdomain',
+ domain: "localhost.localdomain",
user_name: nil,
password: nil,
authentication: nil,
enable_starttls_auto: true
}
assert_equal settings, ActionMailer::Base.smtp_settings
- overridden_options = {user_name: "overridden", password: "somethingobtuse"}
+ overridden_options = { user_name: "overridden", password: "somethingobtuse" }
mail_instance = DeliveryMailer.welcome(delivery_method_options: overridden_options)
delivery_method_instance = mail_instance.delivery_method
assert_equal "overridden", delivery_method_instance.settings[:user_name]
@@ -160,24 +165,29 @@ class MailDeliveryTest < ActiveSupport::TestCase
test "non registered delivery methods raises errors" do
DeliveryMailer.delivery_method = :unknown
- assert_raise RuntimeError do
+ error = assert_raise RuntimeError do
DeliveryMailer.welcome.deliver_now
end
+ assert_equal "Invalid delivery method :unknown", error.message
end
test "undefined delivery methods raises errors" do
DeliveryMailer.delivery_method = nil
- assert_raise RuntimeError do
+ error = assert_raise RuntimeError do
DeliveryMailer.welcome.deliver_now
end
+ assert_equal "Delivery method cannot be nil", error.message
end
test "does not perform deliveries if requested" do
old_perform_deliveries = DeliveryMailer.perform_deliveries
begin
DeliveryMailer.perform_deliveries = false
- Mail::Message.any_instance.expects(:deliver!).never
- DeliveryMailer.welcome.deliver_now
+ stub_any_instance(Mail::Message) do |instance|
+ assert_not_called(instance, :deliver!) do
+ DeliveryMailer.welcome.deliver_now
+ end
+ end
ensure
DeliveryMailer.perform_deliveries = old_perform_deliveries
end
diff --git a/actionmailer/test/fixtures/async_mailer/welcome.erb b/actionmailer/test/fixtures/async_mailer/welcome.erb
deleted file mode 100644
index 01f3f00c63..0000000000
--- a/actionmailer/test/fixtures/async_mailer/welcome.erb
+++ /dev/null
@@ -1 +0,0 @@
-Welcome \ No newline at end of file
diff --git a/actionmailer/test/fixtures/caching_mailer/_partial.html.erb b/actionmailer/test/fixtures/caching_mailer/_partial.html.erb
new file mode 100644
index 0000000000..8e965f52b4
--- /dev/null
+++ b/actionmailer/test/fixtures/caching_mailer/_partial.html.erb
@@ -0,0 +1,3 @@
+<% cache :caching do %>
+ Old fragment caching in a partial
+<% end %>
diff --git a/actionmailer/test/fixtures/caching_mailer/fragment_cache.html.erb b/actionmailer/test/fixtures/caching_mailer/fragment_cache.html.erb
new file mode 100644
index 0000000000..90189627da
--- /dev/null
+++ b/actionmailer/test/fixtures/caching_mailer/fragment_cache.html.erb
@@ -0,0 +1,3 @@
+<% cache :caching do %>
+"Welcome"
+<% end %>
diff --git a/actionmailer/test/fixtures/caching_mailer/fragment_cache_in_partials.html.erb b/actionmailer/test/fixtures/caching_mailer/fragment_cache_in_partials.html.erb
new file mode 100644
index 0000000000..2957d083e8
--- /dev/null
+++ b/actionmailer/test/fixtures/caching_mailer/fragment_cache_in_partials.html.erb
@@ -0,0 +1 @@
+<%= render "partial" %>
diff --git a/actionmailer/test/fixtures/caching_mailer/fragment_caching_options.html.erb b/actionmailer/test/fixtures/caching_mailer/fragment_caching_options.html.erb
new file mode 100644
index 0000000000..0541ac321b
--- /dev/null
+++ b/actionmailer/test/fixtures/caching_mailer/fragment_caching_options.html.erb
@@ -0,0 +1,3 @@
+<%= cache :no_digest, skip_digest: true, expires_in: 0 do %>
+ No Digest
+<% end %>
diff --git a/actionmailer/test/fixtures/caching_mailer/multipart_cache.html.erb b/actionmailer/test/fixtures/caching_mailer/multipart_cache.html.erb
new file mode 100644
index 0000000000..0d26baa2d7
--- /dev/null
+++ b/actionmailer/test/fixtures/caching_mailer/multipart_cache.html.erb
@@ -0,0 +1,3 @@
+<% cache :html_caching, skip_digest: true do %>
+ "Welcome html"
+<% end %>
diff --git a/actionmailer/test/fixtures/caching_mailer/multipart_cache.text.erb b/actionmailer/test/fixtures/caching_mailer/multipart_cache.text.erb
new file mode 100644
index 0000000000..ef97326e03
--- /dev/null
+++ b/actionmailer/test/fixtures/caching_mailer/multipart_cache.text.erb
@@ -0,0 +1,3 @@
+<% cache :text_caching, skip_digest: true do %>
+ "Welcome text"
+<% end %>
diff --git a/actionmailer/test/fixtures/caching_mailer/skip_fragment_cache_digesting.html.erb b/actionmailer/test/fixtures/caching_mailer/skip_fragment_cache_digesting.html.erb
new file mode 100644
index 0000000000..0d52429a81
--- /dev/null
+++ b/actionmailer/test/fixtures/caching_mailer/skip_fragment_cache_digesting.html.erb
@@ -0,0 +1,3 @@
+<%= cache :no_digest, skip_digest: true do %>
+ No Digest
+<% end %>
diff --git a/actionmailer/test/i18n_with_controller_test.rb b/actionmailer/test/i18n_with_controller_test.rb
index 010e44d045..a87a9c9861 100644
--- a/actionmailer/test/i18n_with_controller_test.rb
+++ b/actionmailer/test/i18n_with_controller_test.rb
@@ -1,10 +1,10 @@
-require 'abstract_unit'
-require 'action_view'
-require 'action_controller'
+require "abstract_unit"
+require "action_view"
+require "action_controller"
class I18nTestMailer < ActionMailer::Base
configure do |c|
- c.assets_dir = ''
+ c.assets_dir = ""
end
def mail_with_i18n_subject(recipient)
@@ -18,14 +18,16 @@ end
class TestController < ActionController::Base
def send_mail
email = I18nTestMailer.mail_with_i18n_subject("test@localhost").deliver_now
- render text: "Mail sent - Subject: #{email.subject}"
+ render plain: "Mail sent - Subject: #{email.subject}"
end
end
class ActionMailerI18nWithControllerTest < ActionDispatch::IntegrationTest
Routes = ActionDispatch::Routing::RouteSet.new
Routes.draw do
- get ':controller(/:action(/:id))'
+ ActiveSupport::Deprecation.silence do
+ get ":controller(/:action(/:id))"
+ end
end
class RoutedRackApp
@@ -52,19 +54,24 @@ class ActionMailerI18nWithControllerTest < ActionDispatch::IntegrationTest
end
def test_send_mail
- Mail::SMTP.any_instance.expects(:deliver!)
- with_translation 'de', email_subject: '[Anmeldung] Willkommen' do
- get '/test/send_mail'
- assert_equal "Mail sent - Subject: [Anmeldung] Willkommen", @response.body
+ stub_any_instance(Mail::SMTP, instance: Mail::SMTP.new({})) do |instance|
+ assert_called(instance, :deliver!) do
+ with_translation "de", email_subject: "[Anmeldung] Willkommen" do
+ ActiveSupport::Deprecation.silence do
+ get "/test/send_mail"
+ end
+ assert_equal "Mail sent - Subject: [Anmeldung] Willkommen", @response.body
+ end
+ end
end
end
- protected
+ private
- def with_translation(locale, data)
- I18n.backend.store_translations(locale, data)
- yield
- ensure
- I18n.backend.reload!
- end
+ def with_translation(locale, data)
+ I18n.backend.store_translations(locale, data)
+ yield
+ ensure
+ I18n.backend.reload!
+ end
end
diff --git a/actionmailer/test/log_subscriber_test.rb b/actionmailer/test/log_subscriber_test.rb
index 3871b16840..d864c3acca 100644
--- a/actionmailer/test/log_subscriber_test.rb
+++ b/actionmailer/test/log_subscriber_test.rb
@@ -1,7 +1,7 @@
-require 'abstract_unit'
-require 'mailers/base_mailer'
-require 'active_support/log_subscriber/test_helper'
-require 'action_mailer/log_subscriber'
+require "abstract_unit"
+require "mailers/base_mailer"
+require "active_support/log_subscriber/test_helper"
+require "action_mailer/log_subscriber"
class AMLogSubscriberTest < ActionMailer::TestCase
include ActiveSupport::LogSubscriber::TestHelper
diff --git a/actionmailer/test/mail_helper_test.rb b/actionmailer/test/mail_helper_test.rb
index ff6b25b0c7..a2e04b5f48 100644
--- a/actionmailer/test/mail_helper_test.rb
+++ b/actionmailer/test/mail_helper_test.rb
@@ -1,4 +1,4 @@
-require 'abstract_unit'
+require "abstract_unit"
class HelperMailer < ActionMailer::Base
def use_mail_helper
@@ -65,12 +65,12 @@ The second
end
end
- protected
+ private
- def mail_with_defaults(&block)
- mail(to: "test@localhost", from: "tester@example.com",
- subject: "using helpers", &block)
- end
+ def mail_with_defaults(&block)
+ mail(to: "test@localhost", from: "tester@example.com",
+ subject: "using helpers", &block)
+ end
end
class MailerHelperTest < ActionMailer::TestCase
diff --git a/actionmailer/test/mail_layout_test.rb b/actionmailer/test/mail_layout_test.rb
index 166dd096d4..73059d782d 100644
--- a/actionmailer/test/mail_layout_test.rb
+++ b/actionmailer/test/mail_layout_test.rb
@@ -1,7 +1,7 @@
-require 'abstract_unit'
+require "abstract_unit"
class AutoLayoutMailer < ActionMailer::Base
- default to: 'test@localhost',
+ default to: "test@localhost",
subject: "You have a mail",
from: "tester@example.com"
@@ -11,7 +11,7 @@ class AutoLayoutMailer < ActionMailer::Base
def spam
@world = "Earth"
- mail(body: render(inline: "Hello, <%= @world %>", layout: 'spam'))
+ mail(body: render(inline: "Hello, <%= @world %>", layout: "spam"))
end
def nolayout
@@ -28,9 +28,9 @@ class AutoLayoutMailer < ActionMailer::Base
end
class ExplicitLayoutMailer < ActionMailer::Base
- layout 'spam', except: [:logout]
+ layout "spam", except: [:logout]
- default to: 'test@localhost',
+ default to: "test@localhost",
subject: "You have a mail",
from: "tester@example.com"
@@ -54,10 +54,10 @@ class LayoutMailerTest < ActiveSupport::TestCase
assert_equal "multipart/alternative", mail.mime_type
assert_equal 2, mail.parts.size
- assert_equal 'text/plain', mail.parts.first.mime_type
+ assert_equal "text/plain", mail.parts.first.mime_type
assert_equal "text/plain layout - text/plain multipart", mail.parts.first.body.to_s
- assert_equal 'text/html', mail.parts.last.mime_type
+ assert_equal "text/html", mail.parts.last.mime_type
assert_equal "Hello from layout text/html multipart", mail.parts.last.body.to_s
end
@@ -66,10 +66,10 @@ class LayoutMailerTest < ActiveSupport::TestCase
assert_equal "multipart/mixed", mail.mime_type
assert_equal 2, mail.parts.size
- assert_equal 'text/plain', mail.parts.first.mime_type
+ assert_equal "text/plain", mail.parts.first.mime_type
assert_equal "text/plain layout - text/plain multipart", mail.parts.first.body.to_s
- assert_equal 'text/html', mail.parts.last.mime_type
+ assert_equal "text/html", mail.parts.last.mime_type
assert_equal "Hello from layout text/html multipart", mail.parts.last.body.to_s
end
diff --git a/actionmailer/test/mailers/asset_mailer.rb b/actionmailer/test/mailers/asset_mailer.rb
index f54a50d00d..1cf15128d2 100644
--- a/actionmailer/test/mailers/asset_mailer.rb
+++ b/actionmailer/test/mailers/asset_mailer.rb
@@ -2,6 +2,6 @@ class AssetMailer < ActionMailer::Base
self.mailer_name = "asset_mailer"
def welcome
- mail
+ mail
end
end
diff --git a/actionmailer/test/mailers/base_mailer.rb b/actionmailer/test/mailers/base_mailer.rb
index bd991e209e..2a8884959c 100644
--- a/actionmailer/test/mailers/base_mailer.rb
+++ b/actionmailer/test/mailers/base_mailer.rb
@@ -1,13 +1,13 @@
class BaseMailer < ActionMailer::Base
self.mailer_name = "base_mailer"
- default to: 'system@test.lindsaar.net',
- from: 'jose@test.plataformatec.com',
- reply_to: 'mikel@test.lindsaar.net'
+ default to: "system@test.lindsaar.net",
+ from: "jose@test.plataformatec.com",
+ reply_to: "mikel@test.lindsaar.net"
def welcome(hash = {})
- headers['X-SPAM'] = "Not SPAM"
- mail({subject: "The first email on new API!"}.merge!(hash))
+ headers["X-SPAM"] = "Not SPAM"
+ mail({ subject: "The first email on new API!" }.merge!(hash))
end
def welcome_with_headers(hash = {})
@@ -28,30 +28,30 @@ class BaseMailer < ActionMailer::Base
end
def inline_attachment
- attachments.inline['logo.png'] = "\312\213\254\232"
+ attachments.inline["logo.png"] = "\312\213\254\232"
mail
end
def attachment_with_content(hash = {})
- attachments['invoice.pdf'] = 'This is test File content'
+ attachments["invoice.pdf"] = "This is test File content"
mail(hash)
end
def attachment_with_hash
- attachments['invoice.jpg'] = { data: ::Base64.encode64("\312\213\254\232)b"),
+ attachments["invoice.jpg"] = { data: ::Base64.encode64("\312\213\254\232)b"),
mime_type: "image/x-jpg",
transfer_encoding: "base64" }
mail
end
def attachment_with_hash_default_encoding
- attachments['invoice.jpg'] = { data: "\312\213\254\232)b",
+ attachments["invoice.jpg"] = { data: "\312\213\254\232)b",
mime_type: "image/x-jpg" }
mail
end
def implicit_multipart(hash = {})
- attachments['invoice.pdf'] = 'This is test File content' if hash.delete(:attachments)
+ attachments["invoice.pdf"] = "This is test File content" if hash.delete(:attachments)
mail(hash)
end
@@ -60,10 +60,10 @@ class BaseMailer < ActionMailer::Base
end
def explicit_multipart(hash = {})
- attachments['invoice.pdf'] = 'This is test File content' if hash.delete(:attachments)
+ attachments["invoice.pdf"] = "This is test File content" if hash.delete(:attachments)
mail(hash) do |format|
- format.text { render text: "TEXT Explicit Multipart" }
- format.html { render text: "HTML Explicit Multipart" }
+ format.text { render plain: "TEXT Explicit Multipart" }
+ format.html { render plain: "HTML Explicit Multipart" }
end
end
@@ -76,14 +76,20 @@ class BaseMailer < ActionMailer::Base
def explicit_multipart_with_any(hash = {})
mail(hash) do |format|
- format.any(:text, :html){ render text: "Format with any!" }
+ format.any(:text, :html) { render plain: "Format with any!" }
+ end
+ end
+
+ def explicit_without_specifying_format_with_any(hash = {})
+ mail(hash) do |format|
+ format.any
end
end
def explicit_multipart_with_options(include_html = false)
mail do |format|
- format.text(content_transfer_encoding: "base64"){ render "welcome" }
- format.html{ render "welcome" } if include_html
+ format.text(content_transfer_encoding: "base64") { render "welcome" }
+ format.html { render "welcome" } if include_html
end
end
@@ -94,18 +100,18 @@ class BaseMailer < ActionMailer::Base
end
end
- def implicit_different_template(template_name='')
+ def implicit_different_template(template_name = "")
mail(template_name: template_name)
end
- def explicit_different_template(template_name='')
+ def explicit_different_template(template_name = "")
mail do |format|
format.text { render template: "#{mailer_name}/#{template_name}" }
format.html { render template: "#{mailer_name}/#{template_name}" }
end
end
- def different_layout(layout_name='')
+ def different_layout(layout_name = "")
mail do |format|
format.text { render layout: layout_name }
format.html { render layout: layout_name }
@@ -125,6 +131,6 @@ class BaseMailer < ActionMailer::Base
end
def with_subject_interpolations
- mail(subject: default_i18n_subject(rapper_or_impersonator: 'Slim Shady'), body: '')
+ mail(subject: default_i18n_subject(rapper_or_impersonator: "Slim Shady"), body: "")
end
end
diff --git a/actionmailer/test/mailers/caching_mailer.rb b/actionmailer/test/mailers/caching_mailer.rb
new file mode 100644
index 0000000000..92d3cff7c9
--- /dev/null
+++ b/actionmailer/test/mailers/caching_mailer.rb
@@ -0,0 +1,23 @@
+class CachingMailer < ActionMailer::Base
+ self.mailer_name = "caching_mailer"
+
+ def fragment_cache
+ mail(subject: "welcome", template_name: "fragment_cache")
+ end
+
+ def fragment_cache_in_partials
+ mail(subject: "welcome", template_name: "fragment_cache_in_partials")
+ end
+
+ def skip_fragment_cache_digesting
+ mail(subject: "welcome", template_name: "skip_fragment_cache_digesting")
+ end
+
+ def fragment_caching_options
+ mail(subject: "welcome", template_name: "fragment_caching_options")
+ end
+
+ def multipart_cache
+ mail(subject: "welcome", template_name: "multipart_cache")
+ end
+end
diff --git a/actionmailer/test/mailers/delayed_mailer.rb b/actionmailer/test/mailers/delayed_mailer.rb
index 62d4baa434..cae4ec2f48 100644
--- a/actionmailer/test/mailers/delayed_mailer.rb
+++ b/actionmailer/test/mailers/delayed_mailer.rb
@@ -1,6 +1,26 @@
+require "active_job/arguments"
+
+class DelayedMailerError < StandardError; end
+
class DelayedMailer < ActionMailer::Base
+ cattr_accessor :last_error
+ cattr_accessor :last_rescue_from_instance
+
+ rescue_from DelayedMailerError do |error|
+ @@last_error = error
+ @@last_rescue_from_instance = self
+ end
+
+ rescue_from ActiveJob::DeserializationError do |error|
+ @@last_error = error
+ @@last_rescue_from_instance = self
+ end
def test_message(*)
- mail(from: 'test-sender@test.com', to: 'test-receiver@test.com', subject: 'Test Subject', body: 'Test Body')
+ mail(from: "test-sender@test.com", to: "test-receiver@test.com", subject: "Test Subject", body: "Test Body")
+ end
+
+ def test_raise(klass_name)
+ raise klass_name.constantize, "boom"
end
end
diff --git a/actionmailer/test/mailers/proc_mailer.rb b/actionmailer/test/mailers/proc_mailer.rb
index 7e189d861f..2487db9eb9 100644
--- a/actionmailer/test/mailers/proc_mailer.rb
+++ b/actionmailer/test/mailers/proc_mailer.rb
@@ -1,8 +1,8 @@
class ProcMailer < ActionMailer::Base
- default to: 'system@test.lindsaar.net',
- 'X-Proc-Method' => Proc.new { Time.now.to_i.to_s },
+ default to: "system@test.lindsaar.net",
+ "X-Proc-Method" => Proc.new { Time.now.to_i.to_s },
subject: Proc.new { give_a_greeting },
- 'x-has-to-proc' => :symbol
+ "x-has-to-proc" => :symbol
def welcome
mail
@@ -10,8 +10,7 @@ class ProcMailer < ActionMailer::Base
private
- def give_a_greeting
- "Thanks for signing up this afternoon"
- end
-
+ def give_a_greeting
+ "Thanks for signing up this afternoon"
+ end
end
diff --git a/actionmailer/test/message_delivery_test.rb b/actionmailer/test/message_delivery_test.rb
index 862ce26187..a79d77e1e5 100644
--- a/actionmailer/test/message_delivery_test.rb
+++ b/actionmailer/test/message_delivery_test.rb
@@ -1,7 +1,6 @@
-require 'abstract_unit'
-require 'active_job'
-require 'minitest/mock'
-require 'mailers/delayed_mailer'
+require "abstract_unit"
+require "active_job"
+require "mailers/delayed_mailer"
class MessageDeliveryTest < ActiveSupport::TestCase
include ActiveJob::TestHelper
@@ -13,39 +12,46 @@ class MessageDeliveryTest < ActiveSupport::TestCase
ActionMailer::Base.deliver_later_queue_name = :test_queue
ActionMailer::Base.delivery_method = :test
ActiveJob::Base.logger = Logger.new(nil)
- @mail = DelayedMailer.test_message(1, 2, 3)
ActionMailer::Base.deliveries.clear
ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs = true
ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true
+
+ DelayedMailer.last_error = nil
+ DelayedMailer.last_rescue_from_instance = nil
+
+ @mail = DelayedMailer.test_message(1, 2, 3)
end
teardown do
ActiveJob::Base.logger = @previous_logger
ActionMailer::Base.delivery_method = @previous_delivery_method
ActionMailer::Base.deliver_later_queue_name = @previous_deliver_later_queue_name
+
+ DelayedMailer.last_error = nil
+ DelayedMailer.last_rescue_from_instance = nil
end
- test 'should have a message' do
+ test "should have a message" do
assert @mail.message
end
- test 'its message should be a Mail::Message' do
+ test "its message should be a Mail::Message" do
assert_equal Mail::Message , @mail.message.class
end
- test 'should respond to .deliver_later' do
+ test "should respond to .deliver_later" do
assert_respond_to @mail, :deliver_later
end
- test 'should respond to .deliver_later!' do
+ test "should respond to .deliver_later!" do
assert_respond_to @mail, :deliver_later!
end
- test 'should respond to .deliver_now' do
+ test "should respond to .deliver_now" do
assert_respond_to @mail, :deliver_now
end
- test 'should respond to .deliver_now!' do
+ test "should respond to .deliver_now!" do
assert_respond_to @mail, :deliver_now!
end
@@ -56,42 +62,92 @@ class MessageDeliveryTest < ActiveSupport::TestCase
ActionMailer::Base.deliveries.clear
end
- test 'should enqueue the email with :deliver_now delivery method' do
- assert_performed_with(job: ActionMailer::DeliveryJob, args: ['DelayedMailer', 'test_message', 'deliver_now', 1, 2, 3]) do
+ test "should enqueue the email with :deliver_now delivery method" do
+ assert_performed_with(job: ActionMailer::DeliveryJob, args: ["DelayedMailer", "test_message", "deliver_now", 1, 2, 3]) do
@mail.deliver_later
end
end
- test 'should enqueue the email with :deliver_now! delivery method' do
- assert_performed_with(job: ActionMailer::DeliveryJob, args: ['DelayedMailer', 'test_message', 'deliver_now!', 1, 2, 3]) do
+ test "should enqueue the email with :deliver_now! delivery method" do
+ assert_performed_with(job: ActionMailer::DeliveryJob, args: ["DelayedMailer", "test_message", "deliver_now!", 1, 2, 3]) do
@mail.deliver_later!
end
end
- test 'should enqueue a delivery with a delay' do
+ test "should enqueue a delivery with a delay" do
travel_to Time.new(2004, 11, 24, 01, 04, 44) do
- assert_performed_with(job: ActionMailer::DeliveryJob, at: Time.current.to_f+600.seconds, args: ['DelayedMailer', 'test_message', 'deliver_now', 1, 2, 3]) do
+ assert_performed_with(job: ActionMailer::DeliveryJob, at: Time.current.to_f + 600.seconds, args: ["DelayedMailer", "test_message", "deliver_now", 1, 2, 3]) do
@mail.deliver_later wait: 600.seconds
end
end
end
- test 'should enqueue a delivery at a specific time' do
+ test "should enqueue a delivery at a specific time" do
later_time = Time.now.to_f + 3600
- assert_performed_with(job: ActionMailer::DeliveryJob, at: later_time, args: ['DelayedMailer', 'test_message', 'deliver_now', 1, 2, 3]) do
+ assert_performed_with(job: ActionMailer::DeliveryJob, at: later_time, args: ["DelayedMailer", "test_message", "deliver_now", 1, 2, 3]) do
@mail.deliver_later wait_until: later_time
end
end
- test 'should enqueue the job on the correct queue' do
- assert_performed_with(job: ActionMailer::DeliveryJob, args: ['DelayedMailer', 'test_message', 'deliver_now', 1, 2, 3], queue: "test_queue") do
+ test "should enqueue the job on the correct queue" do
+ assert_performed_with(job: ActionMailer::DeliveryJob, args: ["DelayedMailer", "test_message", "deliver_now", 1, 2, 3], queue: "test_queue") do
@mail.deliver_later
end
end
- test 'can override the queue when enqueuing mail' do
- assert_performed_with(job: ActionMailer::DeliveryJob, args: ['DelayedMailer', 'test_message', 'deliver_now', 1, 2, 3], queue: "another_queue") do
+ test "can override the queue when enqueuing mail" do
+ assert_performed_with(job: ActionMailer::DeliveryJob, args: ["DelayedMailer", "test_message", "deliver_now", 1, 2, 3], queue: "another_queue") do
@mail.deliver_later(queue: :another_queue)
end
end
+
+ test "deliver_later after accessing the message is disallowed" do
+ @mail.message # Load the message, which calls the mailer method.
+
+ assert_raise RuntimeError do
+ @mail.deliver_later
+ end
+ end
+
+ test "job delegates error handling to mailer" do
+ # Superclass not rescued by mailer's rescue_from RuntimeError
+ message = DelayedMailer.test_raise("StandardError")
+ assert_raise(StandardError) { message.deliver_later }
+ assert_nil DelayedMailer.last_error
+ assert_nil DelayedMailer.last_rescue_from_instance
+
+ # Rescued by mailer's rescue_from RuntimeError
+ message = DelayedMailer.test_raise("DelayedMailerError")
+ assert_nothing_raised { message.deliver_later }
+ assert_equal "boom", DelayedMailer.last_error.message
+ assert_kind_of DelayedMailer, DelayedMailer.last_rescue_from_instance
+ end
+
+ class DeserializationErrorFixture
+ include GlobalID::Identification
+
+ def self.find(id)
+ raise "boom, missing find"
+ end
+
+ attr_reader :id
+ def initialize(id = 1)
+ @id = id
+ end
+
+ def to_global_id(options = {})
+ super app: "foo"
+ end
+ end
+
+ test "job delegates deserialization errors to mailer class" do
+ # Inject an argument that can't be deserialized.
+ message = DelayedMailer.test_message(DeserializationErrorFixture.new)
+
+ # DeserializationError is raised, rescued, and delegated to the handler
+ # on the mailer class.
+ assert_nothing_raised { message.deliver_later }
+ assert_equal DelayedMailer, DelayedMailer.last_rescue_from_instance
+ assert_equal "Error while trying to deserialize arguments: boom, missing find", DelayedMailer.last_error.message
+ end
end
diff --git a/actionmailer/test/test_case_test.rb b/actionmailer/test/test_case_test.rb
index 86fd37bea6..193d107b0a 100644
--- a/actionmailer/test/test_case_test.rb
+++ b/actionmailer/test/test_case_test.rb
@@ -1,8 +1,46 @@
-require 'abstract_unit'
+require "abstract_unit"
class TestTestMailer < ActionMailer::Base
end
+class ClearTestDeliveriesMixinTest < ActiveSupport::TestCase
+ include ActionMailer::TestCase::ClearTestDeliveries
+
+ def before_setup
+ ActionMailer::Base.delivery_method, @original_delivery_method = :test, ActionMailer::Base.delivery_method
+ ActionMailer::Base.deliveries << "better clear me, setup"
+ super
+ end
+
+ def after_teardown
+ super
+ assert_equal [], ActionMailer::Base.deliveries
+ ActionMailer::Base.delivery_method = @original_delivery_method
+ end
+
+ def test_deliveries_are_cleared_on_setup_and_teardown
+ assert_equal [], ActionMailer::Base.deliveries
+ ActionMailer::Base.deliveries << "better clear me, teardown"
+ end
+end
+
+class MailerDeliveriesClearingTest < ActionMailer::TestCase
+ def before_setup
+ ActionMailer::Base.deliveries << "better clear me, setup"
+ super
+ end
+
+ def after_teardown
+ super
+ assert_equal [], ActionMailer::Base.deliveries
+ end
+
+ def test_deliveries_are_cleared_on_setup_and_teardown
+ assert_equal [], ActionMailer::Base.deliveries
+ ActionMailer::Base.deliveries << "better clear me, teardown"
+ end
+end
+
class CrazyNameMailerTest < ActionMailer::TestCase
tests TestTestMailer
@@ -20,7 +58,7 @@ class CrazySymbolNameMailerTest < ActionMailer::TestCase
end
class CrazyStringNameMailerTest < ActionMailer::TestCase
- tests 'test_test_mailer'
+ tests "test_test_mailer"
def test_set_mailer_class_manual_using_string
assert_equal TestTestMailer, self.class.mailer_class
diff --git a/actionmailer/test/test_helper_test.rb b/actionmailer/test/test_helper_test.rb
index 0a4bc75d3e..31ac5a5211 100644
--- a/actionmailer/test/test_helper_test.rb
+++ b/actionmailer/test/test_helper_test.rb
@@ -1,5 +1,5 @@
-require 'abstract_unit'
-require 'active_support/testing/stream'
+require "abstract_unit"
+require "active_support/testing/stream"
class TestHelperMailer < ActionMailer::Base
def test
@@ -40,11 +40,11 @@ class TestHelperMailerTest < ActionMailer::TestCase
end
def test_encode
- assert_equal '=?UTF-8?Q?This_is_=E3=81=82_string?=', encode('This is あ string')
+ assert_equal "=?UTF-8?Q?This_is_=E3=81=82_string?=", encode("This is あ string")
end
def test_read_fixture
- assert_equal ['Welcome!'], read_fixture('welcome')
+ assert_equal ["Welcome!"], read_fixture("welcome")
end
def test_assert_emails
@@ -198,6 +198,6 @@ class AnotherTestHelperMailerTest < ActionMailer::TestCase
def test_setup_shouldnt_conflict_with_mailer_setup
assert_kind_of Mail::Message, @expected
- assert_equal 'a value', @test_var
+ assert_equal "a value", @test_var
end
end
diff --git a/actionmailer/test/url_test.rb b/actionmailer/test/url_test.rb
index 7928fe9542..6dbfb3a1ff 100644
--- a/actionmailer/test/url_test.rb
+++ b/actionmailer/test/url_test.rb
@@ -1,5 +1,5 @@
-require 'abstract_unit'
-require 'action_controller'
+require "abstract_unit"
+require "action_controller"
class WelcomeController < ActionController::Base
end
@@ -11,10 +11,10 @@ class ActionMailer::Base
end
class UrlTestMailer < ActionMailer::Base
- default_url_options[:host] = 'www.basecamphq.com'
+ default_url_options[:host] = "www.basecamphq.com"
configure do |c|
- c.assets_dir = '' # To get the tests to pass
+ c.assets_dir = "" # To get the tests to pass
end
def signed_up_with_url(recipient)
@@ -27,14 +27,14 @@ class UrlTestMailer < ActionMailer::Base
def exercise_url_for(options)
@options = options
@url = url_for(@options)
- mail(from: 'from@example.com', to: 'to@example.com', subject: 'subject')
+ mail(from: "from@example.com", to: "to@example.com", subject: "subject")
end
end
class ActionMailerUrlTest < ActionMailer::TestCase
class DummyModel
def self.model_name
- OpenStruct.new(route_key: 'dummy_model')
+ OpenStruct.new(route_key: "dummy_model")
end
def persisted?
@@ -50,11 +50,11 @@ class ActionMailerUrlTest < ActionMailer::TestCase
end
end
- def encode( text, charset="UTF-8" )
- quoted_printable( text, charset )
+ def encode(text, charset = "UTF-8")
+ quoted_printable(text, charset)
end
- def new_mail( charset="UTF-8" )
+ def new_mail(charset = "UTF-8")
mail = Mail.new
mail.mime_version = "1.0"
if charset
@@ -64,7 +64,7 @@ class ActionMailerUrlTest < ActionMailer::TestCase
end
def assert_url_for(expected, options, relative = false)
- expected = "http://www.basecamphq.com#{expected}" if expected.start_with?('/') && !relative
+ expected = "http://www.basecamphq.com#{expected}" if expected.start_with?("/") && !relative
urls = UrlTestMailer.exercise_url_for(options).body.to_s.chomp.split
assert_equal expected, urls.first
@@ -72,44 +72,48 @@ class ActionMailerUrlTest < ActionMailer::TestCase
end
def setup
- @recipient = 'test@localhost'
+ @recipient = "test@localhost"
end
def test_url_for
UrlTestMailer.delivery_method = :test
AppRoutes.draw do
- get ':controller(/:action(/:id))'
- get '/welcome' => 'foo#bar', as: 'welcome'
- get '/dummy_model' => 'foo#baz', as: 'dummy_model'
+ ActiveSupport::Deprecation.silence do
+ get ":controller(/:action(/:id))"
+ get "/welcome" => "foo#bar", as: "welcome"
+ get "/dummy_model" => "foo#baz", as: "dummy_model"
+ end
end
# string
- assert_url_for 'http://foo/', 'http://foo/'
+ assert_url_for "http://foo/", "http://foo/"
# symbol
- assert_url_for '/welcome', :welcome
+ assert_url_for "/welcome", :welcome
# hash
- assert_url_for '/a/b/c', controller: 'a', action: 'b', id: 'c'
- assert_url_for '/a/b/c', {controller: 'a', action: 'b', id: 'c', only_path: true}, true
+ assert_url_for "/a/b/c", controller: "a", action: "b", id: "c"
+ assert_url_for "/a/b/c", { controller: "a", action: "b", id: "c", only_path: true }, true
# model
- assert_url_for '/dummy_model', DummyModel.new
+ assert_url_for "/dummy_model", DummyModel.new
# class
- assert_url_for '/dummy_model', DummyModel
+ assert_url_for "/dummy_model", DummyModel
# array
- assert_url_for '/dummy_model' , [DummyModel]
+ assert_url_for "/dummy_model" , [DummyModel]
end
def test_signed_up_with_url
UrlTestMailer.delivery_method = :test
AppRoutes.draw do
- get ':controller(/:action(/:id))'
- get '/welcome' => "foo#bar", as: "welcome"
+ ActiveSupport::Deprecation.silence do
+ get ":controller(/:action(/:id))"
+ get "/welcome" => "foo#bar", as: "welcome"
+ end
end
expected = new_mail
@@ -124,15 +128,15 @@ class ActionMailerUrlTest < ActionMailer::TestCase
assert_nothing_raised { created = UrlTestMailer.signed_up_with_url(@recipient) }
assert_not_nil created
- expected.message_id = '<123@456>'
- created.message_id = '<123@456>'
+ expected.message_id = "<123@456>"
+ created.message_id = "<123@456>"
assert_dom_equal expected.encoded, created.encoded
assert_nothing_raised { UrlTestMailer.signed_up_with_url(@recipient).deliver_now }
assert_not_nil ActionMailer::Base.deliveries.first
delivered = ActionMailer::Base.deliveries.first
- delivered.message_id = '<123@456>'
+ delivered.message_id = "<123@456>"
assert_dom_equal expected.encoded, delivered.encoded
end
end