From 23aa94a7b2d51536baa5eb91a8cd50cdd6dfa99e Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Mon, 2 Dec 2013 16:51:13 +0100 Subject: `mail()` without arguments is a getter for the current mail. This behavior is documented in our guides (http://edgeguides.rubyonrails.org/action_mailer_basics.html#action-mailer-callbacks) but was broken in the past. This commit short curcuits the `mail` method if: 1. mail() was previously called 2. no headers are passed 3. no block is passed Closes #13090. /cc @pixeltrix --- actionmailer/CHANGELOG.md | 19 +++++++++++++++++++ actionmailer/lib/action_mailer/base.rb | 2 ++ actionmailer/test/base_test.rb | 21 +++++++++++++++++++++ 3 files changed, 42 insertions(+) (limited to 'actionmailer') diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md index dc8c6bdf74..857cde399a 100644 --- a/actionmailer/CHANGELOG.md +++ b/actionmailer/CHANGELOG.md @@ -1,3 +1,22 @@ +* Calling `mail()` without arguments serves as getter for the current mail + message and keeps previously set headers. + + Example: + + class MailerWithCallback < ActionMailer::Base + after_action :a_callback + + def welcome + mail subject: "subject", to: ["joe@example.com"] + end + + def a_callback + mail # => returns the current mail message + end + end + + *Yves Senn* + * Instrument the generation of Action Mailer messages. The time it takes to generate a message is written to the log. diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 501fee55aa..6f8ae05539 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -689,6 +689,8 @@ module ActionMailer # end # def mail(headers = {}, &block) + return @_message if @_mail_was_called && headers.blank? && !block + @_mail_was_called = true m = @_message diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index b74728ae34..c1759d9b92 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -671,6 +671,27 @@ class BaseTest < ActiveSupport::TestCase assert_equal ["robert.pankowecki@gmail.com"], DefaultFromMailer.welcome.from end + test "mail() without arguments serves as getter for the current mail message" do + class MailerWithCallback < ActionMailer::Base + after_action :a_callback + + def welcome + headers('X-Special-Header' => 'special indeed!') + mail subject: "subject", body: "hello world", to: ["joe@example.com"] + end + + def a_callback + mail.to << "jane@example.com" + end + end + + mail = MailerWithCallback.welcome + assert_equal "subject", mail.subject + assert_equal ["joe@example.com", "jane@example.com"], mail.to + assert_equal "hello world", mail.body.encoded.strip + assert_equal "special indeed!", mail["X-Special-Header"].to_s + end + protected # Execute the block setting the given values and restoring old values after -- cgit v1.2.3 From d8888b94b32b28003ec247d0565e73dee731275e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Strza=C5=82kowski?= Date: Thu, 5 Dec 2013 00:17:39 +0100 Subject: Retain ActionPack dependency on ActionView --- actionmailer/test/i18n_with_controller_test.rb | 3 --- 1 file changed, 3 deletions(-) (limited to 'actionmailer') diff --git a/actionmailer/test/i18n_with_controller_test.rb b/actionmailer/test/i18n_with_controller_test.rb index ab5eaaa9d5..14a1b11b6d 100644 --- a/actionmailer/test/i18n_with_controller_test.rb +++ b/actionmailer/test/i18n_with_controller_test.rb @@ -15,9 +15,6 @@ class I18nTestMailer < ActionMailer::Base end end -# Emulate AV railtie -ActionController::Base.superclass.send(:include, ActionView::Layouts) - class TestController < ActionController::Base def send_mail I18nTestMailer.mail_with_i18n_subject("test@localhost").deliver -- cgit v1.2.3 From e064658d64f7fb071ec35764f2d5203e8b58e044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Strza=C5=82kowski?= Date: Thu, 5 Dec 2013 00:22:50 +0100 Subject: Include AV::Layouts directly in AM::Base No need to do this in railtie as AM depends on AV either way --- actionmailer/lib/action_mailer/base.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'actionmailer') diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 6f8ae05539..5723b2cc1b 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -373,6 +373,8 @@ module ActionMailer include AbstractController::AssetPaths include AbstractController::Callbacks + include ActionView::Layouts + PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [:@_action_has_layout] def _protected_ivars # :nodoc: -- cgit v1.2.3 From d6dec7fcb6b8fddf8c170182d4fe64ecfc7b2261 Mon Sep 17 00:00:00 2001 From: Andrew White Date: Mon, 16 Dec 2013 05:52:58 +0000 Subject: Add mailer previews feature based on mail_view gem --- actionmailer/CHANGELOG.md | 4 ++ actionmailer/lib/action_mailer.rb | 2 + actionmailer/lib/action_mailer/base.rb | 20 +++++++++ actionmailer/lib/action_mailer/preview.rb | 67 +++++++++++++++++++++++++++++++ actionmailer/lib/action_mailer/railtie.rb | 8 ++++ 5 files changed, 101 insertions(+) create mode 100644 actionmailer/lib/action_mailer/preview.rb (limited to 'actionmailer') diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md index 857cde399a..fc9aefd416 100644 --- a/actionmailer/CHANGELOG.md +++ b/actionmailer/CHANGELOG.md @@ -1,3 +1,7 @@ +* Add mailer previews feature based on 37 Signals mail_view gem + + *Andrew White* + * Calling `mail()` without arguments serves as getter for the current mail message and keeps previously set headers. diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 5b6960c8fc..557eec4728 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -41,6 +41,8 @@ module ActionMailer autoload :Base autoload :DeliveryMethods autoload :MailHelper + autoload :Preview + autoload :Previews, 'action_mailer/preview' autoload :TestCase autoload :TestHelper end diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 5723b2cc1b..e1f3fd03e2 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -308,6 +308,25 @@ module ActionMailer # Note that unless you have a specific reason to do so, you should prefer using before_action # rather than after_action in your ActionMailer classes so that headers are parsed properly. # + # = Previewing emails + # + # You can preview your email templates visually by adding a mailer preview file to the + # ActionMailer::Base.preview_path. Since most emails do something interesting + # with database data, you'll need to write some scenarios to load messages with fake data: + # + # class NotifierPreview < ActionMailer::Preview + # def welcome + # Notifier.welcome(User.first) + # end + # end + # + # Methods must return a Mail::Message object which can be generated by calling the mailer + # method without the additional deliver. The location of the mailer previews + # directory can be configured using the preview_path option which has a default + # of test/mailers/previews: + # + # config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews" + # # = Configuration options # # These options are specified on the class level, like @@ -362,6 +381,7 @@ module ActionMailer # delivery_method :test. Most useful for unit and functional testing. class Base < AbstractController::Base include DeliveryMethods + include Previews abstract! diff --git a/actionmailer/lib/action_mailer/preview.rb b/actionmailer/lib/action_mailer/preview.rb new file mode 100644 index 0000000000..43d9ec4bb5 --- /dev/null +++ b/actionmailer/lib/action_mailer/preview.rb @@ -0,0 +1,67 @@ +require 'active_support/descendants_tracker' + +module ActionMailer + module Previews #:nodoc: + extend ActiveSupport::Concern + + included do + # Set the location of mailer previews through app configuration: + # + # config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews" + # + class_attribute :preview_path, instance_writer: false + end + end + + class Preview + extend ActiveSupport::DescendantsTracker + + class << self + # Returns all mailer preview classes + def all + load_previews if descendants.empty? + descendants + end + + # Returns the mail object for the given email name + def call(email) + preview = self.new + preview.public_send(email) + end + + # Returns all of the available email previews + def emails + public_instance_methods(false).map(&:to_s).sort + end + + # Returns true if the email exists + def email_exists?(email) + emails.include?(email) + end + + # Returns true if the preview exists + def exists?(preview) + all.any?{ |p| p.preview_name == preview } + end + + # Find a mailer preview by its underscored class name + def find(preview) + all.find{ |p| p.preview_name == preview } + end + + # Returns the underscored name of the mailer preview without the suffix + def preview_name + name.sub(/Preview$/, '').underscore + end + + protected + def load_previews #:nodoc: + Dir["#{preview_path}/**/*_preview.rb"].each{ |file| require_dependency file } + end + + def preview_path #:nodoc: + Base.preview_path + end + end + end +end diff --git a/actionmailer/lib/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb index 7677ff3a7c..af8009ba97 100644 --- a/actionmailer/lib/action_mailer/railtie.rb +++ b/actionmailer/lib/action_mailer/railtie.rb @@ -40,5 +40,13 @@ module ActionMailer config.compile_methods! if config.respond_to?(:compile_methods!) end end + + initializer "action_mailer.configure_mailer_previews", before: :set_autoload_paths do |app| + if Rails.env.development? + options = app.config.action_mailer + options.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/mailers/previews" : nil + app.config.autoload_paths << options.preview_path + end + end end end -- cgit v1.2.3 From 6802196a6b7db928f9dc70fe5a4f3b61a3ea33ce Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Mon, 16 Dec 2013 16:04:07 -0200 Subject: Disable available locales checks to avoid warnings running the tests --- actionmailer/test/abstract_unit.rb | 3 +++ 1 file changed, 3 insertions(+) (limited to 'actionmailer') diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb index aa18c512c7..cf790c7487 100644 --- a/actionmailer/test/abstract_unit.rb +++ b/actionmailer/test/abstract_unit.rb @@ -20,6 +20,9 @@ ActionMailer::Base.send(:include, ActionView::Layouts) # Show backtraces for deprecated behavior for quicker cleanup. ActiveSupport::Deprecation.debug = true +# Disable available locale checks to avoid warnings running the test suite. +I18n.enforce_available_locales = false + # Bogus template processors ActionView::Template.register_template_handler :haml, lambda { |template| "Look its HAML!".inspect } ActionView::Template.register_template_handler :bak, lambda { |template| "Lame backup".inspect } -- cgit v1.2.3 From c0a2d474c50a3a096229f956d9696d9e91f1557a Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 17 Dec 2013 16:05:28 -0800 Subject: Get ready to release 4.1.0.beta1 --- actionmailer/lib/action_mailer/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionmailer') diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb index 9d00091972..46eb763c26 100644 --- a/actionmailer/lib/action_mailer/version.rb +++ b/actionmailer/lib/action_mailer/version.rb @@ -1,7 +1,7 @@ module ActionMailer # Returns the version of the currently loaded ActionMailer as a Gem::Version def self.version - Gem::Version.new "4.1.0.beta" + Gem::Version.new "4.1.0.beta1" end module VERSION #:nodoc: -- cgit v1.2.3 From 69b5dc7e002946c5196a7a38e2559a7ce08a75c0 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Wed, 18 Dec 2013 14:36:31 +0100 Subject: document how to access mail previews. [ci skip] /cc @pixeltrix --- actionmailer/lib/action_mailer/base.rb | 3 +++ 1 file changed, 3 insertions(+) (limited to 'actionmailer') diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index e1f3fd03e2..3c21db991c 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -327,6 +327,9 @@ module ActionMailer # # config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews" # + # An overview of all previews is accessible at http://localhost:3000/rails/mailers + # on a running development server instance. + # # = Configuration options # # These options are specified on the class level, like -- cgit v1.2.3 From fbbd4e18994490f00509a5269bbca0dd3521c3ae Mon Sep 17 00:00:00 2001 From: Chun-wei Kuo Date: Thu, 26 Dec 2013 18:14:38 +0800 Subject: Improve font of some code in API documentation [ci skip] * Add "" or "+" to improve font of some code and filenames in API documentation * Does not contain wording changes --- actionmailer/lib/action_mailer/base.rb | 35 +++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) (limited to 'actionmailer') diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 3c21db991c..275f657f8a 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -50,7 +50,7 @@ module ActionMailer # # * mail - Allows you to specify email to be sent. # - # The hash passed to the mail method allows you to specify any header that a Mail::Message + # The hash passed to the mail method allows you to specify any header that a Mail::Message # will accept (any valid Email header including optional fields). # # The mail method, if not passed a block, will inspect your views and send all the views with @@ -229,7 +229,7 @@ module ActionMailer # An interceptor class must implement the :delivering_email(message) method which will be # called before the email is sent, allowing you to make modifications to the email before it hits # the delivery agents. Your class should make any needed modifications directly to the passed - # in Mail::Message instance. + # in Mail::Message instance. # # = Default Hash # @@ -320,7 +320,7 @@ module ActionMailer # end # end # - # Methods must return a Mail::Message object which can be generated by calling the mailer + # Methods must return a Mail::Message object which can be generated by calling the mailer # method without the additional deliver. The location of the mailer previews # directory can be configured using the preview_path option which has a default # of test/mailers/previews: @@ -339,7 +339,7 @@ module ActionMailer # per the above section. # # * logger - the logger is used for generating information on the mailing run if available. - # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers. + # Can be set to +nil+ for no logging. Compatible with both Ruby's own +Logger+ and Log4r loggers. # # * smtp_settings - Allows detailed configuration for :smtp delivery method: # * :address - Allows you to use a remote mail server. Just change it from its default @@ -357,8 +357,9 @@ module ActionMailer # and starts to use it. # * :openssl_verify_mode - 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 ('none', 'peer', 'client_once', 'fail_if_no_peer_cert') or directly the - # constant (OpenSSL::SSL::VERIFY_NONE, OpenSSL::SSL::VERIFY_PEER, ...). + # of an OpenSSL verify constant ('none', 'peer', 'client_once', + # 'fail_if_no_peer_cert') or directly the constant (OpenSSL::SSL::VERIFY_NONE, + # OpenSSL::SSL::VERIFY_PEER, ...). # # * sendmail_settings - Allows you to override options for the :sendmail delivery method. # * :location - The location of the sendmail executable. Defaults to /usr/sbin/sendmail. @@ -373,7 +374,7 @@ module ActionMailer # # * delivery_method - Defines a delivery method. Possible values are :smtp (default), # :sendmail, :test, and :file. Or you may provide a custom delivery method - # object e.g. MyOwnDeliveryMethodClass. See the Mail gem documentation on the interface you need to + # object e.g. +MyOwnDeliveryMethodClass+. See the Mail gem documentation on the interface you need to # implement for a custom delivery agent. # # * perform_deliveries - Determines whether emails are actually sent from Action Mailer when you @@ -429,7 +430,7 @@ module ActionMailer # Register an Observer which will be notified when mail is delivered. # Either a class or a string can be passed in as the Observer. If a string is passed in - # it will be +constantize+d. + # it will be constantized. def register_observer(observer) delivery_observer = (observer.is_a?(String) ? observer.constantize : observer) Mail.register_observer(delivery_observer) @@ -487,11 +488,11 @@ module ActionMailer end end - # Wraps an email delivery inside of ActiveSupport::Notifications instrumentation. + # Wraps an email delivery inside of ActiveSupport::Notifications instrumentation. # - # This method is actually called by the Mail::Message object itself - # through a callback when you call +:deliver+ on the Mail::Message, - # calling +deliver_mail+ directly and passing a Mail::Message will do + # This method is actually called by the Mail::Message object itself + # through a callback when you call :deliver on the Mail::Message, + # calling +deliver_mail+ directly and passing a Mail::Message will do # nothing except tell the logger you sent the email. def deliver_mail(mail) #:nodoc: ActiveSupport::Notifications.instrument("deliver.action_mailer") do |payload| @@ -567,18 +568,18 @@ module ActionMailer self.class.mailer_name end - # Allows you to pass random and unusual headers to the new Mail::Message + # Allows you to pass random and unusual headers to the new Mail::Message # object which will add them to itself. # # headers['X-Special-Domain-Specific-Header'] = "SecretValue" # # You can also pass a hash into headers of header field names and values, - # which will then be set on the Mail::Message object: + # which will then be set on the Mail::Message object: # # headers 'X-Special-Domain-Specific-Header' => "SecretValue", # 'In-Reply-To' => incoming.message_id # - # The resulting Mail::Message will have the following in its header: + # The resulting Mail::Message will have the following in its header: # # X-Special-Domain-Specific-Header: SecretValue def headers(args = nil) @@ -667,8 +668,8 @@ module ActionMailer # templates in the view paths using by default the mailer name and the # method name that it is being called from, it will then create parts for # each of these templates intelligently, making educated guesses on correct - # content type and sequence, and return a fully prepared Mail::Message - # ready to call +:deliver+ on to send. + # content type and sequence, and return a fully prepared Mail::Message + # ready to call :deliver on to send. # # For example: # -- cgit v1.2.3 From 98cb3e69afd687f7c0d4bc48eabf284da691abcc Mon Sep 17 00:00:00 2001 From: Vipul A M Date: Wed, 1 Jan 2014 23:59:49 +0530 Subject: update copyright notices to 2014. [ci skip] --- actionmailer/MIT-LICENSE | 2 +- actionmailer/lib/action_mailer.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'actionmailer') diff --git a/actionmailer/MIT-LICENSE b/actionmailer/MIT-LICENSE index 5c668d9624..d58dd9ed9b 100644 --- a/actionmailer/MIT-LICENSE +++ b/actionmailer/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2013 David Heinemeier Hansson +Copyright (c) 2004-2014 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/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 557eec4728..83969d4074 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2013 David Heinemeier Hansson +# Copyright (c) 2004-2014 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the -- cgit v1.2.3 From 3713e433667ee95caccb53a4062f540405272234 Mon Sep 17 00:00:00 2001 From: Andrew White Date: Sat, 4 Jan 2014 18:42:34 +0000 Subject: Add preview_path to autoload_paths in after_initialize Only config.autoload_paths is frozen, so add the preview_path to ActiveSupport::Dependencies.autoload_paths directly in an after_initialize block. Also protect against a blank preview_path being added to autoload_paths which can cause a serious slowdown as Dir[] tries to load all *_preview.rb files under / Fixes #13372 --- actionmailer/lib/action_mailer/preview.rb | 8 +++++++- actionmailer/lib/action_mailer/railtie.rb | 12 +++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'actionmailer') diff --git a/actionmailer/lib/action_mailer/preview.rb b/actionmailer/lib/action_mailer/preview.rb index 43d9ec4bb5..ecceaf8c70 100644 --- a/actionmailer/lib/action_mailer/preview.rb +++ b/actionmailer/lib/action_mailer/preview.rb @@ -56,12 +56,18 @@ module ActionMailer protected def load_previews #:nodoc: - Dir["#{preview_path}/**/*_preview.rb"].each{ |file| require_dependency file } + if preview_path? + Dir["#{preview_path}/**/*_preview.rb"].each{ |file| require_dependency file } + end end def preview_path #:nodoc: Base.preview_path end + + def preview_path? #:nodoc: + Base.preview_path? + end end end end diff --git a/actionmailer/lib/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb index af8009ba97..c893ddfef5 100644 --- a/actionmailer/lib/action_mailer/railtie.rb +++ b/actionmailer/lib/action_mailer/railtie.rb @@ -19,6 +19,10 @@ module ActionMailer options.javascripts_dir ||= paths["public/javascripts"].first options.stylesheets_dir ||= paths["public/stylesheets"].first + if Rails.env.development? + options.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/mailers/previews" : nil + end + # make sure readers methods get compiled options.asset_host ||= app.config.asset_host options.relative_url_root ||= app.config.relative_url_root @@ -41,11 +45,9 @@ module ActionMailer end end - initializer "action_mailer.configure_mailer_previews", before: :set_autoload_paths do |app| - if Rails.env.development? - options = app.config.action_mailer - options.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/mailers/previews" : nil - app.config.autoload_paths << options.preview_path + config.after_initialize do + if ActionMailer::Base.preview_path? + ActiveSupport::Dependencies.autoload_paths << ActionMailer::Base.preview_path end end end -- cgit v1.2.3 From 535bd55fcd1d956d923da5c21976d8ba0b8c00af Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Mon, 6 Jan 2014 10:57:00 +0100 Subject: quick formatting pass through CHANGELOGS. [ci skip]. --- actionmailer/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) (limited to 'actionmailer') diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md index fc9aefd416..1867a392eb 100644 --- a/actionmailer/CHANGELOG.md +++ b/actionmailer/CHANGELOG.md @@ -5,6 +5,8 @@ * Calling `mail()` without arguments serves as getter for the current mail message and keeps previously set headers. + Fixes #13090. + Example: class MailerWithCallback < ActionMailer::Base -- cgit v1.2.3