From 5b0c8a1266016c6418555399da83fbc7210c6734 Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar Date: Fri, 15 Jan 2010 20:03:45 +1100 Subject: [PATCH 01/63] Removing internal delivery agents --- .../lib/action_mailer/delivery_method.rb | 56 ------------------- .../lib/action_mailer/delivery_method/file.rb | 21 ------- .../action_mailer/delivery_method/sendmail.rb | 22 -------- .../lib/action_mailer/delivery_method/smtp.rb | 30 ---------- .../lib/action_mailer/delivery_method/test.rb | 12 ---- 5 files changed, 141 deletions(-) delete mode 100644 actionmailer/lib/action_mailer/delivery_method.rb delete mode 100644 actionmailer/lib/action_mailer/delivery_method/file.rb delete mode 100644 actionmailer/lib/action_mailer/delivery_method/sendmail.rb delete mode 100644 actionmailer/lib/action_mailer/delivery_method/smtp.rb delete mode 100644 actionmailer/lib/action_mailer/delivery_method/test.rb diff --git a/actionmailer/lib/action_mailer/delivery_method.rb b/actionmailer/lib/action_mailer/delivery_method.rb deleted file mode 100644 index 4f7d3afc3cd..00000000000 --- a/actionmailer/lib/action_mailer/delivery_method.rb +++ /dev/null @@ -1,56 +0,0 @@ -require 'active_support/core_ext/class' - -module ActionMailer - module DeliveryMethod - autoload :File, 'action_mailer/delivery_method/file' - autoload :Sendmail, 'action_mailer/delivery_method/sendmail' - autoload :Smtp, 'action_mailer/delivery_method/smtp' - autoload :Test, 'action_mailer/delivery_method/test' - - # Creates a new DeliveryMethod object according to the given options. - # - # If no arguments are passed to this method, then a new - # ActionMailer::DeliveryMethod::Stmp object will be returned. - # - # If you pass a Symbol as the first argument, then a corresponding - # delivery method class under the ActionMailer::DeliveryMethod namespace - # will be created. - # For example: - # - # ActionMailer::DeliveryMethod.lookup_method(:sendmail) - # # => returns a new ActionMailer::DeliveryMethod::Sendmail object - # - # If the first argument is not a Symbol, then it will simply be returned: - # - # ActionMailer::DeliveryMethod.lookup_method(MyOwnDeliveryMethod.new) - # # => returns MyOwnDeliveryMethod.new - def self.lookup_method(delivery_method) - case delivery_method - when Symbol - method_name = delivery_method.to_s.camelize - method_class = ActionMailer::DeliveryMethod.const_get(method_name) - method_class.new - when nil # default - Smtp.new - else - delivery_method - end - end - - # An abstract delivery method class. There are multiple delivery method classes. - # See the classes under the ActionMailer::DeliveryMethod, e.g. - # ActionMailer::DeliveryMethod::Smtp. - # Smtp is the default delivery method for production - # while Test is used in testing. - # - # each delivery method exposes just one method - # - # delivery_method = ActionMailer::DeliveryMethod::Smtp.new - # delivery_method.perform_delivery(mail) # send the mail via smtp - # - class Method - superclass_delegating_accessor :settings - self.settings = {} - end - end -end diff --git a/actionmailer/lib/action_mailer/delivery_method/file.rb b/actionmailer/lib/action_mailer/delivery_method/file.rb deleted file mode 100644 index 571e32df491..00000000000 --- a/actionmailer/lib/action_mailer/delivery_method/file.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'tmpdir' - -module ActionMailer - module DeliveryMethod - - # A delivery method implementation which writes all mails to a file. - class File < Method - self.settings = { - :location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails" - } - - def perform_delivery(mail) - FileUtils.mkdir_p settings[:location] - - mail.destinations.uniq.each do |to| - ::File.open(::File.join(settings[:location], to), 'a') { |f| f.write(mail) } - end - end - end - end -end diff --git a/actionmailer/lib/action_mailer/delivery_method/sendmail.rb b/actionmailer/lib/action_mailer/delivery_method/sendmail.rb deleted file mode 100644 index db55af79f1e..00000000000 --- a/actionmailer/lib/action_mailer/delivery_method/sendmail.rb +++ /dev/null @@ -1,22 +0,0 @@ -module ActionMailer - module DeliveryMethod - - # A delivery method implementation which sends via sendmail. - class Sendmail < Method - self.settings = { - :location => '/usr/sbin/sendmail', - :arguments => '-i -t' - } - - def perform_delivery(mail) - sendmail_args = settings[:arguments] - sendmail_args += " -f \"#{mail['return-path']}\"" if mail['return-path'] - IO.popen("#{settings[:location]} #{sendmail_args}","w+") do |sm| - sm.print(mail.encoded.gsub(/\r/, '')) - sm.flush - end - end - end - - end -end diff --git a/actionmailer/lib/action_mailer/delivery_method/smtp.rb b/actionmailer/lib/action_mailer/delivery_method/smtp.rb deleted file mode 100644 index af30c498b51..00000000000 --- a/actionmailer/lib/action_mailer/delivery_method/smtp.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'net/smtp' - -module ActionMailer - module DeliveryMethod - # A delivery method implementation which sends via smtp. - class Smtp < Method - self.settings = { - :address => "localhost", - :port => 25, - :domain => 'localhost.localdomain', - :user_name => nil, - :password => nil, - :authentication => nil, - :enable_starttls_auto => true, - } - - def perform_delivery(mail) - destinations = mail.destinations - sender = (mail['return-path'] && mail['return-path'].address) || mail['from'] - - smtp = Net::SMTP.new(settings[:address], settings[:port]) - smtp.enable_starttls_auto if settings[:enable_starttls_auto] && smtp.respond_to?(:enable_starttls_auto) - smtp.start(settings[:domain], settings[:user_name], settings[:password], - settings[:authentication]) do |smtp| - smtp.sendmail(mail.encoded, sender, destinations) - end - end - end - end -end diff --git a/actionmailer/lib/action_mailer/delivery_method/test.rb b/actionmailer/lib/action_mailer/delivery_method/test.rb deleted file mode 100644 index 6e3239d52a2..00000000000 --- a/actionmailer/lib/action_mailer/delivery_method/test.rb +++ /dev/null @@ -1,12 +0,0 @@ -module ActionMailer - module DeliveryMethod - - # A delivery method implementation designed for testing, which just appends each record to the :deliveries array - class Test < Method - def perform_delivery(mail) - ActionMailer::Base.deliveries << mail - end - end - - end -end From 0750304c0117c85f06cf1ea608747b8fda0a0ecd Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar Date: Sat, 16 Jan 2010 22:10:11 +1100 Subject: [PATCH 02/63] Migrated over to Mail doing delivery. --- actionmailer/lib/action_mailer/base.rb | 87 +++++++++++++++++------ actionmailer/test/abstract_unit.rb | 6 +- actionmailer/test/delivery_method_test.rb | 63 ++++++++++++---- actionmailer/test/mail_service_test.rb | 12 ++-- actionmailer/test/test_helper_test.rb | 2 +- 5 files changed, 125 insertions(+), 45 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index b9a01356baf..89bd45a994a 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -355,20 +355,42 @@ module ActionMailer #:nodoc: alias :controller_path :mailer_name class << self - attr_writer :mailer_name - - delegate :settings, :settings=, :to => ActionMailer::DeliveryMethod::File, :prefix => :file - delegate :settings, :settings=, :to => ActionMailer::DeliveryMethod::Sendmail, :prefix => :sendmail - delegate :settings, :settings=, :to => ActionMailer::DeliveryMethod::Smtp, :prefix => :smtp def mailer_name @mailer_name ||= name.underscore end - alias :controller_path :mailer_name + attr_writer :mailer_name - def delivery_method=(method_name) - @delivery_method = ActionMailer::DeliveryMethod.lookup_method(method_name) + def file_settings + @file_settings ||= {:location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"} end + attr_writer :file_settings + + def sendmail_settings + @sendmail_settings ||= { :location => '/usr/sbin/sendmail', + :arguments => '-i -t' } + end + attr_writer :sendmail_settings + + def smtp_settings + @smtp_settings ||= { :address => "localhost", + :port => 25, + :domain => 'localhost.localdomain', + :user_name => nil, + :password => nil, + :authentication => nil, + :enable_starttls_auto => true } + end + attr_writer :smtp_settings + + def custom_settings + @custom_settings ||= {} + end + attr_writer :custom_settings + + attr_writer :delivery_method + + alias :controller_path :mailer_name def respond_to?(method_symbol, include_private = false) #:nodoc: matches_dynamic_method?(method_symbol) || super @@ -413,7 +435,35 @@ module ActionMailer #:nodoc: # email.set_some_obscure_header "frobnicate" # MyMailer.deliver(email) def deliver(mail) - new.deliver!(mail) + raise "no mail object available for delivery!" unless mail + + begin + ActiveSupport::Notifications.instrument("action_mailer.deliver", + :mailer => self.name) do |payload| + set_payload_for_mail(payload, mail) + mail.delivery_method delivery_method, delivery_settings + if @@perform_deliveries + mail.deliver! + self.deliveries << mail + end + end + rescue Exception => e # Net::SMTP errors or sendmail pipe errors + raise e if raise_delivery_errors + end + + mail + end + + # Get the delivery settings set. This is set using the :smtp_settings, + # :sendmail_settings, :file_settings or :custom_setings + # options hashes. You can set :custom_settings if you are providing + # your own Custom Delivery Method and want to pass options to it. + def delivery_settings + if [:smtp, :sendmail, :file].include?(delivery_method) + instance_variable_get("@#{delivery_method}_settings") + else + @custom_settings + end end def template_root @@ -506,19 +556,7 @@ module ActionMailer #:nodoc: # object (from the create! method). If no cached mail object exists, and # no alternate has been given as the parameter, this will fail. def deliver!(mail = @mail) - raise "no mail object available for delivery!" unless mail - - begin - ActiveSupport::Notifications.instrument("action_mailer.deliver", - :template => template, :mailer => self.class.name) do |payload| - self.class.set_payload_for_mail(payload, mail) - self.delivery_method.perform_delivery(mail) if perform_deliveries - end - rescue Exception => e # Net::SMTP errors or sendmail pipe errors - raise e if raise_delivery_errors - end - - mail + self.class.deliver(mail) end private @@ -533,6 +571,11 @@ module ActionMailer #:nodoc: @mime_version ||= @@default_mime_version.dup if @@default_mime_version @mailer_name ||= self.class.mailer_name.dup + @delivery_method = self.class.delivery_method + @smtp_settings = self.class.smtp_settings.dup + @sendmail_settings = self.class.sendmail_settings.dup + @file_settings = self.class.file_settings.dup + @custom_settings = self.class.custom_settings.dup @template ||= method_name @parts ||= [] diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb index 50b8a530068..1fc5ab85e01 100644 --- a/actionmailer/test/abstract_unit.rb +++ b/actionmailer/test/abstract_unit.rb @@ -26,6 +26,7 @@ FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures') ActionMailer::Base.template_root = FIXTURE_LOAD_PATH class MockSMTP + def self.deliveries @@deliveries end @@ -41,6 +42,7 @@ class MockSMTP def start(*args) yield self end + end class Net::SMTP @@ -57,9 +59,9 @@ rescue LoadError $stderr.puts "Skipping #{test_name} tests. `gem install #{gem_name}` and try again." end -def set_delivery_method(delivery_method) +def set_delivery_method(method) @old_delivery_method = ActionMailer::Base.delivery_method - ActionMailer::Base.delivery_method = delivery_method + ActionMailer::Base.delivery_method = method end def restore_delivery_method diff --git a/actionmailer/test/delivery_method_test.rb b/actionmailer/test/delivery_method_test.rb index 8f8c6b02757..fb43086423c 100644 --- a/actionmailer/test/delivery_method_test.rb +++ b/actionmailer/test/delivery_method_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'mail' class DefaultDeliveryMethodMailer < ActionMailer::Base end @@ -12,18 +13,22 @@ class FileDeliveryMethodMailer < ActionMailer::Base end class CustomDeliveryMethod - attr_accessor :custom_deliveries - def initialize() - @customer_deliveries = [] + + def initialize(values) + @custom_deliveries = [] end - def self.perform_delivery(mail) + attr_accessor :custom_deliveries + + attr_accessor :settings + + def deliver!(mail) self.custom_deliveries << mail end end class CustomerDeliveryMailer < ActionMailer::Base - self.delivery_method = CustomDeliveryMethod.new + self.delivery_method = CustomDeliveryMethod end class ActionMailerBase_delivery_method_Test < Test::Unit::TestCase @@ -36,7 +41,18 @@ class ActionMailerBase_delivery_method_Test < Test::Unit::TestCase end def test_should_be_the_default_smtp - assert_instance_of ActionMailer::DeliveryMethod::Smtp, ActionMailer::Base.delivery_method + assert_equal :smtp, ActionMailer::Base.delivery_method + end + + def test_should_have_default_smtp_delivery_method_settings + settings = { :address => "localhost", + :port => 25, + :domain => 'localhost.localdomain', + :user_name => nil, + :password => nil, + :authentication => nil, + :enable_starttls_auto => true } + assert_equal settings, ActionMailer::Base.smtp_settings end end @@ -50,7 +66,18 @@ class DefaultDeliveryMethodMailer_delivery_method_Test < Test::Unit::TestCase end def test_should_be_the_default_smtp - assert_instance_of ActionMailer::DeliveryMethod::Smtp, DefaultDeliveryMethodMailer.delivery_method + assert_equal :smtp, DefaultDeliveryMethodMailer.delivery_method + end + + def test_should_have_default_smtp_delivery_method_settings + settings = { :address => "localhost", + :port => 25, + :domain => 'localhost.localdomain', + :user_name => nil, + :password => nil, + :authentication => nil, + :enable_starttls_auto => true } + assert_equal settings, DefaultDeliveryMethodMailer.smtp_settings end end @@ -64,7 +91,13 @@ class NonDefaultDeliveryMethodMailer_delivery_method_Test < Test::Unit::TestCase end def test_should_be_the_set_delivery_method - assert_instance_of ActionMailer::DeliveryMethod::Sendmail, NonDefaultDeliveryMethodMailer.delivery_method + assert_equal :sendmail, NonDefaultDeliveryMethodMailer.delivery_method + end + + def test_should_have_default_sendmail_delivery_method_settings + settings = {:location => '/usr/sbin/sendmail', + :arguments => '-i -t'} + assert_equal settings, NonDefaultDeliveryMethodMailer.sendmail_settings end end @@ -78,11 +111,12 @@ class FileDeliveryMethodMailer_delivery_method_Test < Test::Unit::TestCase end def test_should_be_the_set_delivery_method - assert_instance_of ActionMailer::DeliveryMethod::File, FileDeliveryMethodMailer.delivery_method + assert_equal :file, FileDeliveryMethodMailer.delivery_method end - def test_should_default_location_to_the_tmpdir - assert_equal "#{Dir.tmpdir}/mails", ActionMailer::Base.file_settings[:location] + def test_should_have_default_file_delivery_method_settings + settings = {:location => "#{Dir.tmpdir}/mails"} + assert_equal settings, FileDeliveryMethodMailer.file_settings end end @@ -96,6 +130,11 @@ class CustomDeliveryMethodMailer_delivery_method_Test < Test::Unit::TestCase end def test_should_be_the_set_delivery_method - assert_instance_of CustomDeliveryMethod, CustomerDeliveryMailer.delivery_method + assert_equal CustomDeliveryMethod, CustomerDeliveryMailer.delivery_method + end + + def test_should_have_default_custom_delivery_method_settings + settings = {} + assert_equal settings, CustomerDeliveryMailer.custom_settings end end diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index cd41739f1a7..c14dd644cd7 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -546,7 +546,7 @@ class ActionMailerTest < Test::Unit::TestCase assert_not_nil mail mail, from, to = mail - assert_equal 'system@loudthinking.com', from.addresses.first + assert_equal 'system@loudthinking.com', from end def test_reply_to @@ -675,16 +675,13 @@ class ActionMailerTest < Test::Unit::TestCase def test_doesnt_raise_errors_when_raise_delivery_errors_is_false ActionMailer::Base.raise_delivery_errors = false - TestMailer.delivery_method.expects(:perform_delivery).raises(Exception) + Mail::Message.any_instance.expects(:deliver!).raises(Exception) assert_nothing_raised { TestMailer.deliver_signed_up(@recipient) } end def test_performs_delivery_via_sendmail - sm = mock() - sm.expects(:print).with(anything) - sm.expects(:flush) - IO.expects(:popen).once.with('/usr/sbin/sendmail -i -t', 'w+').yields(sm) - ActionMailer::Base.delivery_method = :sendmail + IO.expects(:popen).once.with('/usr/sbin/sendmail -i -t test@localhost', 'w+') + TestMailer.delivery_method = :sendmail TestMailer.deliver_signed_up(@recipient) end @@ -1113,7 +1110,6 @@ EOF def test_starttls_is_not_enabled ActionMailer::Base.smtp_settings[:enable_starttls_auto] = false MockSMTP.any_instance.expects(:respond_to?).never - MockSMTP.any_instance.expects(:enable_starttls_auto).never ActionMailer::Base.delivery_method = :smtp TestMailer.deliver_signed_up(@recipient) ensure diff --git a/actionmailer/test/test_helper_test.rb b/actionmailer/test/test_helper_test.rb index 1fed26f78f6..48e4433e984 100644 --- a/actionmailer/test/test_helper_test.rb +++ b/actionmailer/test/test_helper_test.rb @@ -12,7 +12,7 @@ end class TestHelperMailerTest < ActionMailer::TestCase def test_setup_sets_right_action_mailer_options - assert_instance_of ActionMailer::DeliveryMethod::Test, ActionMailer::Base.delivery_method + assert_equal :test, ActionMailer::Base.delivery_method assert ActionMailer::Base.perform_deliveries assert_equal [], ActionMailer::Base.deliveries end From 03c1457eb5f05519dfc76750d72307258c7771e1 Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar Date: Sat, 16 Jan 2010 22:43:13 +1100 Subject: [PATCH 03/63] Removed autoload of DeliveryMethods --- actionmailer/lib/action_mailer.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 55ddbb24f48..66b07c39f4b 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -32,7 +32,6 @@ module ActionMailer autoload :AdvAttrAccessor autoload :Base - autoload :DeliveryMethod autoload :DeprecatedBody autoload :MailHelper autoload :Quoting From 9a3a0d15fcf527894bec03b9e226c02bdc800f3e Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar Date: Sun, 17 Jan 2010 21:02:38 +1100 Subject: [PATCH 04/63] Updating mail require to 2.0.0 --- actionmailer/actionmailer.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec index 96549bf29c5..b860f3455a9 100644 --- a/actionmailer/actionmailer.gemspec +++ b/actionmailer/actionmailer.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |s| s.homepage = "http://www.rubyonrails.org" s.add_dependency('actionpack', '= 3.0.pre') - s.add_dependency('mail', '~> 1.6.0') + s.add_dependency('mail', '~> 2.0.0') s.files = Dir['CHANGELOG', 'README', 'MIT-LICENSE', 'lib/**/*'] s.has_rdoc = true From ccb7d9def3c20037c9ed5989d8cae1ed68763f4f Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar Date: Sun, 17 Jan 2010 23:27:59 +1100 Subject: [PATCH 05/63] Fixing up base to refactor settings --- actionmailer/lib/action_mailer/base.rb | 70 ++++++++++---------------- actionmailer/test/mail_service_test.rb | 8 +-- 2 files changed, 31 insertions(+), 47 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 89bd45a994a..c1cce733034 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -361,39 +361,26 @@ module ActionMailer #:nodoc: end attr_writer :mailer_name - def file_settings - @file_settings ||= {:location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"} + def delivery_settings + @delivery_settings ||= { :file => { :location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails" }, + :smtp => { :address => "localhost", + :port => 25, + :domain => 'localhost.localdomain', + :user_name => nil, + :password => nil, + :authentication => nil, + :enable_starttls_auto => true }, + :sendmail => { :location => '/usr/sbin/sendmail', + :arguments => '-i -t' } + } end - attr_writer :file_settings - - def sendmail_settings - @sendmail_settings ||= { :location => '/usr/sbin/sendmail', - :arguments => '-i -t' } - end - attr_writer :sendmail_settings - - def smtp_settings - @smtp_settings ||= { :address => "localhost", - :port => 25, - :domain => 'localhost.localdomain', - :user_name => nil, - :password => nil, - :authentication => nil, - :enable_starttls_auto => true } - end - attr_writer :smtp_settings - - def custom_settings - @custom_settings ||= {} - end - attr_writer :custom_settings attr_writer :delivery_method alias :controller_path :mailer_name def respond_to?(method_symbol, include_private = false) #:nodoc: - matches_dynamic_method?(method_symbol) || super + matches_dynamic_method?(method_symbol) || matches_settings_method?(method_symbol) || super end def method_missing(method_symbol, *parameters) #:nodoc: @@ -404,6 +391,8 @@ module ActionMailer #:nodoc: when 'new' then nil else super end + elsif match = matches_settings_method?(method_symbol) + delivery_settings[match[1].to_sym] = parameters[0] else super end @@ -441,7 +430,8 @@ module ActionMailer #:nodoc: ActiveSupport::Notifications.instrument("action_mailer.deliver", :mailer => self.name) do |payload| set_payload_for_mail(payload, mail) - mail.delivery_method delivery_method, delivery_settings + + mail.delivery_method delivery_method, get_delivery_settings(delivery_method) if @@perform_deliveries mail.deliver! self.deliveries << mail @@ -454,18 +444,6 @@ module ActionMailer #:nodoc: mail end - # Get the delivery settings set. This is set using the :smtp_settings, - # :sendmail_settings, :file_settings or :custom_setings - # options hashes. You can set :custom_settings if you are providing - # your own Custom Delivery Method and want to pass options to it. - def delivery_settings - if [:smtp, :sendmail, :file].include?(delivery_method) - instance_variable_get("@#{delivery_method}_settings") - else - @custom_settings - end - end - def template_root self.view_paths && self.view_paths.first end @@ -487,6 +465,16 @@ module ActionMailer #:nodoc: end private + + def get_delivery_settings(method) #:nodoc: + method.is_a?(Symbol) ? delivery_settings[method] : delivery_settings[:custom] + end + + def matches_settings_method?(method_name) #:nodoc: + method_name = method_name.to_s + delivery_method.is_a?(Symbol) ? method = delivery_method : method = :custom + /(file|sendmail|smtp)_settings$/.match(method_name) + end def matches_dynamic_method?(method_name) #:nodoc: method_name = method_name.to_s @@ -572,10 +560,6 @@ module ActionMailer #:nodoc: @mailer_name ||= self.class.mailer_name.dup @delivery_method = self.class.delivery_method - @smtp_settings = self.class.smtp_settings.dup - @sendmail_settings = self.class.sendmail_settings.dup - @file_settings = self.class.file_settings.dup - @custom_settings = self.class.custom_settings.dup @template ||= method_name @parts ||= [] diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index c14dd644cd7..e53d8a38ef2 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -1092,7 +1092,7 @@ EOF end def test_starttls_is_enabled_if_supported - ActionMailer::Base.smtp_settings[:enable_starttls_auto] = true + ActionMailer::Base.delivery_settings[:smtp].merge!(:enable_starttls_auto => true) MockSMTP.any_instance.expects(:respond_to?).with(:enable_starttls_auto).returns(true) MockSMTP.any_instance.expects(:enable_starttls_auto) ActionMailer::Base.delivery_method = :smtp @@ -1100,7 +1100,7 @@ EOF end def test_starttls_is_disabled_if_not_supported - ActionMailer::Base.smtp_settings[:enable_starttls_auto] = true + ActionMailer::Base.delivery_settings[:smtp].merge!(:enable_starttls_auto => true) MockSMTP.any_instance.expects(:respond_to?).with(:enable_starttls_auto).returns(false) MockSMTP.any_instance.expects(:enable_starttls_auto).never ActionMailer::Base.delivery_method = :smtp @@ -1108,12 +1108,12 @@ EOF end def test_starttls_is_not_enabled - ActionMailer::Base.smtp_settings[:enable_starttls_auto] = false + ActionMailer::Base.delivery_settings[:smtp].merge!(:enable_starttls_auto => false) MockSMTP.any_instance.expects(:respond_to?).never ActionMailer::Base.delivery_method = :smtp TestMailer.deliver_signed_up(@recipient) ensure - ActionMailer::Base.smtp_settings[:enable_starttls_auto] = true + ActionMailer::Base.delivery_settings[:smtp].merge!(:enable_starttls_auto => true) end end From d201d39437c754fa38b14dd4168fab601399918e Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar Date: Tue, 19 Jan 2010 23:09:46 +1100 Subject: [PATCH 06/63] latest updates --- actionmailer/lib/action_mailer/base.rb | 22 ++++++++++------------ actionmailer/test/mail_layout_test.rb | 1 + 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index e0a99fa00cf..fd251ff223d 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -354,6 +354,8 @@ module ActionMailer #:nodoc: # Alias controller_path to mailer_name so render :partial in views work. alias :controller_path :mailer_name + class_inheritable_accessor :delivery_method + class << self def mailer_name @@ -361,21 +363,17 @@ module ActionMailer #:nodoc: end attr_writer :mailer_name + # Mail uses the same defaults as Rails, except for the file delivery method + # save location so we just add this here. def delivery_settings - @delivery_settings ||= { :file => { :location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails" }, - :smtp => { :address => "localhost", - :port => 25, - :domain => 'localhost.localdomain', - :user_name => nil, - :password => nil, - :authentication => nil, - :enable_starttls_auto => true }, - :sendmail => { :location => '/usr/sbin/sendmail', - :arguments => '-i -t' } - } + @delivery_settings ||= {:file => {:location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"}} end - attr_writer :delivery_method + # Setup the default settings for Mail delivery + Mail.defaults do + method = ActionMailer::Base.delivery_method ||= :smtp + delivery_method method + end alias :controller_path :mailer_name diff --git a/actionmailer/test/mail_layout_test.rb b/actionmailer/test/mail_layout_test.rb index 0877e7b2cb3..b9ff0754616 100644 --- a/actionmailer/test/mail_layout_test.rb +++ b/actionmailer/test/mail_layout_test.rb @@ -1,6 +1,7 @@ require 'abstract_unit' class AutoLayoutMailer < ActionMailer::Base + def hello(recipient) recipients recipient subject "You have a mail" From c8e2998701653df9035232778d60f18c4a07abb4 Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar Date: Tue, 19 Jan 2010 23:36:49 +1100 Subject: [PATCH 07/63] First pass on fixing delivery method --- actionmailer/lib/action_mailer/base.rb | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index fd251ff223d..082560e695d 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -354,7 +354,8 @@ module ActionMailer #:nodoc: # Alias controller_path to mailer_name so render :partial in views work. alias :controller_path :mailer_name - class_inheritable_accessor :delivery_method + superclass_delegating_accessor :delivery_method + self.delivery_method = :smtp class << self @@ -369,12 +370,6 @@ module ActionMailer #:nodoc: @delivery_settings ||= {:file => {:location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"}} end - # Setup the default settings for Mail delivery - Mail.defaults do - method = ActionMailer::Base.delivery_method ||= :smtp - delivery_method method - end - alias :controller_path :mailer_name def respond_to?(method_symbol, include_private = false) #:nodoc: @@ -390,6 +385,7 @@ module ActionMailer #:nodoc: else super end elsif match = matches_settings_method?(method_symbol) + # TODO Deprecation warning delivery_settings[match[1].to_sym] = parameters[0] else super @@ -463,7 +459,7 @@ module ActionMailer #:nodoc: end private - + def get_delivery_settings(method) #:nodoc: method.is_a?(Symbol) ? delivery_settings[method] : delivery_settings[:custom] end From c1848f9736d9a4a45181642106acecb6a83a45a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 19 Jan 2010 14:28:04 +0100 Subject: [PATCH 08/63] Get all tests passing. --- actionmailer/lib/action_mailer/base.rb | 36 +++++++++++++++++++++----- actionmailer/test/mail_service_test.rb | 9 ++----- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 082560e695d..5ece35e69bf 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -367,7 +367,29 @@ module ActionMailer #:nodoc: # Mail uses the same defaults as Rails, except for the file delivery method # save location so we just add this here. def delivery_settings - @delivery_settings ||= {:file => {:location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"}} + @@delivery_settings ||= begin + hash = Hash.new { |h,k| h[k] = {} } + hash[:file] = { + :location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails" + } + + hash[:smtp] = { + :address => "localhost", + :port => 25, + :domain => 'localhost.localdomain', + :user_name => nil, + :password => nil, + :authentication => nil, + :enable_starttls_auto => true + } + + hash[:sendmail] = { + :location => '/usr/sbin/sendmail', + :arguments => '-i -t' + } + + hash + end end alias :controller_path :mailer_name @@ -386,7 +408,11 @@ module ActionMailer #:nodoc: end elsif match = matches_settings_method?(method_symbol) # TODO Deprecation warning - delivery_settings[match[1].to_sym] = parameters[0] + if match[2] + delivery_settings[match[1].to_sym] = parameters[0] + else + delivery_settings[match[1].to_sym] + end else super end @@ -461,13 +487,11 @@ module ActionMailer #:nodoc: private def get_delivery_settings(method) #:nodoc: - method.is_a?(Symbol) ? delivery_settings[method] : delivery_settings[:custom] + delivery_settings[method] end def matches_settings_method?(method_name) #:nodoc: - method_name = method_name.to_s - delivery_method.is_a?(Symbol) ? method = delivery_method : method = :custom - /(file|sendmail|smtp)_settings$/.match(method_name) + /(\w+)_settings(=)?$/.match(method_name.to_s) end def matches_dynamic_method?(method_name) #:nodoc: diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index 17e7992e299..7b5f8b1ffcc 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -680,7 +680,7 @@ class ActionMailerTest < Test::Unit::TestCase def test_performs_delivery_via_sendmail IO.expects(:popen).once.with('/usr/sbin/sendmail -i -t test@localhost', 'w+') - TestMailer.delivery_method = :sendmail + ActionMailer::Base.delivery_method = :sendmail TestMailer.deliver_signed_up(@recipient) end @@ -1069,12 +1069,7 @@ EOF def test_return_path_with_create mail = TestMailer.create_return_path - assert_equal "another@somewhere.test", mail['return-path'].to_s - end - - def test_return_path_with_create - mail = TestMailer.create_return_path - assert_equal ["another@somewhere.test"], mail.return_path + assert_equal "another@somewhere.test", mail.return_path end def test_return_path_with_deliver From e10f51b6b7b6260824cc86085be49cae216cf06c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 19 Jan 2010 15:34:58 +0100 Subject: [PATCH 09/63] Refactor delivery methods. --- actionmailer/lib/action_mailer.rb | 1 + actionmailer/lib/action_mailer/base.rb | 81 ++---------- .../lib/action_mailer/delivery_methods.rb | 71 ++++++++++ .../lib/action_mailer/deprecated_body.rb | 10 +- actionmailer/test/delivery_method_test.rb | 123 ++++-------------- 5 files changed, 117 insertions(+), 169 deletions(-) create mode 100644 actionmailer/lib/action_mailer/delivery_methods.rb diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 66b07c39f4b..f1c94e9e69c 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -32,6 +32,7 @@ module ActionMailer autoload :AdvAttrAccessor autoload :Base + autoload :DeliveryMethods autoload :DeprecatedBody autoload :MailHelper autoload :Quoting diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 5ece35e69bf..be6d93316f1 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -264,6 +264,8 @@ module ActionMailer #:nodoc: helper ActionMailer::MailHelper include ActionMailer::DeprecatedBody + include ActionMailer::DeliveryMethods + private_class_method :new #:nodoc: @@raise_delivery_errors = true @@ -354,9 +356,6 @@ module ActionMailer #:nodoc: # Alias controller_path to mailer_name so render :partial in views work. alias :controller_path :mailer_name - superclass_delegating_accessor :delivery_method - self.delivery_method = :smtp - class << self def mailer_name @@ -364,38 +363,10 @@ module ActionMailer #:nodoc: end attr_writer :mailer_name - # Mail uses the same defaults as Rails, except for the file delivery method - # save location so we just add this here. - def delivery_settings - @@delivery_settings ||= begin - hash = Hash.new { |h,k| h[k] = {} } - hash[:file] = { - :location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails" - } - - hash[:smtp] = { - :address => "localhost", - :port => 25, - :domain => 'localhost.localdomain', - :user_name => nil, - :password => nil, - :authentication => nil, - :enable_starttls_auto => true - } - - hash[:sendmail] = { - :location => '/usr/sbin/sendmail', - :arguments => '-i -t' - } - - hash - end - end - alias :controller_path :mailer_name def respond_to?(method_symbol, include_private = false) #:nodoc: - matches_dynamic_method?(method_symbol) || matches_settings_method?(method_symbol) || super + matches_dynamic_method?(method_symbol) || super end def method_missing(method_symbol, *parameters) #:nodoc: @@ -406,13 +377,6 @@ module ActionMailer #:nodoc: when 'new' then nil else super end - elsif match = matches_settings_method?(method_symbol) - # TODO Deprecation warning - if match[2] - delivery_settings[match[1].to_sym] = parameters[0] - else - delivery_settings[match[1].to_sym] - end else super end @@ -447,11 +411,13 @@ module ActionMailer #:nodoc: raise "no mail object available for delivery!" unless mail begin - ActiveSupport::Notifications.instrument("action_mailer.deliver", - :mailer => self.name) do |payload| + ActiveSupport::Notifications.instrument("action_mailer.deliver", :mailer => self.name) do |payload| set_payload_for_mail(payload, mail) - - mail.delivery_method delivery_method, get_delivery_settings(delivery_method) + + # TODO Move me to the instance + mail.delivery_method delivery_methods[delivery_method], + delivery_settings[delivery_method] + if @@perform_deliveries mail.deliver! self.deliveries << mail @@ -486,25 +452,12 @@ module ActionMailer #:nodoc: private - def get_delivery_settings(method) #:nodoc: - delivery_settings[method] - end - - def matches_settings_method?(method_name) #:nodoc: - /(\w+)_settings(=)?$/.match(method_name.to_s) - end - def matches_dynamic_method?(method_name) #:nodoc: method_name = method_name.to_s /^(create|deliver)_([_a-z]\w*)/.match(method_name) || /^(new)$/.match(method_name) end end - # Configure delivery method. Check ActionMailer::DeliveryMethod for more - # instructions. - superclass_delegating_reader :delivery_method - self.delivery_method = :smtp - # Add a part to a multipart message, with the given content-type. The # part itself is yielded to the block so that other properties (charset, # body, headers, etc.) can be set on it. @@ -534,20 +487,6 @@ module ActionMailer #:nodoc: part(params, &block) end - # Allow you to set assigns for your template: - # - # body :greetings => "Hi" - # - # Will make @greetings available in the template to be rendered. - def body(object=nil) - returning(super) do # Run deprecation hooks - if object.is_a?(Hash) - @assigns_set = true - object.each { |k, v| instance_variable_set(:"@#{k}", v) } - end - end - end - # Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer # will be initialized according to the named method. If not, the mailer will # remain uninitialized (useful when you only need to invoke the "receive" @@ -591,6 +530,8 @@ module ActionMailer #:nodoc: # render_message "special_message" # render_message :template => "special_message" # render_message :inline => "<%= 'Hi!' %>" + # + # TODO Deprecate me def render_message(object) case object when String diff --git a/actionmailer/lib/action_mailer/delivery_methods.rb b/actionmailer/lib/action_mailer/delivery_methods.rb new file mode 100644 index 00000000000..c8c4148353c --- /dev/null +++ b/actionmailer/lib/action_mailer/delivery_methods.rb @@ -0,0 +1,71 @@ +module ActionMailer + # This modules makes a DSL for adding delivery methods to ActionMailer + module DeliveryMethods + extend ActiveSupport::Concern + + included do + add_delivery_method :smtp, Mail::SMTP, + :address => "localhost", + :port => 25, + :domain => 'localhost.localdomain', + :user_name => nil, + :password => nil, + :authentication => nil, + :enable_starttls_auto => true + + add_delivery_method :file, Mail::FileDelivery, + :location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails" + + add_delivery_method :sendmail, Mail::Sendmail, + :location => '/usr/sbin/sendmail', + :arguments => '-i -t' + + add_delivery_method :test, Mail::TestMailer + + superclass_delegating_reader :delivery_method + self.delivery_method = :smtp + end + + module ClassMethods + def delivery_settings + @@delivery_settings ||= Hash.new { |h,k| h[k] = {} } + end + + def delivery_methods + @@delivery_methods ||= {} + end + + def delivery_method=(method) + raise ArgumentError, "Unknown delivery method #{method.inspect}" unless delivery_methods[method] + @delivery_method = method + end + + def add_delivery_method(symbol, klass, default_options={}) + self.delivery_methods[symbol] = klass + self.delivery_settings[symbol] = default_options + end + + def respond_to?(method_symbol, include_private = false) #:nodoc: + matches_settings_method?(method_symbol) || super + end + + protected + + def method_missing(method_symbol, *parameters) #:nodoc: + if match = matches_settings_method?(method_symbol) + if match[2] + delivery_settings[match[1].to_sym] = parameters[0] + else + delivery_settings[match[1].to_sym] + end + else + super + end + end + + def matches_settings_method?(method_name) #:nodoc: + /(#{delivery_methods.keys.join('|')})_settings(=)?$/.match(method_name.to_s) + end + end + end +end \ No newline at end of file diff --git a/actionmailer/lib/action_mailer/deprecated_body.rb b/actionmailer/lib/action_mailer/deprecated_body.rb index 5379b33a54b..c82610014f3 100644 --- a/actionmailer/lib/action_mailer/deprecated_body.rb +++ b/actionmailer/lib/action_mailer/deprecated_body.rb @@ -1,6 +1,6 @@ module ActionMailer # TODO Remove this module all together in a next release. Ensure that super - # hooks and @assigns_set in ActionMailer::Base are removed as well. + # hooks in ActionMailer::Base are removed as well. module DeprecatedBody extend ActionMailer::AdvAttrAccessor @@ -22,12 +22,14 @@ module ActionMailer end def create_parts - if String === @body && !defined?(@assigns_set) + if String === @body ActiveSupport::Deprecation.warn('body(String) is deprecated. To set the body with a text ' << 'call render(:text => "body")', caller[0,10]) self.response_body = @body - elsif self.response_body - @body = self.response_body + elsif @body.is_a?(Hash) && !@body.empty? + ActiveSupport::Deprecation.warn('body(Hash) is deprecated. Use instance variables to define ' << + 'assigns in your view', caller[0,10]) + @body.each { |k, v| instance_variable_set(:"@#{k}", v) } end end diff --git a/actionmailer/test/delivery_method_test.rb b/actionmailer/test/delivery_method_test.rb index fb43086423c..1e7408d6d6f 100644 --- a/actionmailer/test/delivery_method_test.rb +++ b/actionmailer/test/delivery_method_test.rb @@ -1,41 +1,14 @@ require 'abstract_unit' require 'mail' -class DefaultDeliveryMethodMailer < ActionMailer::Base +class MyCustomDelivery end -class NonDefaultDeliveryMethodMailer < ActionMailer::Base - self.delivery_method = :sendmail -end - -class FileDeliveryMethodMailer < ActionMailer::Base - self.delivery_method = :file -end - -class CustomDeliveryMethod - - def initialize(values) - @custom_deliveries = [] - end - - attr_accessor :custom_deliveries - - attr_accessor :settings - - def deliver!(mail) - self.custom_deliveries << mail - end -end - -class CustomerDeliveryMailer < ActionMailer::Base - self.delivery_method = CustomDeliveryMethod -end - -class ActionMailerBase_delivery_method_Test < Test::Unit::TestCase +class DefaultsDeliveryMethodsTest < ActionMailer::TestCase def setup set_delivery_method :smtp end - + def teardown restore_delivery_method end @@ -54,87 +27,47 @@ class ActionMailerBase_delivery_method_Test < Test::Unit::TestCase :enable_starttls_auto => true } assert_equal settings, ActionMailer::Base.smtp_settings end -end -class DefaultDeliveryMethodMailer_delivery_method_Test < Test::Unit::TestCase - def setup - set_delivery_method :smtp - end - - def teardown - restore_delivery_method - end - - def test_should_be_the_default_smtp - assert_equal :smtp, DefaultDeliveryMethodMailer.delivery_method - end - - def test_should_have_default_smtp_delivery_method_settings - settings = { :address => "localhost", - :port => 25, - :domain => 'localhost.localdomain', - :user_name => nil, - :password => nil, - :authentication => nil, - :enable_starttls_auto => true } - assert_equal settings, DefaultDeliveryMethodMailer.smtp_settings - end -end - -class NonDefaultDeliveryMethodMailer_delivery_method_Test < Test::Unit::TestCase - def setup - set_delivery_method :smtp - end - - def teardown - restore_delivery_method - end - - def test_should_be_the_set_delivery_method - assert_equal :sendmail, NonDefaultDeliveryMethodMailer.delivery_method + def test_should_have_default_file_delivery_method_settings + settings = {:location => "#{Dir.tmpdir}/mails"} + assert_equal settings, ActionMailer::Base.file_settings end def test_should_have_default_sendmail_delivery_method_settings settings = {:location => '/usr/sbin/sendmail', :arguments => '-i -t'} - assert_equal settings, NonDefaultDeliveryMethodMailer.sendmail_settings + assert_equal settings, ActionMailer::Base.sendmail_settings end end -class FileDeliveryMethodMailer_delivery_method_Test < Test::Unit::TestCase +class CustomDeliveryMethodsTest < ActionMailer::TestCase def setup - set_delivery_method :smtp + ActionMailer::Base.add_delivery_method :custom, MyCustomDelivery end def teardown - restore_delivery_method + ActionMailer::Base.delivery_methods.delete(:custom) + ActionMailer::Base.delivery_settings.delete(:custom) end - def test_should_be_the_set_delivery_method - assert_equal :file, FileDeliveryMethodMailer.delivery_method + def test_allow_to_add_a_custom_delivery_method + ActionMailer::Base.delivery_method = :custom + assert_equal :custom, ActionMailer::Base.delivery_method end - def test_should_have_default_file_delivery_method_settings - settings = {:location => "#{Dir.tmpdir}/mails"} - assert_equal settings, FileDeliveryMethodMailer.file_settings - end -end - -class CustomDeliveryMethodMailer_delivery_method_Test < Test::Unit::TestCase - def setup - set_delivery_method :smtp - end - - def teardown - restore_delivery_method - end - - def test_should_be_the_set_delivery_method - assert_equal CustomDeliveryMethod, CustomerDeliveryMailer.delivery_method - end - - def test_should_have_default_custom_delivery_method_settings - settings = {} - assert_equal settings, CustomerDeliveryMailer.custom_settings + def test_allow_to_customize_custom_settings + ActionMailer::Base.custom_settings = { :foo => :bar } + assert_equal Hash[:foo => :bar], ActionMailer::Base.custom_settings + end + + def test_respond_to_custom_method_settings + assert_respond_to ActionMailer::Base, :custom_settings + assert_respond_to ActionMailer::Base, :custom_settings= + end + + def test_should_not_respond_for_invalid_method_settings + assert_raise NoMethodError do + ActionMailer::Base.another_settings + end end end From 2a3ec5fee447a9b3729d04499d9e72b44079faef Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar Date: Wed, 20 Jan 2010 14:12:05 +1100 Subject: [PATCH 10/63] Updating gemspec to 2.0.1 for mail --- actionmailer/actionmailer.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec index b860f3455a9..cc1c25401ee 100644 --- a/actionmailer/actionmailer.gemspec +++ b/actionmailer/actionmailer.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |s| s.homepage = "http://www.rubyonrails.org" s.add_dependency('actionpack', '= 3.0.pre') - s.add_dependency('mail', '~> 2.0.0') + s.add_dependency('mail', '~> 2.0.1') s.files = Dir['CHANGELOG', 'README', 'MIT-LICENSE', 'lib/**/*'] s.has_rdoc = true From c04baed627c85e586e337896d64f61f397554a46 Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar Date: Wed, 20 Jan 2010 14:12:17 +1100 Subject: [PATCH 11/63] Fixing failing test on sendmail expectation --- actionmailer/test/mail_service_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index 7b5f8b1ffcc..396dd00a914 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -679,7 +679,7 @@ class ActionMailerTest < Test::Unit::TestCase end def test_performs_delivery_via_sendmail - IO.expects(:popen).once.with('/usr/sbin/sendmail -i -t test@localhost', 'w+') + IO.expects(:popen).once.with('/usr/sbin/sendmail -i -t -f "system@loudthinking.com" test@localhost', 'w+') ActionMailer::Base.delivery_method = :sendmail TestMailer.deliver_signed_up(@recipient) end From 10c509fbfa70758ece26e8876d1c5c28f659f2f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Wed, 20 Jan 2010 22:25:09 +1100 Subject: [PATCH 12/63] Moved old API into deprecated_api.rb in preparation for new Rails 3 Mailer API --- actionmailer/lib/action_mailer.rb | 1 + .../lib/action_mailer/adv_attr_accessor.rb | 2 + actionmailer/lib/action_mailer/base.rb | 156 +---------------- .../lib/action_mailer/deprecated_api.rb | 163 ++++++++++++++++++ actionmailer/test/base_test.rb | 39 +++++ 5 files changed, 208 insertions(+), 153 deletions(-) create mode 100644 actionmailer/lib/action_mailer/deprecated_api.rb create mode 100644 actionmailer/test/base_test.rb diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index f1c94e9e69c..1765aee9cc5 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -34,6 +34,7 @@ module ActionMailer autoload :Base autoload :DeliveryMethods autoload :DeprecatedBody + autoload :DeprecatedApi autoload :MailHelper autoload :Quoting autoload :TestCase diff --git a/actionmailer/lib/action_mailer/adv_attr_accessor.rb b/actionmailer/lib/action_mailer/adv_attr_accessor.rb index be6b1feca98..91992ed8396 100644 --- a/actionmailer/lib/action_mailer/adv_attr_accessor.rb +++ b/actionmailer/lib/action_mailer/adv_attr_accessor.rb @@ -1,6 +1,8 @@ module ActionMailer module AdvAttrAccessor #:nodoc: def adv_attr_accessor(*names) + + # TODO: ActiveSupport::Deprecation.warn() names.each do |name| ivar = "@#{name}" diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index e105beef638..9df482cdf6d 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -263,9 +263,11 @@ module ActionMailer #:nodoc: helper ActionMailer::MailHelper include ActionMailer::DeprecatedBody + include ActionMailer::DeprecatedApi include ActionMailer::DeliveryMethods + private_class_method :new #:nodoc: @@raise_delivery_errors = true @@ -459,41 +461,13 @@ module ActionMailer #:nodoc: end end - # Add a part to a multipart message, with the given content-type. The - # part itself is yielded to the block so that other properties (charset, - # body, headers, etc.) can be set on it. - def part(params) - params = {:content_type => params} if String === params - - if custom_headers = params.delete(:headers) - ActiveSupport::Deprecation.warn('Passing custom headers with :headers => {} is deprecated. ' << - 'Please just pass in custom headers directly.', caller[0,10]) - params.merge!(custom_headers) - end - - part = Mail::Part.new(params) - yield part if block_given? - @parts << part - end - - # Add an attachment to a multipart message. This is simply a part with the - # content-disposition set to "attachment". - def attachment(params, &block) - super # Run deprecation hooks - - params = { :content_type => params } if String === params - params = { :content_disposition => "attachment", - :content_transfer_encoding => "base64" }.merge(params) - - part(params, &block) - end - # Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer # 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) super() + @mail = Mail.new process(method_name, *args) if method_name end @@ -521,129 +495,5 @@ module ActionMailer #:nodoc: self.class.deliver(mail) end - private - - # Render a message but does not set it as mail body. Useful for rendering - # data for part and attachments. - # - # Examples: - # - # render_message "special_message" - # render_message :template => "special_message" - # render_message :inline => "<%= 'Hi!' %>" - # - # TODO Deprecate me - def render_message(object) - case object - when String - render_to_body(:template => object) - else - render_to_body(object) - end - end - - # Set up the default values for the various instance variables of this - # mailer. Subclasses may override this method to provide different - # defaults. - def initialize_defaults(method_name) #:nodoc: - @charset ||= @@default_charset.dup - @content_type ||= @@default_content_type.dup - @implicit_parts_order ||= @@default_implicit_parts_order.dup - @mime_version ||= @@default_mime_version.dup if @@default_mime_version - - @mailer_name ||= self.class.mailer_name.dup - @delivery_method = self.class.delivery_method - @template ||= method_name - - @parts ||= [] - @headers ||= {} - @sent_on ||= Time.now - - super # Run deprecation hooks - end - - def create_parts #:nodoc: - super # Run deprecation hooks - - if String === response_body - @parts.unshift create_inline_part(response_body) - else - self.class.template_root.find_all(@template, {}, @mailer_name).each do |template| - @parts << create_inline_part(render_to_body(:_template => template), template.mime_type) - end - - if @parts.size > 1 - @content_type = "multipart/alternative" if @content_type !~ /^multipart/ - end - - # If this is a multipart e-mail add the mime_version if it is not - # already set. - @mime_version ||= "1.0" if !@parts.empty? - end - end - - def create_inline_part(body, mime_type=nil) #:nodoc: - ct = mime_type || "text/plain" - main_type, sub_type = split_content_type(ct.to_s) - - Mail::Part.new( - :content_type => [main_type, sub_type, {:charset => charset}], - :content_disposition => "inline", - :body => body - ) - end - - def create_mail #:nodoc: - m = Mail.new - - m.subject, = quote_any_if_necessary(charset, subject) - m.to, m.from = quote_any_address_if_necessary(charset, recipients, from) - m.bcc = quote_address_if_necessary(bcc, charset) unless bcc.nil? - m.cc = quote_address_if_necessary(cc, charset) unless cc.nil? - m.reply_to = quote_address_if_necessary(reply_to, charset) unless reply_to.nil? - m.mime_version = mime_version unless mime_version.nil? - m.date = sent_on.to_time rescue sent_on if sent_on - - headers.each { |k, v| m[k] = v } - - real_content_type, ctype_attrs = parse_content_type - main_type, sub_type = split_content_type(real_content_type) - - if @parts.size == 1 && @parts.first.parts.empty? - m.content_type([main_type, sub_type, ctype_attrs]) - m.body = @parts.first.body.encoded - else - @parts.each do |p| - m.add_part(p) - end - - m.body.set_sort_order(@implicit_parts_order) - m.body.sort_parts! - - if real_content_type =~ /multipart/ - ctype_attrs.delete "charset" - m.content_type([main_type, sub_type, ctype_attrs]) - end - end - - m.content_transfer_encoding = '8bit' unless m.body.only_us_ascii? - - @mail = m - end - - def split_content_type(ct) #:nodoc: - ct.to_s.split("/") - end - - def parse_content_type(defaults=nil) #:nodoc: - if @content_type.blank? - [ nil, {} ] - else - ctype, *attrs = @content_type.split(/;\s*/) - attrs = attrs.inject({}) { |h,s| k,v = s.split(/\=/, 2); h[k] = v; h } - [ctype, {"charset" => @charset}.merge(attrs)] - end - end - end end diff --git a/actionmailer/lib/action_mailer/deprecated_api.rb b/actionmailer/lib/action_mailer/deprecated_api.rb new file mode 100644 index 00000000000..3334b479874 --- /dev/null +++ b/actionmailer/lib/action_mailer/deprecated_api.rb @@ -0,0 +1,163 @@ +module ActionMailer + # TODO Remove this module all together in Rails 3.1. Ensure that super + # hooks in ActionMailer::Base are removed as well. + # + # Moved here to allow us to add the new Mail API + module DeprecatedApi + extend ActionMailer::AdvAttrAccessor + + # Add a part to a multipart message, with the given content-type. The + # part itself is yielded to the block so that other properties (charset, + # body, headers, etc.) can be set on it. + def part(params) + params = {:content_type => params} if String === params + + if custom_headers = params.delete(:headers) + ActiveSupport::Deprecation.warn('Passing custom headers with :headers => {} is deprecated. ' << + 'Please just pass in custom headers directly.', caller[0,10]) + params.merge!(custom_headers) + end + + part = Mail::Part.new(params) + yield part if block_given? + @parts << part + end + + # Add an attachment to a multipart message. This is simply a part with the + # content-disposition set to "attachment". + def attachment(params, &block) + super # Run deprecation hooks + + params = { :content_type => params } if String === params + params = { :content_disposition => "attachment", + :content_transfer_encoding => "base64" }.merge(params) + + part(params, &block) + end + + private + + def create_mail #:nodoc: + m = @mail + + m.subject, = quote_any_if_necessary(charset, subject) + m.to, m.from = quote_any_address_if_necessary(charset, recipients, from) + m.bcc = quote_address_if_necessary(bcc, charset) unless bcc.nil? + m.cc = quote_address_if_necessary(cc, charset) unless cc.nil? + m.reply_to = quote_address_if_necessary(reply_to, charset) unless reply_to.nil? + m.mime_version = mime_version unless mime_version.nil? + m.date = sent_on.to_time rescue sent_on if sent_on + + headers.each { |k, v| m[k] = v } + + real_content_type, ctype_attrs = parse_content_type + main_type, sub_type = split_content_type(real_content_type) + + if @parts.size == 1 && @parts.first.parts.empty? + m.content_type([main_type, sub_type, ctype_attrs]) + m.body = @parts.first.body.encoded + else + @parts.each do |p| + m.add_part(p) + end + + m.body.set_sort_order(@implicit_parts_order) + m.body.sort_parts! + + if real_content_type =~ /multipart/ + ctype_attrs.delete "charset" + m.content_type([main_type, sub_type, ctype_attrs]) + end + end + + m.content_transfer_encoding = '8bit' unless m.body.only_us_ascii? + + @mail + end + + # Render a message but does not set it as mail body. Useful for rendering + # data for part and attachments. + # + # Examples: + # + # render_message "special_message" + # render_message :template => "special_message" + # render_message :inline => "<%= 'Hi!' %>" + # + # TODO Deprecate me + def render_message(object) + case object + when String + render_to_body(:template => object) + else + render_to_body(object) + end + end + + # Set up the default values for the various instance variables of this + # mailer. Subclasses may override this method to provide different + # defaults. + def initialize_defaults(method_name) #:nodoc: + @charset ||= self.class.default_charset.dup + @content_type ||= self.class.default_content_type.dup + @implicit_parts_order ||= self.class.default_implicit_parts_order.dup + @mime_version ||= self.class.default_mime_version.dup if self.class.default_mime_version + + @mailer_name ||= self.class.mailer_name.dup + @delivery_method = self.class.delivery_method + @template ||= method_name + + @parts ||= [] + @headers ||= {} + @sent_on ||= Time.now + + super # Run deprecation hooks + end + + def create_parts #:nodoc: + super # Run deprecation hooks + + if String === response_body + @parts.unshift create_inline_part(response_body) + else + self.class.template_root.find_all(@template, {}, @mailer_name).each do |template| + @parts << create_inline_part(render_to_body(:_template => template), template.mime_type) + end + + if @parts.size > 1 + @content_type = "multipart/alternative" if @content_type !~ /^multipart/ + end + + # If this is a multipart e-mail add the mime_version if it is not + # already set. + @mime_version ||= "1.0" if !@parts.empty? + end + end + + def create_inline_part(body, mime_type=nil) #:nodoc: + ct = mime_type || "text/plain" + main_type, sub_type = split_content_type(ct.to_s) + + Mail::Part.new( + :content_type => [main_type, sub_type, {:charset => charset}], + :content_disposition => "inline", + :body => body + ) + end + + def split_content_type(ct) #:nodoc: + ct.to_s.split("/") + end + + def parse_content_type(defaults=nil) #:nodoc: + if @content_type.blank? + [ nil, {} ] + else + ctype, *attrs = @content_type.split(/;\s*/) + attrs = attrs.inject({}) { |h,s| k,v = s.split(/\=/, 2); h[k] = v; h } + [ctype, {"charset" => @charset}.merge(attrs)] + end + end + + end +end \ No newline at end of file diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb new file mode 100644 index 00000000000..dc1fbc07be0 --- /dev/null +++ b/actionmailer/test/base_test.rb @@ -0,0 +1,39 @@ +# class Notifier < ActionMailer::Base +# delivers_from 'notifications@example.com' +# +# def welcome(user) +# @user = user # available to the view +# mail(:subject => 'Welcome!', :to => user.email_address) +# # auto renders both welcome.text.erb and welcome.html.erb +# end +# +# def goodbye(user) +# headers["Reply-To"] = 'cancelations@example.com' +# mail(:subject => 'Goodbye', :to => user.email_address) do |format| +# format.html { render "shared_template "} +# format.text # goodbye.text.erb +# end +# end +# +# def surprise(user, gift) +# attachments[gift.name] = File.read(gift.path) +# mail(:subject => 'Surprise!', :to => user.email_address) do |format| +# format.html(:charset => "ascii") # surprise.html.erb +# format.text(:transfer_encoding => "base64") # surprise.text.erb +# end +# end +# +# def special_surprise(user, gift) +# attachments[gift.name] = { :content_type => "application/x-gzip", :content => File.read(gift.path) } +# mail(:to => 'special@example.com') # subject not required +# # auto renders both special_surprise.text.erb and special_surprise.html.erb +# end +# end +# +# Notifier.welcome(user) # => returns a Mail object +# Notifier.welcome(user).deliver # => creates and sends the Mail in one step +class BaseTest < ActionMailer::Base + + + +end From c34cfcc29f705c95c2218889cbec1898e008335d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Wed, 20 Jan 2010 23:46:59 +1100 Subject: [PATCH 13/63] Created mail method for new API --- actionmailer/lib/action_mailer/base.rb | 59 ++++++++++++++----- .../lib/action_mailer/deprecated_api.rb | 4 +- actionmailer/test/base_test.rb | 46 +++++++++++++-- 3 files changed, 89 insertions(+), 20 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 9df482cdf6d..8825bd35f95 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -297,7 +297,7 @@ module ActionMailer #:nodoc: @@default_implicit_parts_order = [ "text/plain", "text/enriched", "text/html" ] cattr_accessor :default_implicit_parts_order - @@protected_instance_variables = %w(@parts @mail) + @@protected_instance_variables = %w(@parts @message) cattr_reader :protected_instance_variables # Specify the BCC addresses for the message @@ -352,8 +352,8 @@ module ActionMailer #:nodoc: # location, you can use this to specify that location. adv_attr_accessor :mailer_name - # Expose the internal mail - attr_reader :mail + # Expose the internal Mail message + attr_reader :message # Alias controller_path to mailer_name so render :partial in views work. alias :controller_path :mailer_name @@ -374,7 +374,7 @@ module ActionMailer #:nodoc: def method_missing(method_symbol, *parameters) #:nodoc: if match = matches_dynamic_method?(method_symbol) case match[1] - when 'create' then new(match[2], *parameters).mail + when 'create' then new(match[2], *parameters).message when 'deliver' then new(match[2], *parameters).deliver! when 'new' then nil else super @@ -461,13 +461,40 @@ module ActionMailer #:nodoc: end end + def mail(headers = {}) + # Guard flag to prevent both the old and the new API from firing + # TODO - Move this @mail_was_called flag into deprecated_api.rb + @mail_was_called = true + + m = @message + + m.content_type ||= headers[:content_type] || @@default_content_type + m.charset ||= headers[:charset] || @@default_charset + m.mime_version ||= headers[:mime_version] || @@default_mime_version + + m.subject = quote_if_necessary(headers[:subject], m.charset) if headers[:subject] + m.to = quote_address_if_necessary(headers[:to], m.charset) if headers[:to] + m.from = quote_address_if_necessary(headers[:from], m.charset) if headers[:from] + m.cc = quote_address_if_necessary(headers[:cc], m.charset) if headers[:cc] + m.bcc = quote_address_if_necessary(headers[:bcc], m.charset) if headers[:bcc] + m.reply_to = quote_address_if_necessary(headers[:reply_to], m.charset) if headers[:reply_to] + m.mime_version = headers[:mime_version] if headers[:mime_version] + m.date = headers[:date] if headers[:date] + + m.body.set_sort_order(headers[:parts_order] || @@default_implicit_parts_order) + + # TODO: m.body.sort_parts! + m + end + # Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer # 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) super() - @mail = Mail.new + @mail_was_called = false + @message = Mail.new process(method_name, *args) if method_name end @@ -475,23 +502,27 @@ module ActionMailer #:nodoc: # rendered and a new Mail object created. def process(method_name, *args) initialize_defaults(method_name) + super + + unless @mail_was_called + # Create e-mail parts + create_parts - # Create e-mail parts - create_parts + # Set the subject if not set yet + @subject ||= I18n.t(:subject, :scope => [:actionmailer, mailer_name, method_name], + :default => method_name.humanize) - # Set the subject if not set yet - @subject ||= I18n.t(:subject, :scope => [:actionmailer, mailer_name, method_name], - :default => method_name.humanize) - - # Build the mail object itself - create_mail + # Build the mail object itself + create_mail + end + @message end # Delivers a Mail object. By default, it delivers the cached mail # object (from the create! method). If no cached mail object exists, and # no alternate has been given as the parameter, this will fail. - def deliver!(mail = @mail) + def deliver!(mail = @message) self.class.deliver(mail) end diff --git a/actionmailer/lib/action_mailer/deprecated_api.rb b/actionmailer/lib/action_mailer/deprecated_api.rb index 3334b479874..e11dd4c46ae 100644 --- a/actionmailer/lib/action_mailer/deprecated_api.rb +++ b/actionmailer/lib/action_mailer/deprecated_api.rb @@ -38,7 +38,7 @@ module ActionMailer private def create_mail #:nodoc: - m = @mail + m = @message m.subject, = quote_any_if_necessary(charset, subject) m.to, m.from = quote_any_address_if_necessary(charset, recipients, from) @@ -72,7 +72,7 @@ module ActionMailer m.content_transfer_encoding = '8bit' unless m.body.only_us_ascii? - @mail + @message end # Render a message but does not set it as mail body. Useful for rendering diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index dc1fbc07be0..83d51274f0d 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -1,3 +1,6 @@ +# encoding: utf-8 +require 'abstract_unit' + # class Notifier < ActionMailer::Base # delivers_from 'notifications@example.com' # @@ -30,10 +33,45 @@ # end # end # -# Notifier.welcome(user) # => returns a Mail object +# mail = Notifier.welcome(user) # => returns a Mail object +# mail.deliver +# # Notifier.welcome(user).deliver # => creates and sends the Mail in one step -class BaseTest < ActionMailer::Base +class BaseTest < Test::Unit::TestCase + class TestMailer < ActionMailer::Base + def welcome(hash = {}) + hash = {:to => 'mikel@test.lindsaar.net', :from => 'jose@test.plataformatec.com', + :subject => 'The first email on new API!'}.merge!(hash) + mail(hash) + end + end + + def test_the_method_call_to_mail_does_not_raise_error + assert_nothing_raised { TestMailer.deliver_welcome } + end + + def test_should_set_the_headers_of_the_mail_message + email = TestMailer.deliver_welcome + assert_equal(email.to, ['mikel@test.lindsaar.net']) + assert_equal(email.from, ['jose@test.plataformatec.com']) + assert_equal(email.subject, 'The first email on new API!') + end + + def test_calling_mail_should_pass_the_header_hash_to_the_new_mail_object + + end + + def test_it_should_guard_against_old_api_if_mail_method_called + + end + + # def test_that_class_defaults_are_set_on_instantiation + # pending + # end + # + # def test_should_set_the_subject_from_i18n + # pending + # end - -end +end \ No newline at end of file From d3da87ce771845f99bbdc04d6d6587b22655b063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Thu, 21 Jan 2010 00:10:22 +1100 Subject: [PATCH 14/63] Mail method accepting all headers set via the hash --- actionmailer/lib/action_mailer/base.rb | 6 +++-- actionmailer/test/base_test.rb | 32 ++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 8825bd35f95..39ddafe7fef 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/class' +require "active_support/core_ext/module/delegation" require 'mail' require 'action_mailer/tmail_compat' @@ -267,7 +268,6 @@ module ActionMailer #:nodoc: include ActionMailer::DeliveryMethods - private_class_method :new #:nodoc: @@raise_delivery_errors = true @@ -355,6 +355,9 @@ module ActionMailer #:nodoc: # Expose the internal Mail message attr_reader :message + # Pass calls to headers and attachment to the Mail#Message instance + delegate :headers, :attachments, :to => :@message + # Alias controller_path to mailer_name so render :partial in views work. alias :controller_path :mailer_name @@ -478,7 +481,6 @@ module ActionMailer #:nodoc: m.cc = quote_address_if_necessary(headers[:cc], m.charset) if headers[:cc] m.bcc = quote_address_if_necessary(headers[:bcc], m.charset) if headers[:bcc] m.reply_to = quote_address_if_necessary(headers[:reply_to], m.charset) if headers[:reply_to] - m.mime_version = headers[:mime_version] if headers[:mime_version] m.date = headers[:date] if headers[:date] m.body.set_sort_order(headers[:parts_order] || @@default_implicit_parts_order) diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 83d51274f0d..b8d2e46241d 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -11,7 +11,7 @@ require 'abstract_unit' # end # # def goodbye(user) -# headers["Reply-To"] = 'cancelations@example.com' +# headers["X-SPAM"] = 'Not-SPAM' # mail(:subject => 'Goodbye', :to => user.email_address) do |format| # format.html { render "shared_template "} # format.text # goodbye.text.erb @@ -40,11 +40,14 @@ require 'abstract_unit' class BaseTest < Test::Unit::TestCase class TestMailer < ActionMailer::Base + def welcome(hash = {}) + headers['X-SPAM'] = "Not SPAM" hash = {:to => 'mikel@test.lindsaar.net', :from => 'jose@test.plataformatec.com', :subject => 'The first email on new API!'}.merge!(hash) mail(hash) end + end def test_the_method_call_to_mail_does_not_raise_error @@ -57,12 +60,31 @@ class BaseTest < Test::Unit::TestCase assert_equal(email.from, ['jose@test.plataformatec.com']) assert_equal(email.subject, 'The first email on new API!') end - - def test_calling_mail_should_pass_the_header_hash_to_the_new_mail_object - + + def test_should_allow_all_headers_set + @time = Time.now + email = TestMailer.deliver_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(email.bcc, ['bcc@test.lindsaar.net']) + assert_equal(email.cc, ['cc@test.lindsaar.net']) + assert_equal(email.content_type, 'multipart/mixed') + assert_equal(email.charset, 'iso-8559-1') + assert_equal(email.mime_version, '2.0') + assert_equal(email.reply_to, ['reply-to@test.lindsaar.net']) + assert_equal(email.date, @time) end - def test_it_should_guard_against_old_api_if_mail_method_called +# def test_should_allow_custom_headers_to_be_set +# email = TestMailer.deliver_welcome +# assert_equal("Not SPAM", email['X-SPAM']) +# end + + def test_should_use_class_defaults end From 3829f9ecfd6fcd54edbc15f624ee3b68f6dae135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Thu, 21 Jan 2010 20:03:55 +1100 Subject: [PATCH 15/63] Adding tests for attachments['blah.rb'] = {} et al --- actionmailer/actionmailer.gemspec | 2 +- .../lib/action_mailer/deprecated_api.rb | 12 ++++++++- .../lib/action_mailer/tmail_compat.rb | 5 ++++ actionmailer/test/base_test.rb | 27 ++++++++++++++++++- actionmailer/test/mail_service_test.rb | 1 + 5 files changed, 44 insertions(+), 3 deletions(-) diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec index cc1c25401ee..9a8c1df9e88 100644 --- a/actionmailer/actionmailer.gemspec +++ b/actionmailer/actionmailer.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |s| s.homepage = "http://www.rubyonrails.org" s.add_dependency('actionpack', '= 3.0.pre') - s.add_dependency('mail', '~> 2.0.1') + s.add_dependency('mail', '~> 2.0.2') s.files = Dir['CHANGELOG', 'README', 'MIT-LICENSE', 'lib/**/*'] s.has_rdoc = true diff --git a/actionmailer/lib/action_mailer/deprecated_api.rb b/actionmailer/lib/action_mailer/deprecated_api.rb index e11dd4c46ae..d096ea61800 100644 --- a/actionmailer/lib/action_mailer/deprecated_api.rb +++ b/actionmailer/lib/action_mailer/deprecated_api.rb @@ -19,6 +19,7 @@ module ActionMailer end part = Mail::Part.new(params) + yield part if block_given? @parts << part end @@ -29,7 +30,16 @@ module ActionMailer super # Run deprecation hooks params = { :content_type => params } if String === params - params = { :content_disposition => "attachment", + + if filename = params.delete(:filename) + content_disposition = "attachment; filename=\"#{File.basename(filename)}\"" + else + content_disposition = "attachment" + end + + params[:content] = params.delete(:data) if params[:data] + + params = { :content_disposition => content_disposition, :content_transfer_encoding => "base64" }.merge(params) part(params, &block) diff --git a/actionmailer/lib/action_mailer/tmail_compat.rb b/actionmailer/lib/action_mailer/tmail_compat.rb index 2fd25ff1451..94f21c7cf35 100644 --- a/actionmailer/lib/action_mailer/tmail_compat.rb +++ b/actionmailer/lib/action_mailer/tmail_compat.rb @@ -16,5 +16,10 @@ module Mail end end + def original_filename + STDERR.puts("Message#original_filename is deprecated, please call Message#filename.\n#{caller}") + filename + end + end end \ No newline at end of file diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index b8d2e46241d..f1f5f384829 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -42,12 +42,18 @@ class BaseTest < Test::Unit::TestCase class TestMailer < ActionMailer::Base def welcome(hash = {}) - headers['X-SPAM'] = "Not SPAM" hash = {:to => 'mikel@test.lindsaar.net', :from => 'jose@test.plataformatec.com', :subject => 'The first email on new API!'}.merge!(hash) mail(hash) end + def invoice(hash = {}) + attachments['invoice.pdf'] = 'This is test File content' + hash = {:to => 'mikel@test.lindsaar.net', :from => 'jose@test.plataformatec.com', + :subject => 'Your invoice is attached'}.merge!(hash) + mail(hash) + end + end def test_the_method_call_to_mail_does_not_raise_error @@ -84,6 +90,25 @@ class BaseTest < Test::Unit::TestCase # assert_equal("Not SPAM", email['X-SPAM']) # end + def test_should_allow_you_to_send_an_attachment + assert_nothing_raised { TestMailer.deliver_invoice } + end + + def test_should_allow_you_to_send_an_attachment + email = TestMailer.deliver_invoice + assert_equal(1, email.attachments.length) + end + + def test_should_allow_you_to_send_an_attachment + email = TestMailer.deliver_invoice + assert_equal('invoice.pdf', email.attachments[0].filename) + end + + def test_should_allow_you_to_send_an_attachment + email = TestMailer.deliver_invoice + assert_equal('This is test File content', email.attachments['invoice.pdf'].decoded) + end + def test_should_use_class_defaults end diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index 396dd00a914..f83e13f16f2 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -898,6 +898,7 @@ EOF assert_equal "iso-8859-1", mail.parts[1].charset assert_equal "image/jpeg", mail.parts[2].mime_type + assert_equal "attachment", mail.parts[2][:content_disposition].disposition_type assert_equal "foo.jpg", mail.parts[2][:content_disposition].filename assert_equal "foo.jpg", mail.parts[2][:content_type].filename From 12c001fec449864db64eca9ba3a477a7da30b2ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Fri, 22 Jan 2010 12:46:19 +1100 Subject: [PATCH 16/63] Updating deprecated API to sanitize old style attachments hash to work with new mail.attachments method --- .../lib/action_mailer/deprecated_api.rb | 47 ++++++++++++++----- actionmailer/test/mail_service_test.rb | 2 +- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/actionmailer/lib/action_mailer/deprecated_api.rb b/actionmailer/lib/action_mailer/deprecated_api.rb index d096ea61800..19c94aee8d9 100644 --- a/actionmailer/lib/action_mailer/deprecated_api.rb +++ b/actionmailer/lib/action_mailer/deprecated_api.rb @@ -30,23 +30,48 @@ module ActionMailer super # Run deprecation hooks params = { :content_type => params } if String === params - - if filename = params.delete(:filename) - content_disposition = "attachment; filename=\"#{File.basename(filename)}\"" - else - content_disposition = "attachment" - end - - params[:content] = params.delete(:data) if params[:data] - - params = { :content_disposition => content_disposition, - :content_transfer_encoding => "base64" }.merge(params) + if params[:filename] + params = normalize_file_hash(params) + else + params = normalize_nonfile_hash(params) + end part(params, &block) end private + def normalize_nonfile_hash(params) + content_disposition = "attachment;" + + mime_type = params.delete(:mime_type) + + if content_type = params.delete(:content_type) + content_type = "#{mime_type || content_type};" + end + + params[:body] = params.delete(:data) if params[:data] + + { :content_type => content_type, + :content_disposition => content_disposition }.merge(params) + end + + def normalize_file_hash(params) + filename = File.basename(params.delete(:filename)) + content_disposition = "attachment; filename=\"#{File.basename(filename)}\"" + + mime_type = params.delete(:mime_type) + + if (content_type = params.delete(:content_type)) && (content_type !~ /filename=/) + content_type = "#{mime_type || content_type}; filename=\"#{filename}\"" + end + + params[:body] = params.delete(:data) if params[:data] + + { :content_type => content_type, + :content_disposition => content_disposition }.merge(params) + end + def create_mail #:nodoc: m = @message diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index f83e13f16f2..51d722ea002 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -1036,7 +1036,7 @@ EOF def test_empty_header_values_omitted result = TestMailer.create_unnamed_attachment(@recipient).encoded assert_match %r{Content-Type: application/octet-stream;}, result - assert_match %r{Content-Disposition: attachment[^;]}, result + assert_match %r{Content-Disposition: attachment;}, result end def test_headers_with_nonalpha_chars From 77986f6bdb18414d730fa1ec686365dff26bb4fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Fri, 22 Jan 2010 12:49:13 +1100 Subject: [PATCH 17/63] Added use of AS::Notifications for tmail_compat.rb --- actionmailer/lib/action_mailer/tmail_compat.rb | 9 ++++++--- actionmailer/test/tmail_compat_test.rb | 2 -- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/actionmailer/lib/action_mailer/tmail_compat.rb b/actionmailer/lib/action_mailer/tmail_compat.rb index 94f21c7cf35..da13aac677b 100644 --- a/actionmailer/lib/action_mailer/tmail_compat.rb +++ b/actionmailer/lib/action_mailer/tmail_compat.rb @@ -2,14 +2,16 @@ module Mail class Message def set_content_type(*args) - STDERR.puts("Message#set_content_type is deprecated, please just call Message#content_type with the same arguments.\n#{caller}") + ActiveSupport::Deprecation.warn('Message#set_content_type is deprecated, please just call ' << + 'Message#content_type with the same arguments.', caller[0,10]) content_type(*args) end alias :old_transfer_encoding :transfer_encoding def transfer_encoding(value = nil) if value - STDERR.puts("Message#transfer_encoding is deprecated, please call Message#content_transfer_encoding with the same arguments.\n#{caller}") + ActiveSupport::Deprecation.warn('Message#transfer_encoding is deprecated, please call ' << + 'Message#content_transfer_encoding with the same arguments.', caller[0,10]) content_transfer_encoding(value) else old_transfer_encoding @@ -17,7 +19,8 @@ module Mail end def original_filename - STDERR.puts("Message#original_filename is deprecated, please call Message#filename.\n#{caller}") + ActiveSupport::Deprecation.warn('Message#original_filename is deprecated, ' << + 'please call Message#filename.', caller[0,10]) filename end diff --git a/actionmailer/test/tmail_compat_test.rb b/actionmailer/test/tmail_compat_test.rb index a1ca6a72437..b7fcb3cfeae 100644 --- a/actionmailer/test/tmail_compat_test.rb +++ b/actionmailer/test/tmail_compat_test.rb @@ -4,7 +4,6 @@ class TmailCompatTest < Test::Unit::TestCase def test_set_content_type_raises_deprecation_warning mail = Mail.new - STDERR.expects(:puts) # Deprecation warning assert_nothing_raised do mail.set_content_type "text/plain" end @@ -13,7 +12,6 @@ class TmailCompatTest < Test::Unit::TestCase def test_transfer_encoding_raises_deprecation_warning mail = Mail.new - STDERR.expects(:puts) # Deprecation warning assert_nothing_raised do mail.transfer_encoding "base64" end From 90bbed233e14957376d6c1985202c495b3af6ed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Fri, 22 Jan 2010 12:51:07 +1100 Subject: [PATCH 18/63] Updating deprecated_body.rb to use :content instead of :data or :body in the params hash --- actionmailer/lib/action_mailer/deprecated_body.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/actionmailer/lib/action_mailer/deprecated_body.rb b/actionmailer/lib/action_mailer/deprecated_body.rb index c82610014f3..daaf1453270 100644 --- a/actionmailer/lib/action_mailer/deprecated_body.rb +++ b/actionmailer/lib/action_mailer/deprecated_body.rb @@ -14,10 +14,15 @@ module ActionMailer end def attachment(params, &block) + if params[:data] + ActiveSupport::Deprecation.warn('attachment :data => "string" is deprecated. To set the body of an attachment ' << + 'please use :content instead, like attachment :content => "string"', caller[0,10]) + params[:content] = params.delete(:data) + end if params[:body] - ActiveSupport::Deprecation.warn('attachment :body => "string" is deprecated. To set the body of an attachment ' << - 'please use :data instead, like attachment :data => "string"', caller[0,10]) - params[:data] = params.delete(:body) + ActiveSupport::Deprecation.warn('attachment :data => "string" is deprecated. To set the body of an attachment ' << + 'please use :content instead, like attachment :content => "string"', caller[0,10]) + params[:content] = params.delete(:body) end end From c9dc1ac95bc97800dd3deb82fe1cf6f98e27413d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Fri, 22 Jan 2010 13:32:16 +1100 Subject: [PATCH 19/63] Updating functional tests to not compare equality with encoded, Mail::Message has an == operator --- .../generators/test_unit/mailer/templates/functional_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/generators/test_unit/mailer/templates/functional_test.rb b/railties/lib/generators/test_unit/mailer/templates/functional_test.rb index 4de94076e97..d7366fea5fc 100644 --- a/railties/lib/generators/test_unit/mailer/templates/functional_test.rb +++ b/railties/lib/generators/test_unit/mailer/templates/functional_test.rb @@ -7,7 +7,7 @@ class <%= class_name %>Test < ActionMailer::TestCase @expected.body = read_fixture('<%= action %>') @expected.date = Time.now - assert_equal @expected.encoded, <%= class_name %>.create_<%= action %>(@expected.date).encoded + assert_equal @expected, <%= class_name %>.create_<%= action %>(@expected.date) end <% end -%> From 6fd7d1fc128f1d6d7bddaa6770739d7e936dd049 Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar Date: Fri, 22 Jan 2010 19:38:50 +1100 Subject: [PATCH 20/63] Fixing typo in config.frameworks error --- railties/lib/rails/configuration.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb index 7f1783a6b99..21736a28ebd 100644 --- a/railties/lib/rails/configuration.rb +++ b/railties/lib/rails/configuration.rb @@ -137,7 +137,7 @@ module Rails end def frameworks(*args) - raise "config.frameworks in no longer supported. See the generated " \ + raise "config.frameworks is no longer supported. See the generated " \ "config/boot.rb for steps on how to limit the frameworks that " \ "will be loaded" end From 343ac48f45a433b2ef0dc67f9cd9d1245fb5ef2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Fri, 22 Jan 2010 11:01:21 +0100 Subject: [PATCH 21/63] Moved deprecated_body.rb to deprecatead_api.rb --- actionmailer/lib/action_mailer.rb | 1 - actionmailer/lib/action_mailer/base.rb | 61 -------- .../lib/action_mailer/deprecated_api.rb | 141 ++++++++++++++---- .../lib/action_mailer/deprecated_body.rb | 53 ------- actionmailer/lib/action_mailer/mail_helper.rb | 5 + .../lib/action_mailer/tmail_compat.rb | 6 +- actionmailer/test/mail_service_test.rb | 12 -- 7 files changed, 118 insertions(+), 161 deletions(-) delete mode 100644 actionmailer/lib/action_mailer/deprecated_body.rb diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 1765aee9cc5..37c95baea77 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -33,7 +33,6 @@ module ActionMailer autoload :AdvAttrAccessor autoload :Base autoload :DeliveryMethods - autoload :DeprecatedBody autoload :DeprecatedApi autoload :MailHelper autoload :Quoting diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 39ddafe7fef..0b3b44e3268 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -263,7 +263,6 @@ module ActionMailer #:nodoc: include AbstractController::UrlFor helper ActionMailer::MailHelper - include ActionMailer::DeprecatedBody include ActionMailer::DeprecatedApi include ActionMailer::DeliveryMethods @@ -297,69 +296,11 @@ module ActionMailer #:nodoc: @@default_implicit_parts_order = [ "text/plain", "text/enriched", "text/html" ] cattr_accessor :default_implicit_parts_order - @@protected_instance_variables = %w(@parts @message) - cattr_reader :protected_instance_variables - - # Specify the BCC addresses for the message - adv_attr_accessor :bcc - - # Specify the CC addresses for the message. - adv_attr_accessor :cc - - # Specify the charset to use for the message. This defaults to the - # +default_charset+ specified for ActionMailer::Base. - adv_attr_accessor :charset - - # Specify the content type for the message. This defaults to text/plain - # in most cases, but can be automatically set in some situations. - adv_attr_accessor :content_type - - # Specify the from address for the message. - adv_attr_accessor :from - - # Specify the address (if different than the "from" address) to direct - # replies to this message. - adv_attr_accessor :reply_to - - # Specify additional headers to be added to the message. - adv_attr_accessor :headers - - # Specify the order in which parts should be sorted, based on content-type. - # This defaults to the value for the +default_implicit_parts_order+. - adv_attr_accessor :implicit_parts_order - - # Defaults to "1.0", but may be explicitly given if needed. - adv_attr_accessor :mime_version - - # The recipient addresses for the message, either as a string (for a single - # address) or an array (for multiple addresses). - adv_attr_accessor :recipients - - # The date on which the message was sent. If not set (the default), the - # header will be set by the delivery agent. - adv_attr_accessor :sent_on - - # Specify the subject of the message. - adv_attr_accessor :subject - - # Specify the template name to use for current message. This is the "base" - # template name, without the extension or directory, and may be used to - # have multiple mailer methods share the same template. - adv_attr_accessor :template - - # Override the mailer name, which defaults to an inflected version of the - # mailer's class name. If you want to use a template in a non-standard - # location, you can use this to specify that location. - adv_attr_accessor :mailer_name - # Expose the internal Mail message attr_reader :message # Pass calls to headers and attachment to the Mail#Message instance delegate :headers, :attachments, :to => :@message - - # Alias controller_path to mailer_name so render :partial in views work. - alias :controller_path :mailer_name class << self @@ -504,9 +445,7 @@ module ActionMailer #:nodoc: # rendered and a new Mail object created. def process(method_name, *args) initialize_defaults(method_name) - super - unless @mail_was_called # Create e-mail parts create_parts diff --git a/actionmailer/lib/action_mailer/deprecated_api.rb b/actionmailer/lib/action_mailer/deprecated_api.rb index 19c94aee8d9..32ea4162dc7 100644 --- a/actionmailer/lib/action_mailer/deprecated_api.rb +++ b/actionmailer/lib/action_mailer/deprecated_api.rb @@ -3,8 +3,87 @@ module ActionMailer # hooks in ActionMailer::Base are removed as well. # # Moved here to allow us to add the new Mail API - module DeprecatedApi - extend ActionMailer::AdvAttrAccessor + module DeprecatedApi #:nodoc: + extend ActiveSupport::Concern + + included do + extend ActionMailer::AdvAttrAccessor + + @@protected_instance_variables = %w(@parts) + cattr_reader :protected_instance_variables + + # Specify the BCC addresses for the message + adv_attr_accessor :bcc + + # Specify the CC addresses for the message. + adv_attr_accessor :cc + + # Specify the charset to use for the message. This defaults to the + # +default_charset+ specified for ActionMailer::Base. + adv_attr_accessor :charset + + # Specify the content type for the message. This defaults to text/plain + # in most cases, but can be automatically set in some situations. + adv_attr_accessor :content_type + + # Specify the from address for the message. + adv_attr_accessor :from + + # Specify the address (if different than the "from" address) to direct + # replies to this message. + adv_attr_accessor :reply_to + + # Specify additional headers to be added to the message. + adv_attr_accessor :headers + + # Specify the order in which parts should be sorted, based on content-type. + # This defaults to the value for the +default_implicit_parts_order+. + adv_attr_accessor :implicit_parts_order + + # Defaults to "1.0", but may be explicitly given if needed. + adv_attr_accessor :mime_version + + # The recipient addresses for the message, either as a string (for a single + # address) or an array (for multiple addresses). + adv_attr_accessor :recipients + + # The date on which the message was sent. If not set (the default), the + # header will be set by the delivery agent. + adv_attr_accessor :sent_on + + # Specify the subject of the message. + adv_attr_accessor :subject + + # Specify the template name to use for current message. This is the "base" + # template name, without the extension or directory, and may be used to + # have multiple mailer methods share the same template. + adv_attr_accessor :template + + # Override the mailer name, which defaults to an inflected version of the + # mailer's class name. If you want to use a template in a non-standard + # location, you can use this to specify that location. + adv_attr_accessor :mailer_name + + # Define the body of the message. This is either a Hash (in which case it + # specifies the variables to pass to the template when it is rendered), + # or a string, in which case it specifies the actual text of the message. + adv_attr_accessor :body + + # Alias controller_path to mailer_name so render :partial in views work. + alias :controller_path :mailer_name + end + + def render(*args) + options = args.last.is_a?(Hash) ? args.last : {} + if options[:body] + ActiveSupport::Deprecation.warn(':body in render deprecated. Please call body ' << + 'with a hash instead', caller[0,1]) + + body options.delete(:body) + end + + super + end # Add a part to a multipart message, with the given content-type. The # part itself is yielded to the block so that other properties (charset, @@ -13,8 +92,6 @@ module ActionMailer params = {:content_type => params} if String === params if custom_headers = params.delete(:headers) - ActiveSupport::Deprecation.warn('Passing custom headers with :headers => {} is deprecated. ' << - 'Please just pass in custom headers directly.', caller[0,10]) params.merge!(custom_headers) end @@ -27,19 +104,38 @@ module ActionMailer # Add an attachment to a multipart message. This is simply a part with the # content-disposition set to "attachment". def attachment(params, &block) - super # Run deprecation hooks - params = { :content_type => params } if String === params + params[:content] ||= params.delete(:data) || params.delete(:body) + if params[:filename] params = normalize_file_hash(params) else params = normalize_nonfile_hash(params) end + part(params, &block) end - private + # Render a message but does not set it as mail body. Useful for rendering + # data for part and attachments. + # + # Examples: + # + # render_message "special_message" + # render_message :template => "special_message" + # render_message :inline => "<%= 'Hi!' %>" + # + def render_message(object) + case object + when String + render_to_body(:template => object) + else + render_to_body(object) + end + end + + private def normalize_nonfile_hash(params) content_disposition = "attachment;" @@ -109,25 +205,6 @@ module ActionMailer @message end - - # Render a message but does not set it as mail body. Useful for rendering - # data for part and attachments. - # - # Examples: - # - # render_message "special_message" - # render_message :template => "special_message" - # render_message :inline => "<%= 'Hi!' %>" - # - # TODO Deprecate me - def render_message(object) - case object - when String - render_to_body(:template => object) - else - render_to_body(object) - end - end # Set up the default values for the various instance variables of this # mailer. Subclasses may override this method to provide different @@ -139,18 +216,20 @@ module ActionMailer @mime_version ||= self.class.default_mime_version.dup if self.class.default_mime_version @mailer_name ||= self.class.mailer_name.dup - @delivery_method = self.class.delivery_method @template ||= method_name @parts ||= [] @headers ||= {} @sent_on ||= Time.now - - super # Run deprecation hooks + @body ||= {} end def create_parts #:nodoc: - super # Run deprecation hooks + if String === @body + self.response_body = @body + elsif @body.is_a?(Hash) && !@body.empty? + @body.each { |k, v| instance_variable_set(:"@#{k}", v) } + end if String === response_body @parts.unshift create_inline_part(response_body) @@ -179,7 +258,7 @@ module ActionMailer :body => body ) end - + def split_content_type(ct) #:nodoc: ct.to_s.split("/") end diff --git a/actionmailer/lib/action_mailer/deprecated_body.rb b/actionmailer/lib/action_mailer/deprecated_body.rb deleted file mode 100644 index daaf1453270..00000000000 --- a/actionmailer/lib/action_mailer/deprecated_body.rb +++ /dev/null @@ -1,53 +0,0 @@ -module ActionMailer - # TODO Remove this module all together in a next release. Ensure that super - # hooks in ActionMailer::Base are removed as well. - module DeprecatedBody - extend ActionMailer::AdvAttrAccessor - - # Define the body of the message. This is either a Hash (in which case it - # specifies the variables to pass to the template when it is rendered), - # or a string, in which case it specifies the actual text of the message. - adv_attr_accessor :body - - def initialize_defaults(method_name) - @body ||= {} - end - - def attachment(params, &block) - if params[:data] - ActiveSupport::Deprecation.warn('attachment :data => "string" is deprecated. To set the body of an attachment ' << - 'please use :content instead, like attachment :content => "string"', caller[0,10]) - params[:content] = params.delete(:data) - end - if params[:body] - ActiveSupport::Deprecation.warn('attachment :data => "string" is deprecated. To set the body of an attachment ' << - 'please use :content instead, like attachment :content => "string"', caller[0,10]) - params[:content] = params.delete(:body) - end - end - - def create_parts - if String === @body - ActiveSupport::Deprecation.warn('body(String) is deprecated. To set the body with a text ' << - 'call render(:text => "body")', caller[0,10]) - self.response_body = @body - elsif @body.is_a?(Hash) && !@body.empty? - ActiveSupport::Deprecation.warn('body(Hash) is deprecated. Use instance variables to define ' << - 'assigns in your view', caller[0,10]) - @body.each { |k, v| instance_variable_set(:"@#{k}", v) } - end - end - - def render(*args) - options = args.last.is_a?(Hash) ? args.last : {} - if options[:body] - ActiveSupport::Deprecation.warn(':body in render deprecated. Please call body ' << - 'with a hash instead', caller[0,1]) - - body options.delete(:body) - end - - super - end - end -end diff --git a/actionmailer/lib/action_mailer/mail_helper.rb b/actionmailer/lib/action_mailer/mail_helper.rb index 701dc344310..702c9ba8f7d 100644 --- a/actionmailer/lib/action_mailer/mail_helper.rb +++ b/actionmailer/lib/action_mailer/mail_helper.rb @@ -20,5 +20,10 @@ module ActionMailer def mailer #:nodoc: @controller end + + # Access the message instance. + def message + @message + end end end diff --git a/actionmailer/lib/action_mailer/tmail_compat.rb b/actionmailer/lib/action_mailer/tmail_compat.rb index da13aac677b..d78332c1354 100644 --- a/actionmailer/lib/action_mailer/tmail_compat.rb +++ b/actionmailer/lib/action_mailer/tmail_compat.rb @@ -3,7 +3,7 @@ module Mail def set_content_type(*args) ActiveSupport::Deprecation.warn('Message#set_content_type is deprecated, please just call ' << - 'Message#content_type with the same arguments.', caller[0,10]) + 'Message#content_type with the same arguments', caller[0,10]) content_type(*args) end @@ -11,7 +11,7 @@ module Mail def transfer_encoding(value = nil) if value ActiveSupport::Deprecation.warn('Message#transfer_encoding is deprecated, please call ' << - 'Message#content_transfer_encoding with the same arguments.', caller[0,10]) + 'Message#content_transfer_encoding with the same arguments', caller[0,10]) content_transfer_encoding(value) else old_transfer_encoding @@ -20,7 +20,7 @@ module Mail def original_filename ActiveSupport::Deprecation.warn('Message#original_filename is deprecated, ' << - 'please call Message#filename.', caller[0,10]) + 'please call Message#filename', caller[0,10]) filename end diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index 51d722ea002..ec820e34643 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -301,13 +301,6 @@ class TestMailer < ActionMailer::Base render :text => "testing" end - def body_ivar(recipient) - recipients recipient - subject "Body as a local variable" - from "test@example.com" - body :body => "foo", :bar => "baz" - end - def subject_with_i18n(recipient) recipients recipient from "system@loudthinking.com" @@ -1080,11 +1073,6 @@ EOF assert_equal "another@somewhere.test", MockSMTP.deliveries[0][1].to_s end - def test_body_is_stored_as_an_ivar - mail = TestMailer.create_body_ivar(@recipient) - assert_equal "body: foo\nbar: baz", mail.body.to_s - end - def test_starttls_is_enabled_if_supported ActionMailer::Base.delivery_settings[:smtp].merge!(:enable_starttls_auto => true) MockSMTP.any_instance.expects(:respond_to?).with(:enable_starttls_auto).returns(true) From bb9d71ff9e537597ff4d5962e7870ad99001f605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Fri, 22 Jan 2010 11:10:37 +0100 Subject: [PATCH 22/63] Move class methods to deprecated stuff. --- actionmailer/lib/action_mailer/base.rb | 53 +++---------------- .../lib/action_mailer/deprecated_api.rb | 48 +++++++++++++++++ 2 files changed, 54 insertions(+), 47 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 0b3b44e3268..2c1de0e3b48 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -253,7 +253,6 @@ module ActionMailer #:nodoc: # +implicit_parts_order+. class Base < AbstractController::Base include Quoting - extend AdvAttrAccessor include AbstractController::Logger include AbstractController::Rendering @@ -311,23 +310,6 @@ module ActionMailer #:nodoc: alias :controller_path :mailer_name - def respond_to?(method_symbol, include_private = false) #:nodoc: - matches_dynamic_method?(method_symbol) || super - end - - def method_missing(method_symbol, *parameters) #:nodoc: - if match = matches_dynamic_method?(method_symbol) - case match[1] - when 'create' then new(match[2], *parameters).message - when 'deliver' then new(match[2], *parameters).deliver! - when 'new' then nil - else super - end - else - super - end - end - # Receives a raw email, parses it into an email object, decodes it, # instantiates a new mailer, and passes the email object to the mailer # object's +receive+ method. If you want your mailer to be able to @@ -357,7 +339,6 @@ module ActionMailer #:nodoc: raise "no mail object available for delivery!" unless mail ActiveSupport::Notifications.instrument("action_mailer.deliver", :mailer => self.name) do |payload| - self.set_payload_for_mail(payload, mail) mail.delivery_method delivery_methods[delivery_method], @@ -396,18 +377,11 @@ module ActionMailer #:nodoc: payload[:date] = mail.date payload[:mail] = mail.encoded end - - private - - def matches_dynamic_method?(method_name) #:nodoc: - method_name = method_name.to_s - /^(create|deliver)_([_a-z]\w*)/.match(method_name) || /^(new)$/.match(method_name) - end end def mail(headers = {}) # Guard flag to prevent both the old and the new API from firing - # TODO - Move this @mail_was_called flag into deprecated_api.rb + # Should be removed when old API is deprecated @mail_was_called = true m = @message @@ -426,6 +400,11 @@ module ActionMailer #:nodoc: m.body.set_sort_order(headers[:parts_order] || @@default_implicit_parts_order) + # # Set the subject if not set yet + # @subject ||= I18n.t(:subject, :scope => [:actionmailer, mailer_name, method_name], + # :default => method_name.humanize) + + # TODO: m.body.sort_parts! m end @@ -436,30 +415,10 @@ module ActionMailer #:nodoc: # method, for instance). def initialize(method_name=nil, *args) super() - @mail_was_called = false @message = Mail.new process(method_name, *args) if method_name end - # Process the mailer via the given +method_name+. The body will be - # rendered and a new Mail object created. - def process(method_name, *args) - initialize_defaults(method_name) - super - unless @mail_was_called - # Create e-mail parts - create_parts - - # Set the subject if not set yet - @subject ||= I18n.t(:subject, :scope => [:actionmailer, mailer_name, method_name], - :default => method_name.humanize) - - # Build the mail object itself - create_mail - end - @message - end - # Delivers a Mail object. By default, it delivers the cached mail # object (from the create! method). If no cached mail object exists, and # no alternate has been given as the parameter, this will fail. diff --git a/actionmailer/lib/action_mailer/deprecated_api.rb b/actionmailer/lib/action_mailer/deprecated_api.rb index 32ea4162dc7..430b0cef4aa 100644 --- a/actionmailer/lib/action_mailer/deprecated_api.rb +++ b/actionmailer/lib/action_mailer/deprecated_api.rb @@ -73,6 +73,37 @@ module ActionMailer alias :controller_path :mailer_name end + module ClassMethods + def respond_to?(method_symbol, include_private = false) #:nodoc: + matches_dynamic_method?(method_symbol) || super + end + + def method_missing(method_symbol, *parameters) #:nodoc: + if match = matches_dynamic_method?(method_symbol) + case match[1] + when 'create' then new(match[2], *parameters).message + when 'deliver' then new(match[2], *parameters).deliver! + when 'new' then nil + else super + end + else + super + end + end + + private + + def matches_dynamic_method?(method_name) #:nodoc: + method_name = method_name.to_s + /^(create|deliver)_([_a-z]\w*)/.match(method_name) || /^(new)$/.match(method_name) + end + end + + def initialize(*) + super() + @mail_was_called = false + end + def render(*args) options = args.last.is_a?(Hash) ? args.last : {} if options[:body] @@ -85,6 +116,23 @@ module ActionMailer super end + def process(method_name, *args) + initialize_defaults(method_name) + super + unless @mail_was_called + # Create e-mail parts + create_parts + + # Set the subject if not set yet + @subject ||= I18n.t(:subject, :scope => [:actionmailer, mailer_name, method_name], + :default => method_name.humanize) + + # Build the mail object itself + create_mail + end + end + + # Add a part to a multipart message, with the given content-type. The # part itself is yielded to the block so that other properties (charset, # body, headers, etc.) can be set on it. From b30eb39ff072ce95ccd5ce94ae08d116c23fd260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Fri, 22 Jan 2010 11:57:54 +0100 Subject: [PATCH 23/63] Add more tests to new API. --- actionmailer/lib/action_mailer/base.rb | 38 ++++-- .../lib/action_mailer/deprecated_api.rb | 2 +- actionmailer/test/base_test.rb | 121 ++++++++++++------ 3 files changed, 110 insertions(+), 51 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 2c1de0e3b48..d75bbe952f3 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -277,14 +277,14 @@ module ActionMailer #:nodoc: @@deliveries = [] cattr_accessor :deliveries - @@default_charset = "utf-8" - cattr_accessor :default_charset + extlib_inheritable_accessor :default_charset + self.default_charset = "utf-8" - @@default_content_type = "text/plain" - cattr_accessor :default_content_type + extlib_inheritable_accessor :default_content_type + self.default_content_type = "text/plain" - @@default_mime_version = "1.0" - cattr_accessor :default_mime_version + extlib_inheritable_accessor :default_mime_version + self.default_mime_version = "1.0" # This specifies the order that the parts of a multipart email will be. Usually you put # text/plain at the top so someone without a MIME capable email reader can read the plain @@ -292,14 +292,24 @@ module ActionMailer #:nodoc: # # Any content type that is not listed here will be inserted in the order you add them to # the email after the content types you list here. - @@default_implicit_parts_order = [ "text/plain", "text/enriched", "text/html" ] - cattr_accessor :default_implicit_parts_order + extlib_inheritable_accessor :default_implicit_parts_order + self.default_implicit_parts_order = [ "text/plain", "text/enriched", "text/html" ] # Expose the internal Mail message attr_reader :message - # Pass calls to headers and attachment to the Mail#Message instance - delegate :headers, :attachments, :to => :@message + def headers(args=nil) + if args + ActiveSupport::Deprecation.warn "headers(Hash) is deprecated, please do headers[key] = value instead", caller + @headers = args + else + @message + end + end + + def attachments + @message.attachments + end class << self @@ -386,9 +396,9 @@ module ActionMailer #:nodoc: m = @message - m.content_type ||= headers[:content_type] || @@default_content_type - m.charset ||= headers[:charset] || @@default_charset - m.mime_version ||= headers[:mime_version] || @@default_mime_version + m.content_type ||= headers[:content_type] || self.class.default_content_type + m.charset ||= headers[:charset] || self.class.default_charset + m.mime_version ||= headers[:mime_version] || self.class.default_mime_version m.subject = quote_if_necessary(headers[:subject], m.charset) if headers[:subject] m.to = quote_address_if_necessary(headers[:to], m.charset) if headers[:to] @@ -398,7 +408,7 @@ module ActionMailer #:nodoc: m.reply_to = quote_address_if_necessary(headers[:reply_to], m.charset) if headers[:reply_to] m.date = headers[:date] if headers[:date] - m.body.set_sort_order(headers[:parts_order] || @@default_implicit_parts_order) + m.body.set_sort_order(headers[:parts_order] || self.class.default_implicit_parts_order) # # Set the subject if not set yet # @subject ||= I18n.t(:subject, :scope => [:actionmailer, mailer_name, method_name], diff --git a/actionmailer/lib/action_mailer/deprecated_api.rb b/actionmailer/lib/action_mailer/deprecated_api.rb index 430b0cef4aa..90e2aebf53d 100644 --- a/actionmailer/lib/action_mailer/deprecated_api.rb +++ b/actionmailer/lib/action_mailer/deprecated_api.rb @@ -227,7 +227,7 @@ module ActionMailer m.mime_version = mime_version unless mime_version.nil? m.date = sent_on.to_time rescue sent_on if sent_on - headers.each { |k, v| m[k] = v } + @headers.each { |k, v| m[k] = v } real_content_type, ctype_attrs = parse_content_type main_type, sub_type = split_content_type(real_content_type) diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index f1f5f384829..b11631f444f 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -37,37 +37,43 @@ require 'abstract_unit' # mail.deliver # # Notifier.welcome(user).deliver # => creates and sends the Mail in one step -class BaseTest < Test::Unit::TestCase - +class BaseTest < ActiveSupport::TestCase + DEFAULT_HEADERS = { + :to => 'mikel@test.lindsaar.net', + :from => 'jose@test.plataformatec.com', + :subject => 'The first email on new API!' + } + class TestMailer < ActionMailer::Base - def welcome(hash = {}) - hash = {:to => 'mikel@test.lindsaar.net', :from => 'jose@test.plataformatec.com', - :subject => 'The first email on new API!'}.merge!(hash) - mail(hash) + headers['X-SPAM'] = "Not SPAM" + mail(DEFAULT_HEADERS.merge(hash)) end - - def invoice(hash = {}) + + def attachment_with_content attachments['invoice.pdf'] = 'This is test File content' - hash = {:to => 'mikel@test.lindsaar.net', :from => 'jose@test.plataformatec.com', - :subject => 'Your invoice is attached'}.merge!(hash) - mail(hash) + mail(DEFAULT_HEADERS) + end + + def attachment_with_hash + attachments['invoice.jpg'] = { :content => "you smiling", :mime_type => "image/x-jpg", + :transfer_encoding => "base64" } + mail(DEFAULT_HEADERS) end - end - def test_the_method_call_to_mail_does_not_raise_error + test "method call to mail does not raise error" do assert_nothing_raised { TestMailer.deliver_welcome } end - def test_should_set_the_headers_of_the_mail_message + test "mail() should set the headers of the mail message" do email = TestMailer.deliver_welcome assert_equal(email.to, ['mikel@test.lindsaar.net']) assert_equal(email.from, ['jose@test.plataformatec.com']) assert_equal(email.subject, 'The first email on new API!') end - - def test_should_allow_all_headers_set + + test "mail() with bcc, cc, content_type, charset, mime_version, reply_to and date" do @time = Time.now email = TestMailer.deliver_welcome(:bcc => 'bcc@test.lindsaar.net', :cc => 'cc@test.lindsaar.net', @@ -85,32 +91,58 @@ class BaseTest < Test::Unit::TestCase assert_equal(email.date, @time) end -# def test_should_allow_custom_headers_to_be_set -# email = TestMailer.deliver_welcome -# assert_equal("Not SPAM", email['X-SPAM']) -# end - - def test_should_allow_you_to_send_an_attachment - assert_nothing_raised { TestMailer.deliver_invoice } + test "custom headers" do + email = TestMailer.deliver_welcome + assert_equal("Not SPAM", email['X-SPAM'].decoded) end - def test_should_allow_you_to_send_an_attachment - email = TestMailer.deliver_invoice + test "attachment with content" do + email = TestMailer.deliver_attachment_with_content assert_equal(1, email.attachments.length) - end - - def test_should_allow_you_to_send_an_attachment - email = TestMailer.deliver_invoice assert_equal('invoice.pdf', email.attachments[0].filename) - end - - def test_should_allow_you_to_send_an_attachment - email = TestMailer.deliver_invoice assert_equal('This is test File content', email.attachments['invoice.pdf'].decoded) end - def test_should_use_class_defaults - + test "attachment gets content type from filename" do + email = TestMailer.deliver_attachment_with_content + assert_equal('invoice.pdf', email.attachments[0].filename) + end + + test "attachment with hash" do + email = TestMailer.deliver_attachment_with_hash + assert_equal(1, email.attachments.length) + assert_equal('invoice.jpg', email.attachments[0].filename) + assert_equal("\312\213\254\232)b", email.attachments['invoice.jpg'].decoded) + end + + test "uses default charset from class" do + swap TestMailer, :default_charset => "US-ASCII" do + email = TestMailer.deliver_welcome + assert_equal("US-ASCII", email.charset) + + email = TestMailer.deliver_welcome(:charset => "iso-8559-1") + assert_equal("iso-8559-1", email.charset) + end + end + + test "uses default content type from class" do + swap TestMailer, :default_content_type => "text/html" do + email = TestMailer.deliver_welcome + assert_equal("text/html", email.mime_type) + + email = TestMailer.deliver_welcome(:content_type => "application/xml") + assert_equal("application/xml", email.mime_type) + end + end + + test "uses default mime version from class" do + swap TestMailer, :default_mime_version => "2.0" do + email = TestMailer.deliver_welcome + assert_equal("2.0", email.mime_version) + + email = TestMailer.deliver_welcome(:mime_version => "1.0") + assert_equal("1.0", email.mime_version) + end end # def test_that_class_defaults_are_set_on_instantiation @@ -120,5 +152,22 @@ class BaseTest < Test::Unit::TestCase # def test_should_set_the_subject_from_i18n # pending # end - + + protected + + # Execute the block setting the given values and restoring old values after + # the block is executed. + def swap(object, new_values) + old_values = {} + new_values.each do |key, value| + old_values[key] = object.send key + object.send :"#{key}=", value + end + yield + ensure + old_values.each do |key, value| + object.send :"#{key}=", value + end + end + end \ No newline at end of file From dcb925369389fa98d1548b25504c8e3a07eaeea3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Fri, 22 Jan 2010 13:27:20 +0100 Subject: [PATCH 24/63] Add basic template rendering to new DSL. --- actionmailer/lib/action_mailer/base.rb | 65 +++++++++++++----- .../lib/action_mailer/deprecated_api.rb | 9 +-- actionmailer/lib/action_mailer/mail_helper.rb | 2 +- actionmailer/test/base_test.rb | 66 ++++++++++--------- .../test/fixtures/base_mailer/welcome.erb | 1 + .../test/fixtures/test_mailer/body_ivar.erb | 2 - actionmailer/test/mail_service_test.rb | 9 --- .../lib/action_view/render/rendering.rb | 2 + 8 files changed, 88 insertions(+), 68 deletions(-) create mode 100644 actionmailer/test/fixtures/base_mailer/welcome.erb delete mode 100644 actionmailer/test/fixtures/test_mailer/body_ivar.erb diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index d75bbe952f3..1f432c2a804 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -280,6 +280,7 @@ module ActionMailer #:nodoc: extlib_inheritable_accessor :default_charset self.default_charset = "utf-8" + # TODO This should be used when calling render extlib_inheritable_accessor :default_content_type self.default_content_type = "text/plain" @@ -357,7 +358,7 @@ module ActionMailer #:nodoc: begin # TODO Move me to the instance if @@perform_deliveries - mail.deliver! + mail.deliver! self.deliveries << mail end rescue Exception => e # Net::SMTP errors or sendmail pipe errors @@ -393,32 +394,62 @@ module ActionMailer #:nodoc: # Guard flag to prevent both the old and the new API from firing # Should be removed when old API is deprecated @mail_was_called = true - m = @message - - m.content_type ||= headers[:content_type] || self.class.default_content_type - m.charset ||= headers[:charset] || self.class.default_charset - m.mime_version ||= headers[:mime_version] || self.class.default_mime_version - m.subject = quote_if_necessary(headers[:subject], m.charset) if headers[:subject] - m.to = quote_address_if_necessary(headers[:to], m.charset) if headers[:to] - m.from = quote_address_if_necessary(headers[:from], m.charset) if headers[:from] - m.cc = quote_address_if_necessary(headers[:cc], m.charset) if headers[:cc] - m.bcc = quote_address_if_necessary(headers[:bcc], m.charset) if headers[:bcc] - m.reply_to = quote_address_if_necessary(headers[:reply_to], m.charset) if headers[:reply_to] - m.date = headers[:date] if headers[:date] + # Get default subject from I18n if none is set + headers[:subject] ||= I18n.t(:subject, :scope => [:actionmailer, mailer_name, action_name], + :default => action_name.humanize) - m.body.set_sort_order(headers[:parts_order] || self.class.default_implicit_parts_order) + # Give preference to headers and fallbacks to the ones set in mail + headers[:content_type] ||= m.content_type + headers[:charset] ||= m.charset + headers[:mime_version] ||= m.mime_version - # # Set the subject if not set yet - # @subject ||= I18n.t(:subject, :scope => [:actionmailer, mailer_name, method_name], - # :default => method_name.humanize) + m.content_type = headers[:content_type] || self.class.default_content_type.dup + m.charset = headers[:charset] || self.class.default_charset.dup + m.mime_version = headers[:mime_version] || self.class.default_mime_version.dup + m.subject ||= quote_if_necessary(headers[:subject], m.charset) if headers[:subject] + m.to ||= quote_address_if_necessary(headers[:to], m.charset) if headers[:to] + m.from ||= quote_address_if_necessary(headers[:from], m.charset) if headers[:from] + m.cc ||= quote_address_if_necessary(headers[:cc], m.charset) if headers[:cc] + m.bcc ||= quote_address_if_necessary(headers[:bcc], m.charset) if headers[:bcc] + m.reply_to ||= quote_address_if_necessary(headers[:reply_to], m.charset) if headers[:reply_to] + m.date ||= headers[:date] if headers[:date] + + if block_given? + # Do something + else + # TODO Ensure that we don't need to pass I18n.locale as detail + templates = self.class.template_root.find_all(action_name, {}, mailer_name) + + if templates.size == 1 + unless headers[:content_type] + proper_charset = m.charset + m.content_type = templates[0].mime_type.to_s + m.charset = proper_charset + end + m.body = render_to_body(:_template => templates[0]) + else + templates.each do |template| + part = Mail::Part.new + part.content_type = template.mime_type.to_s + part.charset = m.charset + part.body = render_to_body(:_template => template) + end + end + end + + m.body.set_sort_order(headers[:parts_order] || self.class.default_implicit_parts_order.dup) # TODO: m.body.sort_parts! m end + def fill_in_part(part, template, charset) + + end + # Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer # will be initialized according to the named method. If not, the mailer will # remain uninitialized (useful when you only need to invoke the "receive" diff --git a/actionmailer/lib/action_mailer/deprecated_api.rb b/actionmailer/lib/action_mailer/deprecated_api.rb index 90e2aebf53d..b2bb6a64aa9 100644 --- a/actionmailer/lib/action_mailer/deprecated_api.rb +++ b/actionmailer/lib/action_mailer/deprecated_api.rb @@ -120,19 +120,12 @@ module ActionMailer initialize_defaults(method_name) super unless @mail_was_called - # Create e-mail parts create_parts - - # Set the subject if not set yet - @subject ||= I18n.t(:subject, :scope => [:actionmailer, mailer_name, method_name], - :default => method_name.humanize) - - # Build the mail object itself create_mail end + @message end - # Add a part to a multipart message, with the given content-type. The # part itself is yielded to the block so that other properties (charset, # body, headers, etc.) can be set on it. diff --git a/actionmailer/lib/action_mailer/mail_helper.rb b/actionmailer/lib/action_mailer/mail_helper.rb index 702c9ba8f7d..45ba6f07142 100644 --- a/actionmailer/lib/action_mailer/mail_helper.rb +++ b/actionmailer/lib/action_mailer/mail_helper.rb @@ -22,7 +22,7 @@ module ActionMailer end # Access the message instance. - def message + def message #:nodoc: @message end end diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index b11631f444f..f22b20a6bab 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -44,7 +44,9 @@ class BaseTest < ActiveSupport::TestCase :subject => 'The first email on new API!' } - class TestMailer < ActionMailer::Base + class BaseMailer < ActionMailer::Base + self.mailer_name = "base_mailer" + def welcome(hash = {}) headers['X-SPAM'] = "Not SPAM" mail(DEFAULT_HEADERS.merge(hash)) @@ -63,11 +65,11 @@ class BaseTest < ActiveSupport::TestCase end test "method call to mail does not raise error" do - assert_nothing_raised { TestMailer.deliver_welcome } + assert_nothing_raised { BaseMailer.deliver_welcome } end test "mail() should set the headers of the mail message" do - email = TestMailer.deliver_welcome + email = BaseMailer.deliver_welcome assert_equal(email.to, ['mikel@test.lindsaar.net']) assert_equal(email.from, ['jose@test.plataformatec.com']) assert_equal(email.subject, 'The first email on new API!') @@ -75,7 +77,7 @@ class BaseTest < ActiveSupport::TestCase test "mail() with bcc, cc, content_type, charset, mime_version, reply_to and date" do @time = Time.now - email = TestMailer.deliver_welcome(:bcc => 'bcc@test.lindsaar.net', + email = BaseMailer.deliver_welcome(:bcc => 'bcc@test.lindsaar.net', :cc => 'cc@test.lindsaar.net', :content_type => 'multipart/mixed', :charset => 'iso-8559-1', @@ -91,68 +93,70 @@ class BaseTest < ActiveSupport::TestCase assert_equal(email.date, @time) end + test "mail() renders the template using the method being processed" do + email = BaseMailer.deliver_welcome + assert_equal("Welcome", email.body.encoded) + end + test "custom headers" do - email = TestMailer.deliver_welcome + email = BaseMailer.deliver_welcome assert_equal("Not SPAM", email['X-SPAM'].decoded) end test "attachment with content" do - email = TestMailer.deliver_attachment_with_content + email = BaseMailer.deliver_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) end test "attachment gets content type from filename" do - email = TestMailer.deliver_attachment_with_content + email = BaseMailer.deliver_attachment_with_content assert_equal('invoice.pdf', email.attachments[0].filename) end test "attachment with hash" do - email = TestMailer.deliver_attachment_with_hash + email = BaseMailer.deliver_attachment_with_hash assert_equal(1, email.attachments.length) assert_equal('invoice.jpg', email.attachments[0].filename) assert_equal("\312\213\254\232)b", email.attachments['invoice.jpg'].decoded) end + # test "mail sets proper content type when attachment is included" do + # email = BaseMailer.deliver_attachment_with_content + # assert_equal(1, email.attachments.length) + # assert_equal("multipart/mixed", email.content_type) + # end + test "uses default charset from class" do - swap TestMailer, :default_charset => "US-ASCII" do - email = TestMailer.deliver_welcome + swap BaseMailer, :default_charset => "US-ASCII" do + email = BaseMailer.deliver_welcome assert_equal("US-ASCII", email.charset) - email = TestMailer.deliver_welcome(:charset => "iso-8559-1") + email = BaseMailer.deliver_welcome(:charset => "iso-8559-1") assert_equal("iso-8559-1", email.charset) end end - test "uses default content type from class" do - swap TestMailer, :default_content_type => "text/html" do - email = TestMailer.deliver_welcome - assert_equal("text/html", email.mime_type) - - email = TestMailer.deliver_welcome(:content_type => "application/xml") - assert_equal("application/xml", email.mime_type) - end - end - test "uses default mime version from class" do - swap TestMailer, :default_mime_version => "2.0" do - email = TestMailer.deliver_welcome + swap BaseMailer, :default_mime_version => "2.0" do + email = BaseMailer.deliver_welcome assert_equal("2.0", email.mime_version) - email = TestMailer.deliver_welcome(:mime_version => "1.0") + email = BaseMailer.deliver_welcome(:mime_version => "1.0") assert_equal("1.0", email.mime_version) end end - # def test_that_class_defaults_are_set_on_instantiation - # pending - # end - # - # def test_should_set_the_subject_from_i18n - # pending - # end + test "subject gets default from I18n" do + email = BaseMailer.deliver_welcome(:subject => nil) + assert_equal "Welcome", email.subject + I18n.backend.store_translations('en', :actionmailer => {:base_mailer => {:welcome => {:subject => "New Subject!"}}}) + email = BaseMailer.deliver_welcome(:subject => nil) + assert_equal "New Subject!", email.subject + end + protected # Execute the block setting the given values and restoring old values after diff --git a/actionmailer/test/fixtures/base_mailer/welcome.erb b/actionmailer/test/fixtures/base_mailer/welcome.erb new file mode 100644 index 00000000000..01f3f00c633 --- /dev/null +++ b/actionmailer/test/fixtures/base_mailer/welcome.erb @@ -0,0 +1 @@ +Welcome \ No newline at end of file diff --git a/actionmailer/test/fixtures/test_mailer/body_ivar.erb b/actionmailer/test/fixtures/test_mailer/body_ivar.erb deleted file mode 100644 index 1421e5c9083..00000000000 --- a/actionmailer/test/fixtures/test_mailer/body_ivar.erb +++ /dev/null @@ -1,2 +0,0 @@ -body: <%= @body %> -bar: <%= @bar %> \ No newline at end of file diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index ec820e34643..62422fb4b38 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -416,15 +416,6 @@ class ActionMailerTest < Test::Unit::TestCase assert_equal expected.encoded, delivered.encoded end - def test_subject_with_i18n - assert_nothing_raised { TestMailer.deliver_subject_with_i18n(@recipient) } - assert_equal "Subject with i18n", ActionMailer::Base.deliveries.first.subject.to_s - - I18n.backend.store_translations('en', :actionmailer => {:test_mailer => {:subject_with_i18n => {:subject => "New Subject!"}}}) - assert_nothing_raised { TestMailer.deliver_subject_with_i18n(@recipient) } - assert_equal "New Subject!", ActionMailer::Base.deliveries.last.subject.to_s - end - def test_custom_template expected = new_mail expected.to = @recipient diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb index ec278ca7830..7c33f1334a4 100644 --- a/actionpack/lib/action_view/render/rendering.rb +++ b/actionpack/lib/action_view/render/rendering.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/object/try' + module ActionView module Rendering # Returns the result of a render that's dictated by the options hash. The primary options are: From 1cd55928c6f638affeb5d89293f478817675d7b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Fri, 22 Jan 2010 13:56:06 +0100 Subject: [PATCH 25/63] First work on implicit multipart. --- actionmailer/lib/action_mailer/base.rb | 47 ++++++++++--------- actionmailer/test/base_test.rb | 18 ++++++- .../base_mailer/implicit_multipart.html.erb | 1 + .../base_mailer/implicit_multipart.text.erb | 1 + 4 files changed, 43 insertions(+), 24 deletions(-) create mode 100644 actionmailer/test/fixtures/base_mailer/implicit_multipart.html.erb create mode 100644 actionmailer/test/fixtures/base_mailer/implicit_multipart.text.erb diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 1f432c2a804..fb1dab7c39c 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -401,21 +401,18 @@ module ActionMailer #:nodoc: :default => action_name.humanize) # Give preference to headers and fallbacks to the ones set in mail - headers[:content_type] ||= m.content_type - headers[:charset] ||= m.charset - headers[:mime_version] ||= m.mime_version + content_type = headers[:content_type] || m.content_type + charset = headers[:charset] || m.charset + mime_version = headers[:mime_version] || m.mime_version + body = nil - m.content_type = headers[:content_type] || self.class.default_content_type.dup - m.charset = headers[:charset] || self.class.default_charset.dup - m.mime_version = headers[:mime_version] || self.class.default_mime_version.dup - - m.subject ||= quote_if_necessary(headers[:subject], m.charset) if headers[:subject] - m.to ||= quote_address_if_necessary(headers[:to], m.charset) if headers[:to] - m.from ||= quote_address_if_necessary(headers[:from], m.charset) if headers[:from] - m.cc ||= quote_address_if_necessary(headers[:cc], m.charset) if headers[:cc] - m.bcc ||= quote_address_if_necessary(headers[:bcc], m.charset) if headers[:bcc] - m.reply_to ||= quote_address_if_necessary(headers[:reply_to], m.charset) if headers[:reply_to] - m.date ||= headers[:date] if headers[:date] + m.subject ||= quote_if_necessary(headers[:subject], charset) if headers[:subject] + m.to ||= quote_address_if_necessary(headers[:to], charset) if headers[:to] + m.from ||= quote_address_if_necessary(headers[:from], charset) if headers[:from] + m.cc ||= quote_address_if_necessary(headers[:cc], charset) if headers[:cc] + m.bcc ||= quote_address_if_necessary(headers[:bcc], charset) if headers[:bcc] + m.reply_to ||= quote_address_if_necessary(headers[:reply_to], charset) if headers[:reply_to] + m.date ||= headers[:date] if headers[:date] if block_given? # Do something @@ -424,25 +421,29 @@ module ActionMailer #:nodoc: templates = self.class.template_root.find_all(action_name, {}, mailer_name) if templates.size == 1 - unless headers[:content_type] - proper_charset = m.charset - m.content_type = templates[0].mime_type.to_s - m.charset = proper_charset - end + content_type ||= templates[0].mime_type.to_s m.body = render_to_body(:_template => templates[0]) else + content_type ||= "multipart/alternate" + templates.each do |template| part = Mail::Part.new part.content_type = template.mime_type.to_s - part.charset = m.charset + part.charset = charset part.body = render_to_body(:_template => template) + m.add_part(part) end end end + + m.content_type = content_type || self.class.default_content_type.dup + m.charset = charset || self.class.default_charset.dup + m.mime_version = mime_version || self.class.default_mime_version.dup + + # TODO Add me and test me + # m.body.set_sort_order(headers[:parts_order] || self.class.default_implicit_parts_order.dup) + # m.body.sort_parts! - m.body.set_sort_order(headers[:parts_order] || self.class.default_implicit_parts_order.dup) - - # TODO: m.body.sort_parts! m end diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index f22b20a6bab..8502b29ba4d 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -62,6 +62,10 @@ class BaseTest < ActiveSupport::TestCase :transfer_encoding => "base64" } mail(DEFAULT_HEADERS) end + + def implicit_multipart + mail(DEFAULT_HEADERS) + end end test "method call to mail does not raise error" do @@ -156,7 +160,19 @@ class BaseTest < ActiveSupport::TestCase email = BaseMailer.deliver_welcome(:subject => nil) assert_equal "New Subject!", email.subject end - + + test "implicit multipart tests" do + require 'ruby-debug' + $BREAK = true + email = BaseMailer.deliver_implicit_multipart + + assert_equal(2, email.parts.size) + + assert_equal("multipart/alternate", email.mime_type) + assert_equal("text/plain", email.parts[0].mime_type) + assert_equal("text/html", email.parts[1].mime_type) + end + protected # Execute the block setting the given values and restoring old values after diff --git a/actionmailer/test/fixtures/base_mailer/implicit_multipart.html.erb b/actionmailer/test/fixtures/base_mailer/implicit_multipart.html.erb new file mode 100644 index 00000000000..23745cd282c --- /dev/null +++ b/actionmailer/test/fixtures/base_mailer/implicit_multipart.html.erb @@ -0,0 +1 @@ +HTML Implicit Multipart \ No newline at end of file diff --git a/actionmailer/test/fixtures/base_mailer/implicit_multipart.text.erb b/actionmailer/test/fixtures/base_mailer/implicit_multipart.text.erb new file mode 100644 index 00000000000..d51437fc728 --- /dev/null +++ b/actionmailer/test/fixtures/base_mailer/implicit_multipart.text.erb @@ -0,0 +1 @@ +TEXT Implicit Multipart \ No newline at end of file From 951397b4a2143eee4b900356dab525aed99430ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Fri, 22 Jan 2010 14:38:41 +0100 Subject: [PATCH 26/63] Get implicit multipart and attachments working together. --- actionmailer/lib/action_mailer/base.rb | 50 +++++----- .../lib/action_mailer/delivery_methods.rb | 2 + actionmailer/test/base_test.rb | 96 +++++++++++++++---- .../base_mailer/attachment_with_content.erb | 1 + 4 files changed, 109 insertions(+), 40 deletions(-) create mode 100644 actionmailer/test/fixtures/base_mailer/attachment_with_content.erb diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index fb1dab7c39c..98e559154af 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -390,6 +390,7 @@ module ActionMailer #:nodoc: end end + # TODO Add new delivery method goodness def mail(headers = {}) # Guard flag to prevent both the old and the new API from firing # Should be removed when old API is deprecated @@ -402,9 +403,8 @@ module ActionMailer #:nodoc: # Give preference to headers and fallbacks to the ones set in mail content_type = headers[:content_type] || m.content_type - charset = headers[:charset] || m.charset - mime_version = headers[:mime_version] || m.mime_version - body = nil + charset = headers[:charset] || m.charset || self.class.default_charset.dup + mime_version = headers[:mime_version] || m.mime_version || self.class.default_mime_version.dup m.subject ||= quote_if_necessary(headers[:subject], charset) if headers[:subject] m.to ||= quote_address_if_necessary(headers[:to], charset) if headers[:to] @@ -420,35 +420,41 @@ module ActionMailer #:nodoc: # TODO Ensure that we don't need to pass I18n.locale as detail templates = self.class.template_root.find_all(action_name, {}, mailer_name) - if templates.size == 1 + if templates.size == 1 && !m.has_attachments? content_type ||= templates[0].mime_type.to_s m.body = render_to_body(:_template => templates[0]) + elsif templates.size > 1 && m.has_attachments? + container = Mail::Part.new + container.content_type = "multipart/alternate" + templates.each { |t| insert_part(container, t, charset) } + m.add_part(container) else - content_type ||= "multipart/alternate" - - templates.each do |template| - part = Mail::Part.new - part.content_type = template.mime_type.to_s - part.charset = charset - part.body = render_to_body(:_template => template) - m.add_part(part) - end + templates.each { |t| insert_part(m, t, charset) } end + + content_type ||= (m.has_attachments? ? "multipart/mixed" : "multipart/alternate") end - + + # Check if the content_type was not overwriten along the way and if so, + # fallback to default. m.content_type = content_type || self.class.default_content_type.dup - m.charset = charset || self.class.default_charset.dup - m.mime_version = mime_version || self.class.default_mime_version.dup - - # TODO Add me and test me - # m.body.set_sort_order(headers[:parts_order] || self.class.default_implicit_parts_order.dup) - # m.body.sort_parts! + m.charset = charset + m.mime_version = mime_version + + unless m.parts.empty? + m.body.set_sort_order(headers[:parts_order] || self.class.default_implicit_parts_order.dup) + m.body.sort_parts! + end m end - def fill_in_part(part, template, charset) - + def insert_part(container, template, charset) + part = Mail::Part.new + part.content_type = template.mime_type.to_s + part.charset = charset + part.body = render_to_body(:_template => template) + container.add_part(part) end # Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer diff --git a/actionmailer/lib/action_mailer/delivery_methods.rb b/actionmailer/lib/action_mailer/delivery_methods.rb index c8c4148353c..5883e446f28 100644 --- a/actionmailer/lib/action_mailer/delivery_methods.rb +++ b/actionmailer/lib/action_mailer/delivery_methods.rb @@ -27,6 +27,7 @@ module ActionMailer end module ClassMethods + # TODO Make me class inheritable def delivery_settings @@delivery_settings ||= Hash.new { |h,k| h[k] = {} } end @@ -51,6 +52,7 @@ module ActionMailer protected + # TODO Get rid of this method missing magic def method_missing(method_symbol, *parameters) #:nodoc: if match = matches_settings_method?(method_symbol) if match[2] diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 8502b29ba4d..df0eed695d9 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -3,13 +3,13 @@ require 'abstract_unit' # class Notifier < ActionMailer::Base # delivers_from 'notifications@example.com' -# +# # def welcome(user) # @user = user # available to the view # mail(:subject => 'Welcome!', :to => user.email_address) # # auto renders both welcome.text.erb and welcome.html.erb # end -# +# # def goodbye(user) # headers["X-SPAM"] = 'Not-SPAM' # mail(:subject => 'Goodbye', :to => user.email_address) do |format| @@ -17,7 +17,7 @@ require 'abstract_unit' # format.text # goodbye.text.erb # end # end -# +# # def surprise(user, gift) # attachments[gift.name] = File.read(gift.path) # mail(:subject => 'Surprise!', :to => user.email_address) do |format| @@ -25,17 +25,17 @@ require 'abstract_unit' # format.text(:transfer_encoding => "base64") # surprise.text.erb # end # end -# +# # def special_surprise(user, gift) # attachments[gift.name] = { :content_type => "application/x-gzip", :content => File.read(gift.path) } # mail(:to => 'special@example.com') # subject not required # # auto renders both special_surprise.text.erb and special_surprise.html.erb # end # end -# +# # mail = Notifier.welcome(user) # => returns a Mail object # mail.deliver -# +# # Notifier.welcome(user).deliver # => creates and sends the Mail in one step class BaseTest < ActiveSupport::TestCase DEFAULT_HEADERS = { @@ -44,6 +44,8 @@ class BaseTest < ActiveSupport::TestCase :subject => 'The first email on new API!' } + # TODO Think on the simple case where I want to send an e-mail + # with attachment and small text (without need to add a template). class BaseMailer < ActionMailer::Base self.mailer_name = "base_mailer" @@ -63,8 +65,9 @@ class BaseTest < ActiveSupport::TestCase mail(DEFAULT_HEADERS) end - def implicit_multipart - mail(DEFAULT_HEADERS) + def implicit_multipart(hash = {}) + attachments['invoice.pdf'] = 'This is test File content' if hash.delete(:attachments) + mail(DEFAULT_HEADERS.merge(hash)) end end @@ -72,6 +75,7 @@ class BaseTest < ActiveSupport::TestCase assert_nothing_raised { BaseMailer.deliver_welcome } end + # Basic mail usage without block test "mail() should set the headers of the mail message" do email = BaseMailer.deliver_welcome assert_equal(email.to, ['mikel@test.lindsaar.net']) @@ -102,11 +106,13 @@ class BaseTest < ActiveSupport::TestCase assert_equal("Welcome", email.body.encoded) end + # Custom headers test "custom headers" do email = BaseMailer.deliver_welcome assert_equal("Not SPAM", email['X-SPAM'].decoded) end + # Attachments test "attachment with content" do email = BaseMailer.deliver_attachment_with_content assert_equal(1, email.attachments.length) @@ -126,12 +132,22 @@ class BaseTest < ActiveSupport::TestCase assert_equal("\312\213\254\232)b", email.attachments['invoice.jpg'].decoded) end - # test "mail sets proper content type when attachment is included" do - # email = BaseMailer.deliver_attachment_with_content - # assert_equal(1, email.attachments.length) - # assert_equal("multipart/mixed", email.content_type) - # end + test "sets mime type to multipart/mixed when attachment is included" do + email = BaseMailer.deliver_attachment_with_content + assert_equal(1, email.attachments.length) + assert_equal("multipart/mixed", email.mime_type) + end + test "adds the rendered template as part" do + email = BaseMailer.deliver_attachment_with_content + assert_equal(2, email.parts.length) + assert_equal("text/html", email.parts[0].mime_type) + assert_equal("Attachment with content", email.parts[0].body.encoded) + assert_equal("application/pdf", email.parts[1].mime_type) + assert_equal("VGhpcyBpcyB0ZXN0IEZpbGUgY29udGVudA==\r\n", email.parts[1].body.encoded) + end + + # Defaults values test "uses default charset from class" do swap BaseMailer, :default_charset => "US-ASCII" do email = BaseMailer.deliver_welcome @@ -142,6 +158,16 @@ class BaseTest < ActiveSupport::TestCase end end + test "uses default content type from class" do + swap BaseMailer, :default_content_type => "text/html" do + email = BaseMailer.deliver_welcome + assert_equal("text/html", email.mime_type) + + email = BaseMailer.deliver_welcome(:content_type => "text/plain") + assert_equal("text/plain", email.mime_type) + end + end + test "uses default mime version from class" do swap BaseMailer, :default_mime_version => "2.0" do email = BaseMailer.deliver_welcome @@ -161,18 +187,52 @@ class BaseTest < ActiveSupport::TestCase assert_equal "New Subject!", email.subject end + # Implicit multipart test "implicit multipart tests" do - require 'ruby-debug' - $BREAK = true email = BaseMailer.deliver_implicit_multipart - assert_equal(2, email.parts.size) - assert_equal("multipart/alternate", email.mime_type) assert_equal("text/plain", email.parts[0].mime_type) - assert_equal("text/html", email.parts[1].mime_type) + assert_equal("TEXT Implicit Multipart", email.parts[0].body.encoded) + assert_equal("text/html", email.parts[1].mime_type) + assert_equal("HTML Implicit Multipart", email.parts[1].body.encoded) end + test "implicit multipart tests with sort order" do + order = ["text/html", "text/plain"] + swap BaseMailer, :default_implicit_parts_order => order do + email = BaseMailer.deliver_implicit_multipart + assert_equal("text/html", email.parts[0].mime_type) + assert_equal("text/plain", email.parts[1].mime_type) + + email = BaseMailer.deliver_implicit_multipart(:parts_order => order.reverse) + assert_equal("text/plain", email.parts[0].mime_type) + assert_equal("text/html", email.parts[1].mime_type) + end + end + + test "implicit multipart with attachments creates nested parts" do + email = BaseMailer.deliver_implicit_multipart(:attachments => true) + assert_equal("application/pdf", email.parts[0].mime_type) + assert_equal("multipart/alternate", email.parts[1].mime_type) + assert_equal("text/plain", email.parts[1].parts[0].mime_type) + assert_equal("TEXT Implicit Multipart", email.parts[1].parts[0].body.encoded) + assert_equal("text/html", email.parts[1].parts[1].mime_type) + assert_equal("HTML Implicit Multipart", email.parts[1].parts[1].body.encoded) + end + + # TODO This should be fixed in mail. Sort parts should be recursive. + # test "implicit multipart with attachments and sort order" do + # order = ["text/html", "text/plain"] + # swap BaseMailer, :default_implicit_parts_order => order do + # email = BaseMailer.deliver_implicit_multipart(:attachments => true) + # assert_equal("application/pdf", email.parts[0].mime_type) + # assert_equal("multipart/alternate", email.parts[1].mime_type) + # assert_equal("text/plain", email.parts[1].parts[1].mime_type) + # assert_equal("text/html", email.parts[1].parts[0].mime_type) + # end + # end + protected # Execute the block setting the given values and restoring old values after diff --git a/actionmailer/test/fixtures/base_mailer/attachment_with_content.erb b/actionmailer/test/fixtures/base_mailer/attachment_with_content.erb new file mode 100644 index 00000000000..deb9dbd03b0 --- /dev/null +++ b/actionmailer/test/fixtures/base_mailer/attachment_with_content.erb @@ -0,0 +1 @@ +Attachment with content \ No newline at end of file From bd24c75fc619fa80af03662cc4dd18fc9b70cafb Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar Date: Sat, 23 Jan 2010 18:45:59 +1100 Subject: [PATCH 27/63] Updated Gemspec to mail 2.0.3 from gemcutter --- actionmailer/actionmailer.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec index 9a8c1df9e88..576ca973347 100644 --- a/actionmailer/actionmailer.gemspec +++ b/actionmailer/actionmailer.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |s| s.homepage = "http://www.rubyonrails.org" s.add_dependency('actionpack', '= 3.0.pre') - s.add_dependency('mail', '~> 2.0.2') + s.add_dependency('mail', '~> 2.0.3') s.files = Dir['CHANGELOG', 'README', 'MIT-LICENSE', 'lib/**/*'] s.has_rdoc = true From ddfc0725a062880131745a1c529e94541d4c9ee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sat, 23 Jan 2010 10:23:06 +0100 Subject: [PATCH 28/63] Added AbstractController::Collector. --- actionpack/lib/abstract_controller.rb | 1 + .../lib/abstract_controller/collector.rb | 30 ++++++++++ .../action_controller/metal/mime_responds.rb | 29 +--------- actionpack/test/abstract/collector_test.rb | 57 +++++++++++++++++++ 4 files changed, 91 insertions(+), 26 deletions(-) create mode 100644 actionpack/lib/abstract_controller/collector.rb create mode 100644 actionpack/test/abstract/collector_test.rb diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb index 725d8fb8fc3..2c2ef16622e 100644 --- a/actionpack/lib/abstract_controller.rb +++ b/actionpack/lib/abstract_controller.rb @@ -10,6 +10,7 @@ module AbstractController autoload :Base autoload :Callbacks + autoload :Collector autoload :Helpers autoload :Layouts autoload :LocalizedCache diff --git a/actionpack/lib/abstract_controller/collector.rb b/actionpack/lib/abstract_controller/collector.rb new file mode 100644 index 00000000000..d429333661c --- /dev/null +++ b/actionpack/lib/abstract_controller/collector.rb @@ -0,0 +1,30 @@ +module AbstractController + module Collector + def self.generate_method_for_mime(mime) + sym = mime.is_a?(Symbol) ? mime : mime.to_sym + const = sym.to_s.upcase + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{sym}(*args, &block) # def html(*args, &block) + custom(Mime::#{const}, *args, &block) # custom(Mime::HTML, *args, &block) + end # end + RUBY + end + + Mime::SET.each do |mime| + generate_method_for_mime(mime) + end + + protected + + def method_missing(symbol, &block) + mime_constant = Mime.const_get(symbol.to_s.upcase) + + if Mime::SET.include?(mime_constant) + AbstractController::Collector.generate_method_for_mime(mime_constant) + send(symbol, &block) + else + super + end + end + end +end \ No newline at end of file diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index 4c02677729b..08599d660eb 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -1,3 +1,5 @@ +require 'abstract_controller/collector' + module ActionController #:nodoc: module MimeResponds #:nodoc: extend ActiveSupport::Concern @@ -265,6 +267,7 @@ module ActionController #:nodoc: end class Collector #:nodoc: + include AbstractController::Collector attr_accessor :order def initialize(&block) @@ -289,32 +292,6 @@ module ActionController #:nodoc: def response_for(mime) @responses[mime] || @responses[Mime::ALL] || @default_response end - - def self.generate_method_for_mime(mime) - sym = mime.is_a?(Symbol) ? mime : mime.to_sym - const = sym.to_s.upcase - class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def #{sym}(&block) # def html(&block) - custom(Mime::#{const}, &block) # custom(Mime::HTML, &block) - end # end - RUBY - end - - Mime::SET.each do |mime| - generate_method_for_mime(mime) - end - - def method_missing(symbol, &block) - mime_constant = Mime.const_get(symbol.to_s.upcase) - - if Mime::SET.include?(mime_constant) - self.class.generate_method_for_mime(mime_constant) - send(symbol, &block) - else - super - end - end - end end end diff --git a/actionpack/test/abstract/collector_test.rb b/actionpack/test/abstract/collector_test.rb new file mode 100644 index 00000000000..2ebcebbbb70 --- /dev/null +++ b/actionpack/test/abstract/collector_test.rb @@ -0,0 +1,57 @@ +require 'abstract_unit' + +module AbstractController + module Testing + class MyCollector + include Collector + attr_accessor :responses + + def initialize + @responses = [] + end + + def custom(mime, *args, &block) + @responses << [mime, args, block] + end + end + + class TestCollector < ActiveSupport::TestCase + test "responds to default mime types" do + collector = MyCollector.new + assert_respond_to collector, :html + assert_respond_to collector, :text + end + + test "does not respond to unknown mime types" do + collector = MyCollector.new + assert !collector.respond_to?(:unknown) + end + + test "register mime types on method missing" do + AbstractController::Collector.send(:remove_method, :js) + collector = MyCollector.new + assert !collector.respond_to?(:js) + collector.js + assert_respond_to collector, :js + end + + test "does not register unknown mime types" do + collector = MyCollector.new + assert_raise NameError do + collector.unknown + end + end + + test "generated methods call custom with args received" do + collector = MyCollector.new + collector.html + collector.text(:foo) + collector.js(:bar) { :baz } + assert_equal [Mime::HTML, [], nil], collector.responses[0] + assert_equal [Mime::TEXT, [:foo], nil], collector.responses[1] + assert_equal [Mime::JS, [:bar]], collector.responses[2][0,2] + assert_equal :baz, collector.responses[2][2].call + end + end + end +end \ No newline at end of file From 5c3ef8c17dba23d000d301467b1a620811c940b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sat, 23 Jan 2010 10:24:19 +0100 Subject: [PATCH 29/63] Refactor subject with i18n. --- actionmailer/lib/action_mailer/base.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 98e559154af..94e20d4b63a 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -398,8 +398,7 @@ module ActionMailer #:nodoc: m = @message # Get default subject from I18n if none is set - headers[:subject] ||= I18n.t(:subject, :scope => [:actionmailer, mailer_name, action_name], - :default => action_name.humanize) + headers[:subject] ||= default_subject # Give preference to headers and fallbacks to the ones set in mail content_type = headers[:content_type] || m.content_type @@ -418,7 +417,7 @@ module ActionMailer #:nodoc: # Do something else # TODO Ensure that we don't need to pass I18n.locale as detail - templates = self.class.template_root.find_all(action_name, {}, mailer_name) + templates = self.class.template_root.find_all(action_name, {}, self.class.mailer_name) if templates.size == 1 && !m.has_attachments? content_type ||= templates[0].mime_type.to_s @@ -449,6 +448,11 @@ module ActionMailer #:nodoc: m end + def default_subject + mailer_scope = self.class.mailer_name.gsub('/', '.') + I18n.t(:subject, :scope => [:actionmailer, mailer_scope, action_name], :default => action_name.humanize) + end + def insert_part(container, template, charset) part = Mail::Part.new part.content_type = template.mime_type.to_s From 502028a32bfa46ad18337d2a69f03e8b5dec3c4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sat, 23 Jan 2010 10:29:22 +0100 Subject: [PATCH 30/63] Move double render check out of AbstractController. --- actionpack/lib/abstract_controller/rendering.rb | 4 ---- actionpack/lib/action_controller/metal/rendering.rb | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index a168b1b4c55..1dec3f2c3e6 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -41,10 +41,6 @@ module AbstractController # Mostly abstracts the fact that calling render twice is a DoubleRenderError. # Delegates render_to_body and sticks the result in self.response_body. def render(*args, &block) - if response_body - raise AbstractController::DoubleRenderError - end - options = _normalize_options(*args, &block) self.response_body = render_to_body(options) end diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb index 72e2bbd00ea..8f03035b2b9 100644 --- a/actionpack/lib/action_controller/metal/rendering.rb +++ b/actionpack/lib/action_controller/metal/rendering.rb @@ -13,6 +13,10 @@ module ActionController end def render(*args) + if response_body + raise ::AbstractController::DoubleRenderError + end + args << {} unless args.last.is_a?(Hash) super(*args) self.content_type ||= args.last[:_template].mime_type.to_s From c6b16260fe3d1435848e78415bd0b40c10ad7424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sat, 23 Jan 2010 21:37:34 +1100 Subject: [PATCH 31/63] Added basic explicit multipart rendering and tests --- actionmailer/lib/action_mailer.rb | 1 + actionmailer/lib/action_mailer/base.rb | 28 ++++++-- actionmailer/lib/action_mailer/mail_helper.rb | 2 +- actionmailer/test/base_test.rb | 67 ++++++++++++++++--- 4 files changed, 79 insertions(+), 19 deletions(-) diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 37c95baea77..67466e15e2b 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -31,6 +31,7 @@ module ActionMailer extend ::ActiveSupport::Autoload autoload :AdvAttrAccessor + autoload :Collector autoload :Base autoload :DeliveryMethods autoload :DeprecatedApi diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 94e20d4b63a..28e4c88b5a3 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -297,8 +297,9 @@ module ActionMailer #:nodoc: self.default_implicit_parts_order = [ "text/plain", "text/enriched", "text/html" ] # Expose the internal Mail message + # TODO: Make this an _internal ivar? attr_reader :message - + def headers(args=nil) if args ActiveSupport::Deprecation.warn "headers(Hash) is deprecated, please do headers[key] = value instead", caller @@ -413,12 +414,25 @@ module ActionMailer #:nodoc: m.reply_to ||= quote_address_if_necessary(headers[:reply_to], charset) if headers[:reply_to] m.date ||= headers[:date] if headers[:date] - if block_given? - # Do something + if headers[:body] + templates = [ActionView::Template::Text.new(headers[:body], format_for_text)] + elsif block_given? + collector = ActionMailer::Collector.new(self, {:charset => charset}) do + render action_name + end + yield collector + + collector.responses.each do |response| + part = Mail::Part.new(response) + m.add_part(part) + end + else # TODO Ensure that we don't need to pass I18n.locale as detail templates = self.class.template_root.find_all(action_name, {}, self.class.mailer_name) - + end + + if templates if templates.size == 1 && !m.has_attachments? content_type ||= templates[0].mime_type.to_s m.body = render_to_body(:_template => templates[0]) @@ -430,17 +444,17 @@ module ActionMailer #:nodoc: else templates.each { |t| insert_part(m, t, charset) } end - - content_type ||= (m.has_attachments? ? "multipart/mixed" : "multipart/alternate") end + content_type ||= (m.has_attachments? ? "multipart/mixed" : "multipart/alternate") + # Check if the content_type was not overwriten along the way and if so, # fallback to default. m.content_type = content_type || self.class.default_content_type.dup m.charset = charset m.mime_version = mime_version - unless m.parts.empty? + if m.parts.present? && templates m.body.set_sort_order(headers[:parts_order] || self.class.default_implicit_parts_order.dup) m.body.sort_parts! end diff --git a/actionmailer/lib/action_mailer/mail_helper.rb b/actionmailer/lib/action_mailer/mail_helper.rb index 45ba6f07142..adba94cbef0 100644 --- a/actionmailer/lib/action_mailer/mail_helper.rb +++ b/actionmailer/lib/action_mailer/mail_helper.rb @@ -18,7 +18,7 @@ module ActionMailer # Access the mailer instance. def mailer #:nodoc: - @controller + @_controller end # Access the message instance. diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index df0eed695d9..bae50868aeb 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -69,6 +69,14 @@ class BaseTest < ActiveSupport::TestCase attachments['invoice.pdf'] = 'This is test File content' if hash.delete(:attachments) mail(DEFAULT_HEADERS.merge(hash)) end + + def explicit_multipart(hash = {}) + mail(DEFAULT_HEADERS.merge(hash)) do |format| + format.text { render :text => "TEXT Explicit Multipart" } + format.html { render :text => "HTML Explicit Multipart" } + end + end + end test "method call to mail does not raise error" do @@ -106,6 +114,11 @@ class BaseTest < ActiveSupport::TestCase assert_equal("Welcome", email.body.encoded) end + test "can pass in :body to the mail method hash" do + email = BaseMailer.deliver_welcome(:body => "Hello there") + assert_equal("Hello there", email.body.encoded) + end + # Custom headers test "custom headers" do email = BaseMailer.deliver_welcome @@ -221,17 +234,49 @@ class BaseTest < ActiveSupport::TestCase assert_equal("HTML Implicit Multipart", email.parts[1].parts[1].body.encoded) end - # TODO This should be fixed in mail. Sort parts should be recursive. - # test "implicit multipart with attachments and sort order" do - # order = ["text/html", "text/plain"] - # swap BaseMailer, :default_implicit_parts_order => order do - # email = BaseMailer.deliver_implicit_multipart(:attachments => true) - # assert_equal("application/pdf", email.parts[0].mime_type) - # assert_equal("multipart/alternate", email.parts[1].mime_type) - # assert_equal("text/plain", email.parts[1].parts[1].mime_type) - # assert_equal("text/html", email.parts[1].parts[0].mime_type) - # end - # end + test "implicit multipart with attachments and sort order" do + order = ["text/html", "text/plain"] + swap BaseMailer, :default_implicit_parts_order => order do + email = BaseMailer.deliver_implicit_multipart(:attachments => true) + assert_equal("application/pdf", email.parts[0].mime_type) + assert_equal("multipart/alternate", email.parts[1].mime_type) + assert_equal("text/plain", email.parts[1].parts[1].mime_type) + assert_equal("text/html", email.parts[1].parts[0].mime_type) + end + end + + test "explicit multipart tests" do + email = BaseMailer.deliver_explicit_multipart + assert_equal(2, email.parts.size) + assert_equal("multipart/alternate", email.mime_type) + assert_equal("text/plain", email.parts[0].mime_type) + assert_equal("TEXT Explicit Multipart", email.parts[0].body.encoded) + assert_equal("text/html", email.parts[1].mime_type) + assert_equal("HTML Explicit Multipart", email.parts[1].body.encoded) + end + + test "explicit multipart does not sort order" do + order = ["text/html", "text/plain"] + swap BaseMailer, :default_implicit_parts_order => order do + email = BaseMailer.deliver_explicit_multipart + assert_equal("text/plain", email.parts[0].mime_type) + assert_equal("text/html", email.parts[1].mime_type) + + email = BaseMailer.deliver_explicit_multipart(:parts_order => order.reverse) + assert_equal("text/plain", email.parts[0].mime_type) + assert_equal("text/html", email.parts[1].mime_type) + end + end + + #test "explicit multipart with attachments creates nested parts" do + # email = BaseMailer.deliver_explicit_multipart(:attachments => true) + # assert_equal("application/pdf", email.parts[0].mime_type) + # assert_equal("multipart/alternate", email.parts[1].mime_type) + # assert_equal("text/plain", email.parts[1].parts[0].mime_type) + # assert_equal("TEXT Implicit Multipart", email.parts[1].parts[0].body.encoded) + # assert_equal("text/html", email.parts[1].parts[1].mime_type) + # assert_equal("HTML Implicit Multipart", email.parts[1].parts[1].body.encoded) + #end protected From 5a19d24892b9d842ef7d27875eacecbbad71a9aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sat, 23 Jan 2010 21:50:36 +1100 Subject: [PATCH 32/63] Adding collector to ActionMailer --- actionmailer/lib/action_mailer/collector.rb | 32 +++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 actionmailer/lib/action_mailer/collector.rb diff --git a/actionmailer/lib/action_mailer/collector.rb b/actionmailer/lib/action_mailer/collector.rb new file mode 100644 index 00000000000..49c3f04badf --- /dev/null +++ b/actionmailer/lib/action_mailer/collector.rb @@ -0,0 +1,32 @@ +require 'abstract_controller/collector' + +module ActionMailer #:nodoc: + + class Collector + + include AbstractController::Collector + + attr_accessor :responses + + def initialize(context, options, &block) + @default_options = options + @default_render = block + @default_formats = context.formats + @context = context + @responses = [] + end + + def custom(mime, options={}, &block) + options = @default_options.merge(:content_type => mime.to_s).merge(options) + @context.formats = [mime.to_sym] + options[:body] = if block + block.call + else + @default_render.call + end + @responses << options + @context.formats = @default_formats + end + + end +end \ No newline at end of file From 6ba944608e527b8d7fc564a6e450e2d4c4355ddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sat, 23 Jan 2010 12:20:20 +0100 Subject: [PATCH 33/63] Make implicit and explicit templates pass through the same part creation process. --- actionmailer/lib/action_mailer/base.rb | 172 ++++++++++---------- actionmailer/lib/action_mailer/collector.rb | 25 +-- actionmailer/test/base_test.rb | 37 +++-- 3 files changed, 127 insertions(+), 107 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 28e4c88b5a3..5e61b8693a1 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -1,5 +1,5 @@ require 'active_support/core_ext/class' -require "active_support/core_ext/module/delegation" +require 'active_support/core_ext/module/delegation' require 'mail' require 'action_mailer/tmail_compat' @@ -391,90 +391,6 @@ module ActionMailer #:nodoc: end end - # TODO Add new delivery method goodness - def mail(headers = {}) - # Guard flag to prevent both the old and the new API from firing - # Should be removed when old API is deprecated - @mail_was_called = true - m = @message - - # Get default subject from I18n if none is set - headers[:subject] ||= default_subject - - # Give preference to headers and fallbacks to the ones set in mail - content_type = headers[:content_type] || m.content_type - charset = headers[:charset] || m.charset || self.class.default_charset.dup - mime_version = headers[:mime_version] || m.mime_version || self.class.default_mime_version.dup - - m.subject ||= quote_if_necessary(headers[:subject], charset) if headers[:subject] - m.to ||= quote_address_if_necessary(headers[:to], charset) if headers[:to] - m.from ||= quote_address_if_necessary(headers[:from], charset) if headers[:from] - m.cc ||= quote_address_if_necessary(headers[:cc], charset) if headers[:cc] - m.bcc ||= quote_address_if_necessary(headers[:bcc], charset) if headers[:bcc] - m.reply_to ||= quote_address_if_necessary(headers[:reply_to], charset) if headers[:reply_to] - m.date ||= headers[:date] if headers[:date] - - if headers[:body] - templates = [ActionView::Template::Text.new(headers[:body], format_for_text)] - elsif block_given? - collector = ActionMailer::Collector.new(self, {:charset => charset}) do - render action_name - end - yield collector - - collector.responses.each do |response| - part = Mail::Part.new(response) - m.add_part(part) - end - - else - # TODO Ensure that we don't need to pass I18n.locale as detail - templates = self.class.template_root.find_all(action_name, {}, self.class.mailer_name) - end - - if templates - if templates.size == 1 && !m.has_attachments? - content_type ||= templates[0].mime_type.to_s - m.body = render_to_body(:_template => templates[0]) - elsif templates.size > 1 && m.has_attachments? - container = Mail::Part.new - container.content_type = "multipart/alternate" - templates.each { |t| insert_part(container, t, charset) } - m.add_part(container) - else - templates.each { |t| insert_part(m, t, charset) } - end - end - - content_type ||= (m.has_attachments? ? "multipart/mixed" : "multipart/alternate") - - # Check if the content_type was not overwriten along the way and if so, - # fallback to default. - m.content_type = content_type || self.class.default_content_type.dup - m.charset = charset - m.mime_version = mime_version - - if m.parts.present? && templates - m.body.set_sort_order(headers[:parts_order] || self.class.default_implicit_parts_order.dup) - m.body.sort_parts! - end - - m - end - - def default_subject - mailer_scope = self.class.mailer_name.gsub('/', '.') - I18n.t(:subject, :scope => [:actionmailer, mailer_scope, action_name], :default => action_name.humanize) - end - - def insert_part(container, template, charset) - part = Mail::Part.new - part.content_type = template.mime_type.to_s - part.charset = charset - part.body = render_to_body(:_template => template) - container.add_part(part) - end - # Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer # will be initialized according to the named method. If not, the mailer will # remain uninitialized (useful when you only need to invoke the "receive" @@ -492,5 +408,91 @@ module ActionMailer #:nodoc: self.class.deliver(mail) end + # TODO Add new delivery method goodness + def mail(headers = {}) + # Guard flag to prevent both the old and the new API from firing + # Should be removed when old API is deprecated + @mail_was_called = true + + m, sort_parts = @message, true + + # Give preference to headers and fallback to the ones set in mail + content_type = headers[:content_type] || m.content_type + charset = headers[:charset] || m.charset || self.class.default_charset.dup + mime_version = headers[:mime_version] || m.mime_version || self.class.default_mime_version.dup + + headers[:subject] ||= default_subject + quote_fields(m, headers, charset) + + responses = if headers[:body] + [ { :body => headers[:body], :content_type => self.class.default_content_type.dup } ] + elsif block_given? + sort_parts = false + collector = ActionMailer::Collector.new(self) { render(action_name) } + yield(collector) + collector.responses + else + # TODO Ensure that we don't need to pass I18n.locale as detail + templates = self.class.template_root.find_all(action_name, {}, self.class.mailer_name) + + templates.map do |template| + { :body => render_to_body(:_template => template), + :content_type => template.mime_type.to_s } + end + end + + content_type ||= create_parts_from_responses(m, responses, charset) + + m.content_type = content_type + m.charset = charset + m.mime_version = mime_version + + if sort_parts && m.parts.present? + m.body.set_sort_order(headers[:parts_order] || self.class.default_implicit_parts_order.dup) + m.body.sort_parts! + end + + m + end + + protected + + def default_subject #:nodoc: + mailer_scope = self.class.mailer_name.gsub('/', '.') + I18n.t(:subject, :scope => [:actionmailer, mailer_scope, action_name], :default => action_name.humanize) + end + + def quote_fields(m, headers, charset) #:nodoc: + m.subject ||= quote_if_necessary(headers[:subject], charset) if headers[:subject] + m.to ||= quote_address_if_necessary(headers[:to], charset) if headers[:to] + m.from ||= quote_address_if_necessary(headers[:from], charset) if headers[:from] + m.cc ||= quote_address_if_necessary(headers[:cc], charset) if headers[:cc] + m.bcc ||= quote_address_if_necessary(headers[:bcc], charset) if headers[:bcc] + m.reply_to ||= quote_address_if_necessary(headers[:reply_to], charset) if headers[:reply_to] + m.date ||= headers[:date] if headers[:date] + end + + def create_parts_from_responses(m, responses, charset) #:nodoc: + if responses.size == 1 && !m.has_attachments? + m.body = responses[0][:body] + return responses[0][:content_type] + elsif responses.size > 1 && m.has_attachments? + container = Mail::Part.new + container.content_type = "multipart/alternate" + responses.each { |r| insert_part(container, r, charset) } + m.add_part(container) + else + responses.each { |r| insert_part(m, r, charset) } + end + + m.has_attachments? ? "multipart/mixed" : "multipart/alternate" + end + + def insert_part(container, response, charset) #:nodoc: + response[:charset] ||= charset + part = Mail::Part.new(response) + container.add_part(part) + end + end end diff --git a/actionmailer/lib/action_mailer/collector.rb b/actionmailer/lib/action_mailer/collector.rb index 49c3f04badf..0891b6f1236 100644 --- a/actionmailer/lib/action_mailer/collector.rb +++ b/actionmailer/lib/action_mailer/collector.rb @@ -1,23 +1,29 @@ require 'abstract_controller/collector' +require 'active_support/core_ext/hash/reverse_merge' +require 'active_support/core_ext/array/extract_options' module ActionMailer #:nodoc: - class Collector - include AbstractController::Collector - - attr_accessor :responses + attr_reader :responses - def initialize(context, options, &block) - @default_options = options - @default_render = block - @default_formats = context.formats + def initialize(context, &block) @context = context @responses = [] + @default_render = block + @default_formats = context.formats end + # TODO Test me + def any(*args, &block) + options = args.extract_options! + raise "You have to supply at least one format" if args.empty? + args.each { |type| send(type, options, &block) } + end + alias :all :any + def custom(mime, options={}, &block) - options = @default_options.merge(:content_type => mime.to_s).merge(options) + options.reverse_merge!(:content_type => mime.to_s) @context.formats = [mime.to_sym] options[:body] = if block block.call @@ -27,6 +33,5 @@ module ActionMailer #:nodoc: @responses << options @context.formats = @default_formats end - end end \ No newline at end of file diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index bae50868aeb..3033b2db0f3 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -54,9 +54,9 @@ class BaseTest < ActiveSupport::TestCase mail(DEFAULT_HEADERS.merge(hash)) end - def attachment_with_content + def attachment_with_content(hash = {}) attachments['invoice.pdf'] = 'This is test File content' - mail(DEFAULT_HEADERS) + mail(DEFAULT_HEADERS.merge(hash)) end def attachment_with_hash @@ -69,8 +69,9 @@ class BaseTest < ActiveSupport::TestCase attachments['invoice.pdf'] = 'This is test File content' if hash.delete(:attachments) mail(DEFAULT_HEADERS.merge(hash)) end - + def explicit_multipart(hash = {}) + attachments['invoice.pdf'] = 'This is test File content' if hash.delete(:attachments) mail(DEFAULT_HEADERS.merge(hash)) do |format| format.text { render :text => "TEXT Explicit Multipart" } format.html { render :text => "HTML Explicit Multipart" } @@ -116,6 +117,7 @@ class BaseTest < ActiveSupport::TestCase test "can pass in :body to the mail method hash" do email = BaseMailer.deliver_welcome(:body => "Hello there") + assert_equal("text/plain", email.mime_type) assert_equal("Hello there", email.body.encoded) end @@ -154,12 +156,23 @@ class BaseTest < ActiveSupport::TestCase test "adds the rendered template as part" do email = BaseMailer.deliver_attachment_with_content assert_equal(2, email.parts.length) + assert_equal("multipart/mixed", email.mime_type) assert_equal("text/html", email.parts[0].mime_type) assert_equal("Attachment with content", email.parts[0].body.encoded) assert_equal("application/pdf", email.parts[1].mime_type) assert_equal("VGhpcyBpcyB0ZXN0IEZpbGUgY29udGVudA==\r\n", email.parts[1].body.encoded) end + test "adds the given :body as part" do + email = BaseMailer.deliver_attachment_with_content(:body => "I'm the eggman") + assert_equal(2, email.parts.length) + assert_equal("multipart/mixed", email.mime_type) + assert_equal("text/plain", email.parts[0].mime_type) + assert_equal("I'm the eggman", email.parts[0].body.encoded) + assert_equal("application/pdf", email.parts[1].mime_type) + assert_equal("VGhpcyBpcyB0ZXN0IEZpbGUgY29udGVudA==\r\n", email.parts[1].body.encoded) + end + # Defaults values test "uses default charset from class" do swap BaseMailer, :default_charset => "US-ASCII" do @@ -268,15 +281,15 @@ class BaseTest < ActiveSupport::TestCase end end - #test "explicit multipart with attachments creates nested parts" do - # email = BaseMailer.deliver_explicit_multipart(:attachments => true) - # assert_equal("application/pdf", email.parts[0].mime_type) - # assert_equal("multipart/alternate", email.parts[1].mime_type) - # assert_equal("text/plain", email.parts[1].parts[0].mime_type) - # assert_equal("TEXT Implicit Multipart", email.parts[1].parts[0].body.encoded) - # assert_equal("text/html", email.parts[1].parts[1].mime_type) - # assert_equal("HTML Implicit Multipart", email.parts[1].parts[1].body.encoded) - #end + test "explicit multipart with attachments creates nested parts" do + email = BaseMailer.deliver_explicit_multipart(:attachments => true) + assert_equal("application/pdf", email.parts[0].mime_type) + assert_equal("multipart/alternate", email.parts[1].mime_type) + assert_equal("text/plain", email.parts[1].parts[0].mime_type) + assert_equal("TEXT Explicit Multipart", email.parts[1].parts[0].body.encoded) + assert_equal("text/html", email.parts[1].parts[1].mime_type) + assert_equal("HTML Explicit Multipart", email.parts[1].parts[1].body.encoded) + end protected From c985a0ee3d9ae279fd02814ca60782e2f93215e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sat, 23 Jan 2010 12:46:33 +0100 Subject: [PATCH 34/63] Add some tests to collector with templates and any. --- actionmailer/lib/action_mailer/base.rb | 2 +- actionmailer/lib/action_mailer/collector.rb | 3 +- actionmailer/test/base_test.rb | 57 +++++++++++++++---- .../explicit_multipart_templates.html.erb | 1 + .../explicit_multipart_templates.text.erb | 1 + 5 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 actionmailer/test/fixtures/base_mailer/explicit_multipart_templates.html.erb create mode 100644 actionmailer/test/fixtures/base_mailer/explicit_multipart_templates.text.erb diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 5e61b8693a1..4ab37618071 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -2,6 +2,7 @@ require 'active_support/core_ext/class' require 'active_support/core_ext/module/delegation' require 'mail' require 'action_mailer/tmail_compat' +require 'action_mailer/collector' module ActionMailer #:nodoc: # Action Mailer allows you to send email from your application using a mailer model and views. @@ -442,7 +443,6 @@ module ActionMailer #:nodoc: end content_type ||= create_parts_from_responses(m, responses, charset) - m.content_type = content_type m.charset = charset m.mime_version = mime_version diff --git a/actionmailer/lib/action_mailer/collector.rb b/actionmailer/lib/action_mailer/collector.rb index 0891b6f1236..5431efccfee 100644 --- a/actionmailer/lib/action_mailer/collector.rb +++ b/actionmailer/lib/action_mailer/collector.rb @@ -14,11 +14,10 @@ module ActionMailer #:nodoc: @default_formats = context.formats end - # TODO Test me def any(*args, &block) options = args.extract_options! raise "You have to supply at least one format" if args.empty? - args.each { |type| send(type, options, &block) } + args.each { |type| send(type, options.dup, &block) } end alias :all :any diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 3033b2db0f3..83943162d2c 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -77,7 +77,19 @@ class BaseTest < ActiveSupport::TestCase format.html { render :text => "HTML Explicit Multipart" } end end - + + def explicit_multipart_templates(hash = {}) + mail(DEFAULT_HEADERS.merge(hash)) do |format| + format.html + format.text + end + end + + def explicit_multipart_with_any(hash = {}) + mail(DEFAULT_HEADERS.merge(hash)) do |format| + format.any(:text, :html){ render :text => "Format with any!" } + end + end end test "method call to mail does not raise error" do @@ -214,7 +226,7 @@ class BaseTest < ActiveSupport::TestCase end # Implicit multipart - test "implicit multipart tests" do + test "implicit multipart" do email = BaseMailer.deliver_implicit_multipart assert_equal(2, email.parts.size) assert_equal("multipart/alternate", email.mime_type) @@ -224,7 +236,7 @@ class BaseTest < ActiveSupport::TestCase assert_equal("HTML Implicit Multipart", email.parts[1].body.encoded) end - test "implicit multipart tests with sort order" do + test "implicit multipart with sort order" do order = ["text/html", "text/plain"] swap BaseMailer, :default_implicit_parts_order => order do email = BaseMailer.deliver_implicit_multipart @@ -258,7 +270,8 @@ class BaseTest < ActiveSupport::TestCase end end - test "explicit multipart tests" do + # Explicit multipart + test "explicit multipart" do email = BaseMailer.deliver_explicit_multipart assert_equal(2, email.parts.size) assert_equal("multipart/alternate", email.mime_type) @@ -282,15 +295,37 @@ class BaseTest < ActiveSupport::TestCase end test "explicit multipart with attachments creates nested parts" do - email = BaseMailer.deliver_explicit_multipart(:attachments => true) - assert_equal("application/pdf", email.parts[0].mime_type) - assert_equal("multipart/alternate", email.parts[1].mime_type) - assert_equal("text/plain", email.parts[1].parts[0].mime_type) - assert_equal("TEXT Explicit Multipart", email.parts[1].parts[0].body.encoded) - assert_equal("text/html", email.parts[1].parts[1].mime_type) - assert_equal("HTML Explicit Multipart", email.parts[1].parts[1].body.encoded) + email = BaseMailer.deliver_explicit_multipart(:attachments => true) + assert_equal("application/pdf", email.parts[0].mime_type) + assert_equal("multipart/alternate", email.parts[1].mime_type) + assert_equal("text/plain", email.parts[1].parts[0].mime_type) + assert_equal("TEXT Explicit Multipart", email.parts[1].parts[0].body.encoded) + assert_equal("text/html", email.parts[1].parts[1].mime_type) + assert_equal("HTML Explicit Multipart", email.parts[1].parts[1].body.encoded) end + # TODO Seems Mail is sorting the templates automatically, and not on demand + # test "explicit multipart with templates" do + # email = BaseMailer.deliver_explicit_multipart_templates + # assert_equal(2, email.parts.size) + # assert_equal("multipart/alternate", email.mime_type) + # assert_equal("text/html", email.parts[0].mime_type) + # assert_equal("HTML Explicit Multipart Templates", email.parts[0].body.encoded) + # assert_equal("text/plain", email.parts[1].mime_type) + # assert_equal("TEXT Explicit Multipart Templates", email.parts[1].body.encoded) + # end + + test "explicit multipart with any" do + email = BaseMailer.deliver_explicit_multipart_with_any + assert_equal(2, email.parts.size) + assert_equal("multipart/alternate", email.mime_type) + assert_equal("text/plain", email.parts[0].mime_type) + assert_equal("Format with any!", email.parts[0].body.encoded) + assert_equal("text/html", email.parts[1].mime_type) + assert_equal("Format with any!", email.parts[1].body.encoded) + end + + protected # Execute the block setting the given values and restoring old values after diff --git a/actionmailer/test/fixtures/base_mailer/explicit_multipart_templates.html.erb b/actionmailer/test/fixtures/base_mailer/explicit_multipart_templates.html.erb new file mode 100644 index 00000000000..c0a61b6249c --- /dev/null +++ b/actionmailer/test/fixtures/base_mailer/explicit_multipart_templates.html.erb @@ -0,0 +1 @@ +HTML Explicit Multipart Templates \ No newline at end of file diff --git a/actionmailer/test/fixtures/base_mailer/explicit_multipart_templates.text.erb b/actionmailer/test/fixtures/base_mailer/explicit_multipart_templates.text.erb new file mode 100644 index 00000000000..507230b1de3 --- /dev/null +++ b/actionmailer/test/fixtures/base_mailer/explicit_multipart_templates.text.erb @@ -0,0 +1 @@ +TEXT Explicit Multipart Templates \ No newline at end of file From e7e4ed48df3048f32d94e398e99d0048a66ba67e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sun, 24 Jan 2010 09:34:50 +1100 Subject: [PATCH 35/63] Set sort order for explicit parts from the collector's template sequence --- actionmailer/lib/action_mailer/base.rb | 12 ++++++++---- actionmailer/test/base_test.rb | 19 +++++++++---------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 4ab37618071..e95643ca44b 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -415,7 +415,7 @@ module ActionMailer #:nodoc: # Should be removed when old API is deprecated @mail_was_called = true - m, sort_parts = @message, true + m = @message # Give preference to headers and fallback to the ones set in mail content_type = headers[:content_type] || m.content_type @@ -425,12 +425,16 @@ module ActionMailer #:nodoc: headers[:subject] ||= default_subject quote_fields(m, headers, charset) + sort_order = headers[:parts_order] || self.class.default_implicit_parts_order.dup + responses = if headers[:body] [ { :body => headers[:body], :content_type => self.class.default_content_type.dup } ] elsif block_given? - sort_parts = false collector = ActionMailer::Collector.new(self) { render(action_name) } yield(collector) + # Collect the sort order of the parts from the collector as Mail will always + # sort parts on encode into a "sane" sequence. + sort_order = collector.responses.map { |r| r[:content_type] } collector.responses else # TODO Ensure that we don't need to pass I18n.locale as detail @@ -447,8 +451,8 @@ module ActionMailer #:nodoc: m.charset = charset m.mime_version = mime_version - if sort_parts && m.parts.present? - m.body.set_sort_order(headers[:parts_order] || self.class.default_implicit_parts_order.dup) + if m.multipart? + m.body.set_sort_order(sort_order) m.body.sort_parts! end diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 83943162d2c..ad0e1d9fe2c 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -304,16 +304,15 @@ class BaseTest < ActiveSupport::TestCase assert_equal("HTML Explicit Multipart", email.parts[1].parts[1].body.encoded) end - # TODO Seems Mail is sorting the templates automatically, and not on demand - # test "explicit multipart with templates" do - # email = BaseMailer.deliver_explicit_multipart_templates - # assert_equal(2, email.parts.size) - # assert_equal("multipart/alternate", email.mime_type) - # assert_equal("text/html", email.parts[0].mime_type) - # assert_equal("HTML Explicit Multipart Templates", email.parts[0].body.encoded) - # assert_equal("text/plain", email.parts[1].mime_type) - # assert_equal("TEXT Explicit Multipart Templates", email.parts[1].body.encoded) - # end + test "explicit multipart with templates" do + email = BaseMailer.deliver_explicit_multipart_templates + assert_equal(2, email.parts.size) + assert_equal("multipart/alternate", email.mime_type) + assert_equal("text/html", email.parts[0].mime_type) + assert_equal("HTML Explicit Multipart Templates", email.parts[0].body.encoded) + assert_equal("text/plain", email.parts[1].mime_type) + assert_equal("TEXT Explicit Multipart Templates", email.parts[1].body.encoded) + end test "explicit multipart with any" do email = BaseMailer.deliver_explicit_multipart_with_any From 258ca148004eaa63740ab2186338b3ac872b8187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sun, 24 Jan 2010 11:15:42 +1100 Subject: [PATCH 36/63] Delegated ActionMailer::Base.deliveries to Mail.deliveries, added callback support in Mail to call ActionMailer on delivery, moved deliver to deprecated API in preparation for new API --- actionmailer/actionmailer.gemspec | 2 +- actionmailer/lib/action_mailer/base.rb | 56 ++++++------------- .../lib/action_mailer/deprecated_api.rb | 32 +++++++++++ actionmailer/lib/action_mailer/test_case.rb | 2 +- actionmailer/test/asset_host_test.rb | 2 +- actionmailer/test/base_test.rb | 9 +++ actionmailer/test/mail_helper_test.rb | 2 +- actionmailer/test/mail_layout_test.rb | 2 +- actionmailer/test/mail_render_test.rb | 4 +- actionmailer/test/mail_service_test.rb | 6 +- actionmailer/test/url_test.rb | 2 +- 11 files changed, 70 insertions(+), 49 deletions(-) diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec index 576ca973347..4f53c709c45 100644 --- a/actionmailer/actionmailer.gemspec +++ b/actionmailer/actionmailer.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |s| s.homepage = "http://www.rubyonrails.org" s.add_dependency('actionpack', '= 3.0.pre') - s.add_dependency('mail', '~> 2.0.3') + s.add_dependency('mail', '~> 2.0.5') s.files = Dir['CHANGELOG', 'README', 'MIT-LICENSE', 'lib/**/*'] s.has_rdoc = true diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index e95643ca44b..c1dc4157280 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -275,8 +275,16 @@ module ActionMailer #:nodoc: @@perform_deliveries = true cattr_accessor :perform_deliveries - @@deliveries = [] - cattr_accessor :deliveries + # Provides a list of emails that have been delivered by Mail + def self.deliveries + Mail.deliveries + end + + # Allows you to over write the default deliveries store from an array to some + # other object. If you just want to clear the store, call Mail.deliveries.clear. + def self.deliveries=(val) + Mail.deliveries = val + end extlib_inheritable_accessor :default_charset self.default_charset = "utf-8" @@ -342,35 +350,6 @@ module ActionMailer #:nodoc: end end - # Deliver the given mail object directly. This can be used to deliver - # a preconstructed mail object, like: - # - # email = MyMailer.create_some_mail(parameters) - # email.set_some_obscure_header "frobnicate" - # MyMailer.deliver(email) - def deliver(mail) - raise "no mail object available for delivery!" unless mail - - ActiveSupport::Notifications.instrument("action_mailer.deliver", :mailer => self.name) do |payload| - self.set_payload_for_mail(payload, mail) - - mail.delivery_method delivery_methods[delivery_method], - delivery_settings[delivery_method] - - begin - # TODO Move me to the instance - if @@perform_deliveries - mail.deliver! - self.deliveries << mail - end - rescue Exception => e # Net::SMTP errors or sendmail pipe errors - raise e if raise_delivery_errors - end - end - - mail - end - def template_root self.view_paths && self.view_paths.first end @@ -380,6 +359,12 @@ module ActionMailer #:nodoc: self.view_paths = ActionView::Base.process_view_paths(root) end + def delivered_email(mail) + ActiveSupport::Notifications.instrument("action_mailer.deliver", :mailer => self.name) do |payload| + self.set_payload_for_mail(payload, mail) + end + end + def set_payload_for_mail(payload, mail) #:nodoc: payload[:message_id] = mail.message_id payload[:subject] = mail.subject @@ -402,13 +387,6 @@ module ActionMailer #:nodoc: process(method_name, *args) if method_name end - # Delivers a Mail object. By default, it delivers the cached mail - # object (from the create! method). If no cached mail object exists, and - # no alternate has been given as the parameter, this will fail. - def deliver!(mail = @message) - self.class.deliver(mail) - end - # TODO Add new delivery method goodness def mail(headers = {}) # Guard flag to prevent both the old and the new API from firing @@ -417,6 +395,8 @@ module ActionMailer #:nodoc: m = @message + m.register_for_delivery_notification(self.class) + # Give preference to headers and fallback to the ones set in mail content_type = headers[:content_type] || m.content_type charset = headers[:charset] || m.charset || self.class.default_charset.dup diff --git a/actionmailer/lib/action_mailer/deprecated_api.rb b/actionmailer/lib/action_mailer/deprecated_api.rb index b2bb6a64aa9..a9ebd70f869 100644 --- a/actionmailer/lib/action_mailer/deprecated_api.rb +++ b/actionmailer/lib/action_mailer/deprecated_api.rb @@ -71,9 +71,34 @@ module ActionMailer # Alias controller_path to mailer_name so render :partial in views work. alias :controller_path :mailer_name + end module ClassMethods + + # Deliver the given mail object directly. This can be used to deliver + # a preconstructed mail object, like: + # + # email = MyMailer.create_some_mail(parameters) + # email.set_some_obscure_header "frobnicate" + # MyMailer.deliver(email) + def deliver(mail) + raise "no mail object available for delivery!" unless mail + + ActiveSupport::Notifications.instrument("action_mailer.deliver", :mailer => self.name) do |payload| + self.set_payload_for_mail(payload, mail) + + mail.delivery_method delivery_methods[delivery_method], + delivery_settings[delivery_method] + + mail.raise_delivery_errors = raise_delivery_errors + mail.perform_deliveries = perform_deliveries + mail.deliver + end + + mail + end + def respond_to?(method_symbol, include_private = false) #:nodoc: matches_dynamic_method?(method_symbol) || super end @@ -104,6 +129,13 @@ module ActionMailer @mail_was_called = false end + # Delivers a Mail object. By default, it delivers the cached mail + # object (from the create! method). If no cached mail object exists, and + # no alternate has been given as the parameter, this will fail. + def deliver!(mail = @message) + self.class.deliver(mail) + end + def render(*args) options = args.last.is_a?(Hash) ? args.last : {} if options[:body] diff --git a/actionmailer/lib/action_mailer/test_case.rb b/actionmailer/lib/action_mailer/test_case.rb index 0ca4f5494e0..7c4033a125c 100644 --- a/actionmailer/lib/action_mailer/test_case.rb +++ b/actionmailer/lib/action_mailer/test_case.rb @@ -37,7 +37,7 @@ module ActionMailer def initialize_test_deliveries ActionMailer::Base.delivery_method = :test ActionMailer::Base.perform_deliveries = true - ActionMailer::Base.deliveries = [] + ActionMailer::Base.deliveries.clear end def set_expected_mail diff --git a/actionmailer/test/asset_host_test.rb b/actionmailer/test/asset_host_test.rb index f3383e5608d..7ba78b6daa1 100644 --- a/actionmailer/test/asset_host_test.rb +++ b/actionmailer/test/asset_host_test.rb @@ -12,7 +12,7 @@ class AssetHostTest < Test::Unit::TestCase def setup set_delivery_method :test ActionMailer::Base.perform_deliveries = true - ActionMailer::Base.deliveries = [] + ActionMailer::Base.deliveries.clear @recipient = 'test@localhost' end diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index ad0e1d9fe2c..43a9fb29bb4 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -324,6 +324,15 @@ class BaseTest < ActiveSupport::TestCase assert_equal("Format with any!", email.parts[1].body.encoded) end + test "ActionMailer should be told when Mail gets delivered" do + BaseMailer.expects(:delivered_email).once + email = BaseMailer.deliver_welcome + end + + # test "ActionMailer should be told when Mail gets delivered using new API" do + # BaseMailer.expects(:delivered_email).once + # email = BaseMailer.welcome.deliver + # end protected diff --git a/actionmailer/test/mail_helper_test.rb b/actionmailer/test/mail_helper_test.rb index 2d3565d159c..d81bc57ce02 100644 --- a/actionmailer/test/mail_helper_test.rb +++ b/actionmailer/test/mail_helper_test.rb @@ -63,7 +63,7 @@ class MailerHelperTest < Test::Unit::TestCase def setup set_delivery_method :test ActionMailer::Base.perform_deliveries = true - ActionMailer::Base.deliveries = [] + ActionMailer::Base.deliveries.clear @recipient = 'test@localhost' end diff --git a/actionmailer/test/mail_layout_test.rb b/actionmailer/test/mail_layout_test.rb index b9ff0754616..b4bd5836160 100644 --- a/actionmailer/test/mail_layout_test.rb +++ b/actionmailer/test/mail_layout_test.rb @@ -55,7 +55,7 @@ class LayoutMailerTest < Test::Unit::TestCase def setup set_delivery_method :test ActionMailer::Base.perform_deliveries = true - ActionMailer::Base.deliveries = [] + ActionMailer::Base.deliveries.clear @recipient = 'test@localhost' end diff --git a/actionmailer/test/mail_render_test.rb b/actionmailer/test/mail_render_test.rb index 09ce5e48540..3424823c999 100644 --- a/actionmailer/test/mail_render_test.rb +++ b/actionmailer/test/mail_render_test.rb @@ -86,7 +86,7 @@ class RenderHelperTest < Test::Unit::TestCase def setup set_delivery_method :test ActionMailer::Base.perform_deliveries = true - ActionMailer::Base.deliveries = [] + ActionMailer::Base.deliveries.clear @recipient = 'test@localhost' end @@ -135,7 +135,7 @@ class FirstSecondHelperTest < Test::Unit::TestCase def setup set_delivery_method :test ActionMailer::Base.perform_deliveries = true - ActionMailer::Base.deliveries = [] + ActionMailer::Base.deliveries.clear @recipient = 'test@localhost' end diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index 62422fb4b38..df5afda4473 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -337,7 +337,7 @@ class ActionMailerTest < Test::Unit::TestCase set_delivery_method :test ActionMailer::Base.perform_deliveries = true ActionMailer::Base.raise_delivery_errors = true - ActionMailer::Base.deliveries = [] + ActionMailer::Base.deliveries.clear @original_logger = TestMailer.logger @recipient = 'test@localhost' @@ -658,7 +658,7 @@ class ActionMailerTest < Test::Unit::TestCase def test_doesnt_raise_errors_when_raise_delivery_errors_is_false ActionMailer::Base.raise_delivery_errors = false - Mail::Message.any_instance.expects(:deliver!).raises(Exception) + Mail::TestMailer.any_instance.expects(:deliver!).raises(Exception) assert_nothing_raised { TestMailer.deliver_signed_up(@recipient) } end @@ -1113,7 +1113,7 @@ class MethodNamingTest < Test::Unit::TestCase def setup set_delivery_method :test ActionMailer::Base.perform_deliveries = true - ActionMailer::Base.deliveries = [] + ActionMailer::Base.deliveries.clear end def teardown diff --git a/actionmailer/test/url_test.rb b/actionmailer/test/url_test.rb index 12bf609dce6..b7ae5304ed3 100644 --- a/actionmailer/test/url_test.rb +++ b/actionmailer/test/url_test.rb @@ -44,7 +44,7 @@ class ActionMailerUrlTest < Test::Unit::TestCase def setup set_delivery_method :test ActionMailer::Base.perform_deliveries = true - ActionMailer::Base.deliveries = [] + ActionMailer::Base.deliveries.clear @recipient = 'test@localhost' end From afc758297c553de467b0c434330cb703d62795f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sun, 24 Jan 2010 12:30:13 +1100 Subject: [PATCH 37/63] Moving AS::Notifications call to one location in base --- actionmailer/lib/action_mailer/deprecated_api.rb | 16 +++++++--------- actionmailer/test/base_test.rb | 7 ++----- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/actionmailer/lib/action_mailer/deprecated_api.rb b/actionmailer/lib/action_mailer/deprecated_api.rb index a9ebd70f869..a2fa481d0e1 100644 --- a/actionmailer/lib/action_mailer/deprecated_api.rb +++ b/actionmailer/lib/action_mailer/deprecated_api.rb @@ -83,19 +83,17 @@ module ActionMailer # email.set_some_obscure_header "frobnicate" # MyMailer.deliver(email) def deliver(mail) + return if @mail_was_called raise "no mail object available for delivery!" unless mail - ActiveSupport::Notifications.instrument("action_mailer.deliver", :mailer => self.name) do |payload| - self.set_payload_for_mail(payload, mail) + mail.register_for_delivery_notification(self) - mail.delivery_method delivery_methods[delivery_method], - delivery_settings[delivery_method] - - mail.raise_delivery_errors = raise_delivery_errors - mail.perform_deliveries = perform_deliveries - mail.deliver - end + mail.delivery_method delivery_methods[delivery_method], + delivery_settings[delivery_method] + mail.raise_delivery_errors = raise_delivery_errors + mail.perform_deliveries = perform_deliveries + mail.deliver mail end diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 43a9fb29bb4..0b650e9a289 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -325,15 +325,12 @@ class BaseTest < ActiveSupport::TestCase end test "ActionMailer should be told when Mail gets delivered" do + BaseMailer.deliveries.clear BaseMailer.expects(:delivered_email).once email = BaseMailer.deliver_welcome + assert_equal(1, BaseMailer.deliveries.length) end - # test "ActionMailer should be told when Mail gets delivered using new API" do - # BaseMailer.expects(:delivered_email).once - # email = BaseMailer.welcome.deliver - # end - protected # Execute the block setting the given values and restoring old values after From 73a9000402b5766e660dbf3489f5289c21c3f472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sun, 24 Jan 2010 20:38:53 +1100 Subject: [PATCH 38/63] Adding failing tests for calling just the action, instead of :create_action_name and :deliver_action_name --- actionmailer/lib/action_mailer/base.rb | 4 +++- actionmailer/test/base_test.rb | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index c1dc4157280..e7eb6bffcda 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -387,7 +387,8 @@ module ActionMailer #:nodoc: process(method_name, *args) if method_name end - # TODO Add new delivery method goodness + # TODO: Clean this up and refactor before Rails 3.0 release. + # This works for now, but not neat def mail(headers = {}) # Guard flag to prevent both the old and the new API from firing # Should be removed when old API is deprecated @@ -446,6 +447,7 @@ module ActionMailer #:nodoc: I18n.t(:subject, :scope => [:actionmailer, mailer_scope, action_name], :default => action_name.humanize) end + # TODO: Move this into Mail def quote_fields(m, headers, charset) #:nodoc: m.subject ||= quote_if_necessary(headers[:subject], charset) if headers[:subject] m.to ||= quote_address_if_necessary(headers[:to], charset) if headers[:to] diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 0b650e9a289..7f41f3485ce 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -327,7 +327,21 @@ class BaseTest < ActiveSupport::TestCase test "ActionMailer should be told when Mail gets delivered" do BaseMailer.deliveries.clear BaseMailer.expects(:delivered_email).once - email = BaseMailer.deliver_welcome + BaseMailer.deliver_welcome + assert_equal(1, BaseMailer.deliveries.length) + end + + test "Calling just the action should return the generated mail object" do + BaseMailer.deliveries.clear + email = BaseMailer.welcome + assert_equal(0, BaseMailer.deliveries.length) + assert_equal('The first email on new API!', email.subject) + end + + test "Calling deliver on the action should deliver the mail object" do + BaseMailer.deliveries.clear + BaseMailer.expects(:delivered_email).once + BaseMailer.welcome.deliver assert_equal(1, BaseMailer.deliveries.length) end From 7409b734841c8bd691006634dd072212aa905cf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sun, 24 Jan 2010 16:37:28 +0100 Subject: [PATCH 39/63] Some refactoring. --- actionmailer/lib/action_mailer/base.rb | 166 ++++++++++-------- .../lib/action_mailer/delivery_methods.rb | 100 +++++------ .../lib/action_mailer/deprecated_api.rb | 64 +++---- .../lib/action_mailer/tmail_compat.rb | 6 +- 4 files changed, 168 insertions(+), 168 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index e7eb6bffcda..b881611cfb6 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -263,33 +263,42 @@ module ActionMailer #:nodoc: include AbstractController::UrlFor helper ActionMailer::MailHelper - include ActionMailer::DeprecatedApi - include ActionMailer::DeliveryMethods + include ActionMailer::DeprecatedApi + extend ActionMailer::DeliveryMethods + + add_delivery_method :smtp, Mail::SMTP, + :address => "localhost", + :port => 25, + :domain => 'localhost.localdomain', + :user_name => nil, + :password => nil, + :authentication => nil, + :enable_starttls_auto => true + + add_delivery_method :file, Mail::FileDelivery, + :location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails" + + add_delivery_method :sendmail, Mail::Sendmail, + :location => '/usr/sbin/sendmail', + :arguments => '-i -t' + + add_delivery_method :test, Mail::TestMailer + + superclass_delegating_reader :delivery_method + self.delivery_method = :smtp private_class_method :new #:nodoc: - @@raise_delivery_errors = true cattr_accessor :raise_delivery_errors + @@raise_delivery_errors = true - @@perform_deliveries = true cattr_accessor :perform_deliveries - - # Provides a list of emails that have been delivered by Mail - def self.deliveries - Mail.deliveries - end - - # Allows you to over write the default deliveries store from an array to some - # other object. If you just want to clear the store, call Mail.deliveries.clear. - def self.deliveries=(val) - Mail.deliveries = val - end + @@perform_deliveries = true extlib_inheritable_accessor :default_charset self.default_charset = "utf-8" - # TODO This should be used when calling render extlib_inheritable_accessor :default_content_type self.default_content_type = "text/plain" @@ -305,24 +314,9 @@ module ActionMailer #:nodoc: extlib_inheritable_accessor :default_implicit_parts_order self.default_implicit_parts_order = [ "text/plain", "text/enriched", "text/html" ] - # Expose the internal Mail message - # TODO: Make this an _internal ivar? - attr_reader :message - - def headers(args=nil) - if args - ActiveSupport::Deprecation.warn "headers(Hash) is deprecated, please do headers[key] = value instead", caller - @headers = args - else - @message - end - end - - def attachments - @message.attachments - end - class << self + # Provides a list of emails that have been delivered by Mail + delegate :deliveries, :deliveries=, :to => Mail def mailer_name @mailer_name ||= name.underscore @@ -359,6 +353,7 @@ module ActionMailer #:nodoc: self.view_paths = ActionView::Base.process_view_paths(root) end + # TODO The delivery should happen inside the instrument block def delivered_email(mail) ActiveSupport::Notifications.instrument("action_mailer.deliver", :mailer => self.name) do |payload| self.set_payload_for_mail(payload, mail) @@ -377,66 +372,63 @@ module ActionMailer #:nodoc: end end + attr_internal :message + # Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer # 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) super() - @message = Mail.new + @_message = Mail.new process(method_name, *args) if method_name end - # TODO: Clean this up and refactor before Rails 3.0 release. - # This works for now, but not neat - def mail(headers = {}) + def headers(args=nil) + if args + ActiveSupport::Deprecation.warn "headers(Hash) is deprecated, please do headers[key] = value instead", caller[0,2] + @headers = args + else + @_message + end + end + + def attachments + @_message.attachments + end + + def mail(headers={}, &block) # Guard flag to prevent both the old and the new API from firing - # Should be removed when old API is deprecated + # Should be removed when old API is removed @mail_was_called = true - - m = @message - - m.register_for_delivery_notification(self.class) + m = @_message # Give preference to headers and fallback to the ones set in mail content_type = headers[:content_type] || m.content_type charset = headers[:charset] || m.charset || self.class.default_charset.dup mime_version = headers[:mime_version] || m.mime_version || self.class.default_mime_version.dup + # Set subjects and fields quotings headers[:subject] ||= default_subject - quote_fields(m, headers, charset) - - sort_order = headers[:parts_order] || self.class.default_implicit_parts_order.dup - - responses = if headers[:body] - [ { :body => headers[:body], :content_type => self.class.default_content_type.dup } ] - elsif block_given? - collector = ActionMailer::Collector.new(self) { render(action_name) } - yield(collector) - # Collect the sort order of the parts from the collector as Mail will always - # sort parts on encode into a "sane" sequence. - sort_order = collector.responses.map { |r| r[:content_type] } - collector.responses - else - # TODO Ensure that we don't need to pass I18n.locale as detail - templates = self.class.template_root.find_all(action_name, {}, self.class.mailer_name) - - templates.map do |template| - { :body => render_to_body(:_template => template), - :content_type => template.mime_type.to_s } - end - end + quote_fields!(headers, charset) + # Render the templates and blocks + responses, sort_order = collect_responses_and_sort_order(headers, &block) content_type ||= create_parts_from_responses(m, responses, charset) + + # Tidy up content type, charset, mime version and sort order m.content_type = content_type m.charset = charset m.mime_version = mime_version + sort_order = headers[:parts_order] || sort_order || self.class.default_implicit_parts_order.dup if m.multipart? m.body.set_sort_order(sort_order) m.body.sort_parts! end + # Finaly set delivery behavior configured in class + wrap_delivery_behavior!(headers[:delivery_method]) m end @@ -448,14 +440,44 @@ module ActionMailer #:nodoc: end # TODO: Move this into Mail - def quote_fields(m, headers, charset) #:nodoc: - m.subject ||= quote_if_necessary(headers[:subject], charset) if headers[:subject] - m.to ||= quote_address_if_necessary(headers[:to], charset) if headers[:to] - m.from ||= quote_address_if_necessary(headers[:from], charset) if headers[:from] - m.cc ||= quote_address_if_necessary(headers[:cc], charset) if headers[:cc] - m.bcc ||= quote_address_if_necessary(headers[:bcc], charset) if headers[:bcc] - m.reply_to ||= quote_address_if_necessary(headers[:reply_to], charset) if headers[:reply_to] - m.date ||= headers[:date] if headers[:date] + def quote_fields!(headers, charset) #:nodoc: + m = @_message + m.subject ||= quote_if_necessary(headers[:subject], charset) if headers[:subject] + m.to ||= quote_address_if_necessary(headers[:to], charset) if headers[:to] + m.from ||= quote_address_if_necessary(headers[:from], charset) if headers[:from] + m.cc ||= quote_address_if_necessary(headers[:cc], charset) if headers[:cc] + m.bcc ||= quote_address_if_necessary(headers[:bcc], charset) if headers[:bcc] + m.reply_to ||= quote_address_if_necessary(headers[:reply_to], charset) if headers[:reply_to] + m.date ||= headers[:date] if headers[:date] + end + + def collect_responses_and_sort_order(headers) #:nodoc: + responses, sort_order = [], nil + + if block_given? + collector = ActionMailer::Collector.new(self) { render(action_name) } + yield(collector) + sort_order = collector.responses.map { |r| r[:content_type] } + responses = collector.responses + elsif headers[:body] + responses << { + :body => headers[:body], + :content_type => self.class.default_content_type.dup + } + else + self.class.template_root.find_all(action_name, {}, self.class.mailer_name).each do |template| + responses << { + :body => render_to_body(:_template => template), + :content_type => template.mime_type.to_s + } + end + end + + [responses, sort_order] + end + + def wrap_delivery_behavior!(method=nil) #:nodoc: + self.class.wrap_delivery_behavior(@_message, method) end def create_parts_from_responses(m, responses, charset) #:nodoc: diff --git a/actionmailer/lib/action_mailer/delivery_methods.rb b/actionmailer/lib/action_mailer/delivery_methods.rb index 5883e446f28..16b84d41183 100644 --- a/actionmailer/lib/action_mailer/delivery_methods.rb +++ b/actionmailer/lib/action_mailer/delivery_methods.rb @@ -1,73 +1,63 @@ module ActionMailer # This modules makes a DSL for adding delivery methods to ActionMailer module DeliveryMethods - extend ActiveSupport::Concern - - included do - add_delivery_method :smtp, Mail::SMTP, - :address => "localhost", - :port => 25, - :domain => 'localhost.localdomain', - :user_name => nil, - :password => nil, - :authentication => nil, - :enable_starttls_auto => true - - add_delivery_method :file, Mail::FileDelivery, - :location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails" - - add_delivery_method :sendmail, Mail::Sendmail, - :location => '/usr/sbin/sendmail', - :arguments => '-i -t' - - add_delivery_method :test, Mail::TestMailer - - superclass_delegating_reader :delivery_method - self.delivery_method = :smtp + # TODO Make me class inheritable + def delivery_settings + @@delivery_settings ||= Hash.new { |h,k| h[k] = {} } end - module ClassMethods - # TODO Make me class inheritable - def delivery_settings - @@delivery_settings ||= Hash.new { |h,k| h[k] = {} } + def delivery_methods + @@delivery_methods ||= {} + end + + def delivery_method=(method) + raise ArgumentError, "Unknown delivery method #{method.inspect}" unless delivery_methods[method] + @delivery_method = method + end + + def add_delivery_method(symbol, klass, default_options={}) + self.delivery_methods[symbol] = klass + self.delivery_settings[symbol] = default_options + end + + def wrap_delivery_behavior(mail, method=nil) + method ||= delivery_method + + mail.register_for_delivery_notification(self) + + if method.is_a?(Symbol) + mail.delivery_method(delivery_methods[method], + delivery_settings[method]) + else + mail.delivery_method(method) end - def delivery_methods - @@delivery_methods ||= {} - end + mail.perform_deliveries = perform_deliveries + mail.raise_delivery_errors = raise_delivery_errors + end - def delivery_method=(method) - raise ArgumentError, "Unknown delivery method #{method.inspect}" unless delivery_methods[method] - @delivery_method = method - end - def add_delivery_method(symbol, klass, default_options={}) - self.delivery_methods[symbol] = klass - self.delivery_settings[symbol] = default_options - end + def respond_to?(method_symbol, include_private = false) #:nodoc: + matches_settings_method?(method_symbol) || super + end - def respond_to?(method_symbol, include_private = false) #:nodoc: - matches_settings_method?(method_symbol) || super - end + protected - protected - - # TODO Get rid of this method missing magic - def method_missing(method_symbol, *parameters) #:nodoc: - if match = matches_settings_method?(method_symbol) - if match[2] - delivery_settings[match[1].to_sym] = parameters[0] - else - delivery_settings[match[1].to_sym] - end + # TODO Get rid of this method missing magic + def method_missing(method_symbol, *parameters) #:nodoc: + if match = matches_settings_method?(method_symbol) + if match[2] + delivery_settings[match[1].to_sym] = parameters[0] else - super + delivery_settings[match[1].to_sym] end + else + super end + end - def matches_settings_method?(method_name) #:nodoc: - /(#{delivery_methods.keys.join('|')})_settings(=)?$/.match(method_name.to_s) - end + def matches_settings_method?(method_name) #:nodoc: + /(#{delivery_methods.keys.join('|')})_settings(=)?$/.match(method_name.to_s) end end end \ No newline at end of file diff --git a/actionmailer/lib/action_mailer/deprecated_api.rb b/actionmailer/lib/action_mailer/deprecated_api.rb index a2fa481d0e1..f969584a179 100644 --- a/actionmailer/lib/action_mailer/deprecated_api.rb +++ b/actionmailer/lib/action_mailer/deprecated_api.rb @@ -71,7 +71,6 @@ module ActionMailer # Alias controller_path to mailer_name so render :partial in views work. alias :controller_path :mailer_name - end module ClassMethods @@ -82,17 +81,14 @@ module ActionMailer # email = MyMailer.create_some_mail(parameters) # email.set_some_obscure_header "frobnicate" # MyMailer.deliver(email) - def deliver(mail) - return if @mail_was_called + def deliver(mail, show_warning=true) + if show_warning + ActiveSupport::Deprecation.warn "ActionMailer::Base.deliver is deprecated, just call " << + "deliver in the instance instead", caller + end + raise "no mail object available for delivery!" unless mail - - mail.register_for_delivery_notification(self) - - mail.delivery_method delivery_methods[delivery_method], - delivery_settings[delivery_method] - - mail.raise_delivery_errors = raise_delivery_errors - mail.perform_deliveries = perform_deliveries + wrap_delivery_behavior(mail) mail.deliver mail end @@ -122,23 +118,19 @@ module ActionMailer end end - def initialize(*) - super() - @mail_was_called = false - end - # Delivers a Mail object. By default, it delivers the cached mail # object (from the create! method). If no cached mail object exists, and # no alternate has been given as the parameter, this will fail. - def deliver!(mail = @message) - self.class.deliver(mail) + def deliver!(mail = @_message) + self.class.deliver(mail, false) end + alias :deliver :deliver! def render(*args) options = args.last.is_a?(Hash) ? args.last : {} if options[:body] - ActiveSupport::Deprecation.warn(':body in render deprecated. Please call body ' << - 'with a hash instead', caller[0,1]) + ActiveSupport::Deprecation.warn(':body in render deprecated. Please use instance ' << + 'variables as assigns instead', caller[0,1]) body options.delete(:body) end @@ -153,7 +145,7 @@ module ActionMailer create_parts create_mail end - @message + @_message end # Add a part to a multipart message, with the given content-type. The @@ -175,6 +167,7 @@ module ActionMailer # Add an attachment to a multipart message. This is simply a part with the # content-disposition set to "attachment". def attachment(params, &block) + ActiveSupport::Deprecation.warn "attachment is deprecated, please use the attachments API instead", caller[0,2] params = { :content_type => params } if String === params params[:content] ||= params.delete(:data) || params.delete(:body) @@ -197,13 +190,9 @@ module ActionMailer # render_message :template => "special_message" # render_message :inline => "<%= 'Hi!' %>" # - def render_message(object) - case object - when String - render_to_body(:template => object) - else - render_to_body(object) - end + def render_message(*args) + ActiveSupport::Deprecation.warn "render_message is deprecated, use render instead", caller[0,2] + render(*args) end private @@ -240,14 +229,12 @@ module ActionMailer end def create_mail #:nodoc: - m = @message + m = @_message - m.subject, = quote_any_if_necessary(charset, subject) - m.to, m.from = quote_any_address_if_necessary(charset, recipients, from) - m.bcc = quote_address_if_necessary(bcc, charset) unless bcc.nil? - m.cc = quote_address_if_necessary(cc, charset) unless cc.nil? - m.reply_to = quote_address_if_necessary(reply_to, charset) unless reply_to.nil? - m.mime_version = mime_version unless mime_version.nil? + quote_fields!({:subject => subject, :to => recipients, :from => from, + :bcc => bcc, :cc => cc, :reply_to => reply_to}, charset) + + m.mime_version = mime_version unless mime_version.nil? m.date = sent_on.to_time rescue sent_on if sent_on @headers.each { |k, v| m[k] = v } @@ -274,7 +261,7 @@ module ActionMailer m.content_transfer_encoding = '8bit' unless m.body.only_us_ascii? - @message + @_message end # Set up the default values for the various instance variables of this @@ -286,8 +273,9 @@ module ActionMailer @implicit_parts_order ||= self.class.default_implicit_parts_order.dup @mime_version ||= self.class.default_mime_version.dup if self.class.default_mime_version - @mailer_name ||= self.class.mailer_name.dup - @template ||= method_name + @mailer_name ||= self.class.mailer_name.dup + @template ||= method_name + @mail_was_called = false @parts ||= [] @headers ||= {} diff --git a/actionmailer/lib/action_mailer/tmail_compat.rb b/actionmailer/lib/action_mailer/tmail_compat.rb index d78332c1354..c6efdc53b67 100644 --- a/actionmailer/lib/action_mailer/tmail_compat.rb +++ b/actionmailer/lib/action_mailer/tmail_compat.rb @@ -3,7 +3,7 @@ module Mail def set_content_type(*args) ActiveSupport::Deprecation.warn('Message#set_content_type is deprecated, please just call ' << - 'Message#content_type with the same arguments', caller[0,10]) + 'Message#content_type with the same arguments', caller[0,2]) content_type(*args) end @@ -11,7 +11,7 @@ module Mail def transfer_encoding(value = nil) if value ActiveSupport::Deprecation.warn('Message#transfer_encoding is deprecated, please call ' << - 'Message#content_transfer_encoding with the same arguments', caller[0,10]) + 'Message#content_transfer_encoding with the same arguments', caller[0,2]) content_transfer_encoding(value) else old_transfer_encoding @@ -20,7 +20,7 @@ module Mail def original_filename ActiveSupport::Deprecation.warn('Message#original_filename is deprecated, ' << - 'please call Message#filename', caller[0,10]) + 'please call Message#filename', caller[0,2]) filename end From f30d73bab4c676b187276797ac2a6dc89132c43f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sun, 24 Jan 2010 17:31:18 +0100 Subject: [PATCH 40/63] Add new class delivery method API. --- actionmailer/lib/action_mailer/base.rb | 18 ++- .../lib/action_mailer/deprecated_api.rb | 25 +-- actionmailer/test/asset_host_test.rb | 12 +- actionmailer/test/base_test.rb | 135 +++++++--------- actionmailer/test/mail_helper_test.rb | 28 ++-- actionmailer/test/mail_layout_test.rb | 42 +++-- actionmailer/test/mail_render_test.rb | 62 ++++---- actionmailer/test/mail_service_test.rb | 148 +++++++++--------- actionmailer/test/subscriber_test.rb | 18 +-- actionmailer/test/test_helper_test.rb | 24 +-- actionmailer/test/tmail_compat_test.rb | 14 +- actionmailer/test/url_test.rb | 4 +- 12 files changed, 259 insertions(+), 271 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index b881611cfb6..8e30c54c496 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -253,6 +253,8 @@ module ActionMailer #:nodoc: # and appear last in the mime encoded message. You can also pick a different order from inside a method with # +implicit_parts_order+. class Base < AbstractController::Base + abstract! + include Quoting include AbstractController::Logger @@ -264,8 +266,8 @@ module ActionMailer #:nodoc: helper ActionMailer::MailHelper - include ActionMailer::DeprecatedApi extend ActionMailer::DeliveryMethods + include ActionMailer::DeprecatedApi add_delivery_method :smtp, Mail::SMTP, :address => "localhost", @@ -370,6 +372,20 @@ module ActionMailer #:nodoc: payload[:date] = mail.date payload[:mail] = mail.encoded end + + def respond_to?(method, *args) + super || action_methods.include?(method.to_s) + end + + protected + + def method_missing(method, *args) + if action_methods.include?(method.to_s) + new(method, *args).message + else + super + end + end end attr_internal :message diff --git a/actionmailer/lib/action_mailer/deprecated_api.rb b/actionmailer/lib/action_mailer/deprecated_api.rb index f969584a179..f36b1befd63 100644 --- a/actionmailer/lib/action_mailer/deprecated_api.rb +++ b/actionmailer/lib/action_mailer/deprecated_api.rb @@ -1,8 +1,8 @@ module ActionMailer - # TODO Remove this module all together in Rails 3.1. Ensure that super - # hooks in ActionMailer::Base are removed as well. - # - # Moved here to allow us to add the new Mail API + # Part of this API is deprecated and is going to be removed in Rails 3.1 (just check + # the methods which give you a warning). + # All the rest will be deprecated after 3.1 release instead, this allows a smoother + # migration path. module DeprecatedApi #:nodoc: extend ActiveSupport::Concern @@ -83,8 +83,8 @@ module ActionMailer # MyMailer.deliver(email) def deliver(mail, show_warning=true) if show_warning - ActiveSupport::Deprecation.warn "ActionMailer::Base.deliver is deprecated, just call " << - "deliver in the instance instead", caller + ActiveSupport::Deprecation.warn "#{self}.deliver is deprecated, call " << + "deliver in the mailer instance instead", caller[0,2] end raise "no mail object available for delivery!" unless mail @@ -100,9 +100,14 @@ module ActionMailer def method_missing(method_symbol, *parameters) #:nodoc: if match = matches_dynamic_method?(method_symbol) case match[1] - when 'create' then new(match[2], *parameters).message - when 'deliver' then new(match[2], *parameters).deliver! - when 'new' then nil + when 'create' + ActiveSupport::Deprecation.warn "#{self}.create_#{match[2]} is deprecated, " << + "use #{self}.#{match[2]} instead", caller[0,2] + new(match[2], *parameters).message + when 'deliver' + ActiveSupport::Deprecation.warn "#{self}.deliver_#{match[2]} is deprecated, " << + "use #{self}.#{match[2]}.deliver instead", caller[0,2] + new(match[2], *parameters).deliver else super end else @@ -167,7 +172,6 @@ module ActionMailer # Add an attachment to a multipart message. This is simply a part with the # content-disposition set to "attachment". def attachment(params, &block) - ActiveSupport::Deprecation.warn "attachment is deprecated, please use the attachments API instead", caller[0,2] params = { :content_type => params } if String === params params[:content] ||= params.delete(:data) || params.delete(:body) @@ -259,6 +263,7 @@ module ActionMailer end end + wrap_delivery_behavior! m.content_transfer_encoding = '8bit' unless m.body.only_us_ascii? @_message diff --git a/actionmailer/test/asset_host_test.rb b/actionmailer/test/asset_host_test.rb index 7ba78b6daa1..124032f1d92 100644 --- a/actionmailer/test/asset_host_test.rb +++ b/actionmailer/test/asset_host_test.rb @@ -1,8 +1,8 @@ require 'abstract_unit' class AssetHostMailer < ActionMailer::Base - def email_with_asset(recipient) - recipients recipient + def email_with_asset + recipients 'test@localhost' subject "testing email containing asset path while asset_host is set" from "tester@example.com" end @@ -13,8 +13,6 @@ class AssetHostTest < Test::Unit::TestCase set_delivery_method :test ActionMailer::Base.perform_deliveries = true ActionMailer::Base.deliveries.clear - - @recipient = 'test@localhost' end def teardown @@ -23,7 +21,7 @@ class AssetHostTest < Test::Unit::TestCase def test_asset_host_as_string ActionController::Base.asset_host = "http://www.example.com" - mail = AssetHostMailer.deliver_email_with_asset(@recipient) + mail = AssetHostMailer.email_with_asset assert_equal "\"Somelogo\"", mail.body.to_s.strip end @@ -35,7 +33,7 @@ class AssetHostTest < Test::Unit::TestCase "http://assets.example.com" end } - mail = AssetHostMailer.deliver_email_with_asset(@recipient) + mail = AssetHostMailer.email_with_asset assert_equal "\"Somelogo\"", mail.body.to_s.strip end @@ -48,7 +46,7 @@ class AssetHostTest < Test::Unit::TestCase end } mail = nil - assert_nothing_raised { mail = AssetHostMailer.deliver_email_with_asset(@recipient) } + assert_nothing_raised { mail = AssetHostMailer.email_with_asset } assert_equal "\"Somelogo\"", mail.body.to_s.strip end end \ No newline at end of file diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 7f41f3485ce..3b2a072dce6 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -3,40 +3,6 @@ require 'abstract_unit' # class Notifier < ActionMailer::Base # delivers_from 'notifications@example.com' -# -# def welcome(user) -# @user = user # available to the view -# mail(:subject => 'Welcome!', :to => user.email_address) -# # auto renders both welcome.text.erb and welcome.html.erb -# end -# -# def goodbye(user) -# headers["X-SPAM"] = 'Not-SPAM' -# mail(:subject => 'Goodbye', :to => user.email_address) do |format| -# format.html { render "shared_template "} -# format.text # goodbye.text.erb -# end -# end -# -# def surprise(user, gift) -# attachments[gift.name] = File.read(gift.path) -# mail(:subject => 'Surprise!', :to => user.email_address) do |format| -# format.html(:charset => "ascii") # surprise.html.erb -# format.text(:transfer_encoding => "base64") # surprise.text.erb -# end -# end -# -# def special_surprise(user, gift) -# attachments[gift.name] = { :content_type => "application/x-gzip", :content => File.read(gift.path) } -# mail(:to => 'special@example.com') # subject not required -# # auto renders both special_surprise.text.erb and special_surprise.html.erb -# end -# end -# -# mail = Notifier.welcome(user) # => returns a Mail object -# mail.deliver -# -# Notifier.welcome(user).deliver # => creates and sends the Mail in one step class BaseTest < ActiveSupport::TestCase DEFAULT_HEADERS = { :to => 'mikel@test.lindsaar.net', @@ -44,8 +10,6 @@ class BaseTest < ActiveSupport::TestCase :subject => 'The first email on new API!' } - # TODO Think on the simple case where I want to send an e-mail - # with attachment and small text (without need to add a template). class BaseMailer < ActionMailer::Base self.mailer_name = "base_mailer" @@ -60,7 +24,7 @@ class BaseTest < ActiveSupport::TestCase end def attachment_with_hash - attachments['invoice.jpg'] = { :content => "you smiling", :mime_type => "image/x-jpg", + attachments['invoice.jpg'] = { :data => "you smiling", :mime_type => "image/x-jpg", :transfer_encoding => "base64" } mail(DEFAULT_HEADERS) end @@ -93,12 +57,12 @@ class BaseTest < ActiveSupport::TestCase end test "method call to mail does not raise error" do - assert_nothing_raised { BaseMailer.deliver_welcome } + assert_nothing_raised { BaseMailer.welcome.deliver } end # Basic mail usage without block test "mail() should set the headers of the mail message" do - email = BaseMailer.deliver_welcome + email = BaseMailer.welcome.deliver assert_equal(email.to, ['mikel@test.lindsaar.net']) assert_equal(email.from, ['jose@test.plataformatec.com']) assert_equal(email.subject, 'The first email on new API!') @@ -106,13 +70,13 @@ class BaseTest < ActiveSupport::TestCase test "mail() with bcc, cc, content_type, charset, mime_version, reply_to and date" do @time = Time.now - email = BaseMailer.deliver_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) + 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).deliver assert_equal(email.bcc, ['bcc@test.lindsaar.net']) assert_equal(email.cc, ['cc@test.lindsaar.net']) assert_equal(email.content_type, 'multipart/mixed') @@ -123,50 +87,50 @@ class BaseTest < ActiveSupport::TestCase end test "mail() renders the template using the method being processed" do - email = BaseMailer.deliver_welcome + email = BaseMailer.welcome.deliver assert_equal("Welcome", email.body.encoded) end test "can pass in :body to the mail method hash" do - email = BaseMailer.deliver_welcome(:body => "Hello there") + email = BaseMailer.welcome(:body => "Hello there").deliver assert_equal("text/plain", email.mime_type) assert_equal("Hello there", email.body.encoded) end # Custom headers test "custom headers" do - email = BaseMailer.deliver_welcome + email = BaseMailer.welcome.deliver assert_equal("Not SPAM", email['X-SPAM'].decoded) end # Attachments test "attachment with content" do - email = BaseMailer.deliver_attachment_with_content + email = BaseMailer.attachment_with_content.deliver 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) end test "attachment gets content type from filename" do - email = BaseMailer.deliver_attachment_with_content + email = BaseMailer.attachment_with_content.deliver assert_equal('invoice.pdf', email.attachments[0].filename) end test "attachment with hash" do - email = BaseMailer.deliver_attachment_with_hash + email = BaseMailer.attachment_with_hash.deliver assert_equal(1, email.attachments.length) assert_equal('invoice.jpg', email.attachments[0].filename) assert_equal("\312\213\254\232)b", email.attachments['invoice.jpg'].decoded) end test "sets mime type to multipart/mixed when attachment is included" do - email = BaseMailer.deliver_attachment_with_content + email = BaseMailer.attachment_with_content.deliver assert_equal(1, email.attachments.length) assert_equal("multipart/mixed", email.mime_type) end test "adds the rendered template as part" do - email = BaseMailer.deliver_attachment_with_content + email = BaseMailer.attachment_with_content.deliver assert_equal(2, email.parts.length) assert_equal("multipart/mixed", email.mime_type) assert_equal("text/html", email.parts[0].mime_type) @@ -176,7 +140,7 @@ class BaseTest < ActiveSupport::TestCase end test "adds the given :body as part" do - email = BaseMailer.deliver_attachment_with_content(:body => "I'm the eggman") + email = BaseMailer.attachment_with_content(:body => "I'm the eggman").deliver assert_equal(2, email.parts.length) assert_equal("multipart/mixed", email.mime_type) assert_equal("text/plain", email.parts[0].mime_type) @@ -188,46 +152,46 @@ class BaseTest < ActiveSupport::TestCase # Defaults values test "uses default charset from class" do swap BaseMailer, :default_charset => "US-ASCII" do - email = BaseMailer.deliver_welcome + email = BaseMailer.welcome.deliver assert_equal("US-ASCII", email.charset) - email = BaseMailer.deliver_welcome(:charset => "iso-8559-1") + email = BaseMailer.welcome(:charset => "iso-8559-1").deliver assert_equal("iso-8559-1", email.charset) end end test "uses default content type from class" do swap BaseMailer, :default_content_type => "text/html" do - email = BaseMailer.deliver_welcome + email = BaseMailer.welcome.deliver assert_equal("text/html", email.mime_type) - email = BaseMailer.deliver_welcome(:content_type => "text/plain") + email = BaseMailer.welcome(:content_type => "text/plain").deliver assert_equal("text/plain", email.mime_type) end end test "uses default mime version from class" do swap BaseMailer, :default_mime_version => "2.0" do - email = BaseMailer.deliver_welcome + email = BaseMailer.welcome.deliver assert_equal("2.0", email.mime_version) - email = BaseMailer.deliver_welcome(:mime_version => "1.0") + email = BaseMailer.welcome(:mime_version => "1.0").deliver assert_equal("1.0", email.mime_version) end end test "subject gets default from I18n" do - email = BaseMailer.deliver_welcome(:subject => nil) + email = BaseMailer.welcome(:subject => nil).deliver assert_equal "Welcome", email.subject I18n.backend.store_translations('en', :actionmailer => {:base_mailer => {:welcome => {:subject => "New Subject!"}}}) - email = BaseMailer.deliver_welcome(:subject => nil) + email = BaseMailer.welcome(:subject => nil).deliver assert_equal "New Subject!", email.subject end # Implicit multipart test "implicit multipart" do - email = BaseMailer.deliver_implicit_multipart + email = BaseMailer.implicit_multipart.deliver assert_equal(2, email.parts.size) assert_equal("multipart/alternate", email.mime_type) assert_equal("text/plain", email.parts[0].mime_type) @@ -239,18 +203,18 @@ class BaseTest < ActiveSupport::TestCase test "implicit multipart with sort order" do order = ["text/html", "text/plain"] swap BaseMailer, :default_implicit_parts_order => order do - email = BaseMailer.deliver_implicit_multipart + email = BaseMailer.implicit_multipart.deliver assert_equal("text/html", email.parts[0].mime_type) assert_equal("text/plain", email.parts[1].mime_type) - email = BaseMailer.deliver_implicit_multipart(:parts_order => order.reverse) + email = BaseMailer.implicit_multipart(:parts_order => order.reverse).deliver assert_equal("text/plain", email.parts[0].mime_type) assert_equal("text/html", email.parts[1].mime_type) end end test "implicit multipart with attachments creates nested parts" do - email = BaseMailer.deliver_implicit_multipart(:attachments => true) + email = BaseMailer.implicit_multipart(:attachments => true).deliver assert_equal("application/pdf", email.parts[0].mime_type) assert_equal("multipart/alternate", email.parts[1].mime_type) assert_equal("text/plain", email.parts[1].parts[0].mime_type) @@ -262,7 +226,7 @@ class BaseTest < ActiveSupport::TestCase test "implicit multipart with attachments and sort order" do order = ["text/html", "text/plain"] swap BaseMailer, :default_implicit_parts_order => order do - email = BaseMailer.deliver_implicit_multipart(:attachments => true) + email = BaseMailer.implicit_multipart(:attachments => true).deliver assert_equal("application/pdf", email.parts[0].mime_type) assert_equal("multipart/alternate", email.parts[1].mime_type) assert_equal("text/plain", email.parts[1].parts[1].mime_type) @@ -272,7 +236,7 @@ class BaseTest < ActiveSupport::TestCase # Explicit multipart test "explicit multipart" do - email = BaseMailer.deliver_explicit_multipart + email = BaseMailer.explicit_multipart.deliver assert_equal(2, email.parts.size) assert_equal("multipart/alternate", email.mime_type) assert_equal("text/plain", email.parts[0].mime_type) @@ -284,18 +248,18 @@ class BaseTest < ActiveSupport::TestCase test "explicit multipart does not sort order" do order = ["text/html", "text/plain"] swap BaseMailer, :default_implicit_parts_order => order do - email = BaseMailer.deliver_explicit_multipart + email = BaseMailer.explicit_multipart.deliver assert_equal("text/plain", email.parts[0].mime_type) assert_equal("text/html", email.parts[1].mime_type) - email = BaseMailer.deliver_explicit_multipart(:parts_order => order.reverse) + email = BaseMailer.explicit_multipart(:parts_order => order.reverse).deliver assert_equal("text/plain", email.parts[0].mime_type) assert_equal("text/html", email.parts[1].mime_type) end end test "explicit multipart with attachments creates nested parts" do - email = BaseMailer.deliver_explicit_multipart(:attachments => true) + email = BaseMailer.explicit_multipart(:attachments => true).deliver assert_equal("application/pdf", email.parts[0].mime_type) assert_equal("multipart/alternate", email.parts[1].mime_type) assert_equal("text/plain", email.parts[1].parts[0].mime_type) @@ -305,7 +269,7 @@ class BaseTest < ActiveSupport::TestCase end test "explicit multipart with templates" do - email = BaseMailer.deliver_explicit_multipart_templates + email = BaseMailer.explicit_multipart_templates.deliver assert_equal(2, email.parts.size) assert_equal("multipart/alternate", email.mime_type) assert_equal("text/html", email.parts[0].mime_type) @@ -315,7 +279,7 @@ class BaseTest < ActiveSupport::TestCase end test "explicit multipart with any" do - email = BaseMailer.deliver_explicit_multipart_with_any + email = BaseMailer.explicit_multipart_with_any.deliver assert_equal(2, email.parts.size) assert_equal("multipart/alternate", email.mime_type) assert_equal("text/plain", email.parts[0].mime_type) @@ -324,21 +288,30 @@ class BaseTest < ActiveSupport::TestCase assert_equal("Format with any!", email.parts[1].body.encoded) end - test "ActionMailer should be told when Mail gets delivered" do - BaseMailer.deliveries.clear - BaseMailer.expects(:delivered_email).once - BaseMailer.deliver_welcome - assert_equal(1, BaseMailer.deliveries.length) + # Class level API with method missing + test "should respond to action methods" do + assert BaseMailer.respond_to?(:welcome) + assert BaseMailer.respond_to?(:implicit_multipart) + assert !BaseMailer.respond_to?(:mail) + assert !BaseMailer.respond_to?(:headers) end - test "Calling just the action should return the generated mail object" do + test "calling just the action should return the generated mail object" do BaseMailer.deliveries.clear email = BaseMailer.welcome assert_equal(0, BaseMailer.deliveries.length) assert_equal('The first email on new API!', email.subject) end - test "Calling deliver on the action should deliver the mail object" do + test "calling deliver on the action should deliver the mail object" do + BaseMailer.deliveries.clear + BaseMailer.expects(:delivered_email).once + BaseMailer.welcome.deliver + assert_equal(1, BaseMailer.deliveries.length) + end + + # Delivery hooks + test "ActionMailer should be told when Mail gets delivered" do BaseMailer.deliveries.clear BaseMailer.expects(:delivered_email).once BaseMailer.welcome.deliver diff --git a/actionmailer/test/mail_helper_test.rb b/actionmailer/test/mail_helper_test.rb index d81bc57ce02..a9b3cd3ce1d 100644 --- a/actionmailer/test/mail_helper_test.rb +++ b/actionmailer/test/mail_helper_test.rb @@ -10,22 +10,22 @@ class HelperMailer < ActionMailer::Base helper MailerHelper helper :example - def use_helper(recipient) - recipients recipient + def use_helper + recipients 'test@localhost' subject "using helpers" from "tester@example.com" end - def use_example_helper(recipient) - recipients recipient + def use_example_helper + recipients 'test@localhost' subject "using helpers" from "tester@example.com" @text = "emphasize me!" end - def use_mail_helper(recipient) - recipients recipient + def use_mail_helper + recipients 'test@localhost' subject "using mailing helpers" from "tester@example.com" @@ -37,8 +37,8 @@ class HelperMailer < ActionMailer::Base "it off!" end - def use_helper_method(recipient) - recipients recipient + def use_helper_method + recipients 'test@localhost' subject "using helpers" from "tester@example.com" @@ -53,7 +53,7 @@ class HelperMailer < ActionMailer::Base helper_method :name_of_the_mailer_class end -class MailerHelperTest < Test::Unit::TestCase +class MailerHelperTest < ActiveSupport::TestCase def new_mail( charset="utf-8" ) mail = Mail.new mail.set_content_type "text", "plain", { "charset" => charset } if charset @@ -64,8 +64,6 @@ class MailerHelperTest < Test::Unit::TestCase set_delivery_method :test ActionMailer::Base.perform_deliveries = true ActionMailer::Base.deliveries.clear - - @recipient = 'test@localhost' end def teardown @@ -73,22 +71,22 @@ class MailerHelperTest < Test::Unit::TestCase end def test_use_helper - mail = HelperMailer.create_use_helper(@recipient) + mail = HelperMailer.use_helper assert_match %r{Mr. Joe Person}, mail.encoded end def test_use_example_helper - mail = HelperMailer.create_use_example_helper(@recipient) + mail = HelperMailer.use_example_helper assert_match %r{emphasize me!}, mail.encoded end def test_use_helper_method - mail = HelperMailer.create_use_helper_method(@recipient) + mail = HelperMailer.use_helper_method assert_match %r{HelperMailer}, mail.encoded end def test_use_mail_helper - mail = HelperMailer.create_use_mail_helper(@recipient) + mail = HelperMailer.use_mail_helper assert_match %r{ But soft!}, mail.encoded assert_match %r{east, and\r\n Juliet}, mail.encoded end diff --git a/actionmailer/test/mail_layout_test.rb b/actionmailer/test/mail_layout_test.rb index b4bd5836160..4038fbf3399 100644 --- a/actionmailer/test/mail_layout_test.rb +++ b/actionmailer/test/mail_layout_test.rb @@ -2,14 +2,14 @@ require 'abstract_unit' class AutoLayoutMailer < ActionMailer::Base - def hello(recipient) - recipients recipient + def hello + recipients 'test@localhost' subject "You have a mail" from "tester@example.com" end - def spam(recipient) - recipients recipient + def spam + recipients 'test@localhost' subject "You have a mail" from "tester@example.com" @@ -17,8 +17,8 @@ class AutoLayoutMailer < ActionMailer::Base render(:inline => "Hello, <%= @world %>", :layout => 'spam') end - def nolayout(recipient) - recipients recipient + def nolayout + recipients 'test@localhost' subject "You have a mail" from "tester@example.com" @@ -26,8 +26,8 @@ class AutoLayoutMailer < ActionMailer::Base render(:inline => "Hello, <%= @world %>", :layout => false) end - def multipart(recipient, type = nil) - recipients recipient + def multipart(type = nil) + recipients 'test@localhost' subject "You have a mail" from "tester@example.com" @@ -38,14 +38,14 @@ end class ExplicitLayoutMailer < ActionMailer::Base layout 'spam', :except => [:logout] - def signup(recipient) - recipients recipient + def signup + recipients 'test@localhost' subject "You have a mail" from "tester@example.com" end - def logout(recipient) - recipients recipient + def logout + recipients 'test@localhost' subject "You have a mail" from "tester@example.com" end @@ -56,8 +56,6 @@ class LayoutMailerTest < Test::Unit::TestCase set_delivery_method :test ActionMailer::Base.perform_deliveries = true ActionMailer::Base.deliveries.clear - - @recipient = 'test@localhost' end def teardown @@ -65,12 +63,12 @@ class LayoutMailerTest < Test::Unit::TestCase end def test_should_pickup_default_layout - mail = AutoLayoutMailer.create_hello(@recipient) + mail = AutoLayoutMailer.hello assert_equal "Hello from layout Inside", mail.body.to_s.strip end def test_should_pickup_multipart_layout - mail = AutoLayoutMailer.create_multipart(@recipient) + mail = AutoLayoutMailer.multipart # CHANGED: content_type returns an object # assert_equal "multipart/alternative", mail.content_type assert_equal "multipart/alternative", mail.mime_type @@ -94,7 +92,7 @@ class LayoutMailerTest < Test::Unit::TestCase end def test_should_pickup_multipartmixed_layout - mail = AutoLayoutMailer.create_multipart(@recipient, "multipart/mixed") + mail = AutoLayoutMailer.multipart("multipart/mixed") # CHANGED: content_type returns an object # assert_equal "multipart/mixed", mail.content_type assert_equal "multipart/mixed", mail.mime_type @@ -116,7 +114,7 @@ class LayoutMailerTest < Test::Unit::TestCase end def test_should_fix_multipart_layout - mail = AutoLayoutMailer.create_multipart(@recipient, "text/plain") + mail = AutoLayoutMailer.multipart("text/plain") assert_equal "multipart/alternative", mail.mime_type assert_equal 2, mail.parts.size @@ -129,22 +127,22 @@ class LayoutMailerTest < Test::Unit::TestCase def test_should_pickup_layout_given_to_render - mail = AutoLayoutMailer.create_spam(@recipient) + mail = AutoLayoutMailer.spam assert_equal "Spammer layout Hello, Earth", mail.body.to_s.strip end def test_should_respect_layout_false - mail = AutoLayoutMailer.create_nolayout(@recipient) + mail = AutoLayoutMailer.nolayout assert_equal "Hello, Earth", mail.body.to_s.strip end def test_explicit_class_layout - mail = ExplicitLayoutMailer.create_signup(@recipient) + mail = ExplicitLayoutMailer.signup assert_equal "Spammer layout We do not spam", mail.body.to_s.strip end def test_explicit_layout_exceptions - mail = ExplicitLayoutMailer.create_logout(@recipient) + mail = ExplicitLayoutMailer.logout assert_equal "You logged out", mail.body.to_s.strip end end diff --git a/actionmailer/test/mail_render_test.rb b/actionmailer/test/mail_render_test.rb index 3424823c999..804200fd36b 100644 --- a/actionmailer/test/mail_render_test.rb +++ b/actionmailer/test/mail_render_test.rb @@ -1,8 +1,8 @@ require 'abstract_unit' class RenderMailer < ActionMailer::Base - def inline_template(recipient) - recipients recipient + def inline_template + recipients 'test@localhost' subject "using helpers" from "tester@example.com" @@ -10,46 +10,46 @@ class RenderMailer < ActionMailer::Base render :inline => "Hello, <%= @world %>" end - def file_template(recipient) - recipients recipient + def file_template + recipients 'test@localhost' subject "using helpers" from "tester@example.com" - @recipient = recipient + @recipient = 'test@localhost' render :file => "templates/signed_up" end - def implicit_body(recipient) - recipients recipient + def implicit_body + recipients 'test@localhost' subject "using helpers" from "tester@example.com" - @recipient = recipient + @recipient = 'test@localhost' render :template => "templates/signed_up" end - def rxml_template(recipient) - recipients recipient + def rxml_template + recipients 'test@localhost' subject "rendering rxml template" from "tester@example.com" end - def included_subtemplate(recipient) - recipients recipient + def included_subtemplate + recipients 'test@localhost' subject "Including another template in the one being rendered" from "tester@example.com" end - def mailer_accessor(recipient) - recipients recipient + def mailer_accessor + recipients 'test@localhost' subject "Mailer Accessor" from "tester@example.com" render :inline => "Look, <%= mailer.subject %>!" end - def no_instance_variable(recipient) - recipients recipient + def no_instance_variable + recipients 'test@localhost' subject "No Instance Variable" from "tester@example.com" @@ -65,16 +65,16 @@ class RenderMailer < ActionMailer::Base end class FirstMailer < ActionMailer::Base - def share(recipient) - recipients recipient + def share + recipients 'test@localhost' subject "using helpers" from "tester@example.com" end end class SecondMailer < ActionMailer::Base - def share(recipient) - recipients recipient + def share + recipients 'test@localhost' subject "using helpers" from "tester@example.com" end @@ -96,37 +96,37 @@ class RenderHelperTest < Test::Unit::TestCase end def test_implicit_body - mail = RenderMailer.create_implicit_body(@recipient) + mail = RenderMailer.implicit_body assert_equal "Hello there, \n\nMr. test@localhost", mail.body.to_s.strip end def test_inline_template - mail = RenderMailer.create_inline_template(@recipient) + mail = RenderMailer.inline_template assert_equal "Hello, Earth", mail.body.to_s.strip end def test_file_template - mail = RenderMailer.create_file_template(@recipient) + mail = RenderMailer.file_template assert_equal "Hello there, \n\nMr. test@localhost", mail.body.to_s.strip end def test_rxml_template - mail = RenderMailer.deliver_rxml_template(@recipient) + mail = RenderMailer.rxml_template.deliver assert_equal "\n", mail.body.to_s.strip end def test_included_subtemplate - mail = RenderMailer.deliver_included_subtemplate(@recipient) + mail = RenderMailer.included_subtemplate.deliver assert_equal "Hey Ho, let's go!", mail.body.to_s.strip end def test_mailer_accessor - mail = RenderMailer.deliver_mailer_accessor(@recipient) + mail = RenderMailer.mailer_accessor.deliver assert_equal "Look, Mailer Accessor!", mail.body.to_s.strip end def test_no_instance_variable - mail = RenderMailer.deliver_no_instance_variable(@recipient) + mail = RenderMailer.no_instance_variable.deliver assert_equal "Look, subject.nil? is true!", mail.body.to_s.strip end end @@ -145,13 +145,13 @@ class FirstSecondHelperTest < Test::Unit::TestCase end def test_ordering - mail = FirstMailer.create_share(@recipient) + mail = FirstMailer.share assert_equal "first mail", mail.body.to_s.strip - mail = SecondMailer.create_share(@recipient) + mail = SecondMailer.share assert_equal "second mail", mail.body.to_s.strip - mail = FirstMailer.create_share(@recipient) + mail = FirstMailer.share assert_equal "first mail", mail.body.to_s.strip - mail = SecondMailer.create_share(@recipient) + mail = SecondMailer.share assert_equal "second mail", mail.body.to_s.strip end end diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index df5afda4473..b3bf1b9acd7 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -120,11 +120,11 @@ class TestMailer < ActionMailer::Base content_type "multipart/alternative" part "text/plain" do |p| - p.body = render_message(:text => "blah") + p.body = render(:text => "blah") end part "text/html" do |p| - p.body = render_message(:inline => "<%= content_tag(:b, 'blah') %>") + p.body = render(:inline => "<%= content_tag(:b, 'blah') %>") end end @@ -297,7 +297,7 @@ class TestMailer < ActionMailer::Base recipients "no.one@nowhere.test" subject "return path test" from "some.one@somewhere.test" - headers "return-path" => "another@somewhere.test" + headers["return-path"] = "another@somewhere.test" render :text => "testing" end @@ -350,7 +350,7 @@ class ActionMailerTest < Test::Unit::TestCase def test_nested_parts created = nil - assert_nothing_raised { created = TestMailer.create_nested_multipart(@recipient)} + assert_nothing_raised { created = TestMailer.nested_multipart(@recipient)} assert_equal 2, created.parts.size assert_equal 2, created.parts.first.parts.size @@ -366,8 +366,8 @@ class ActionMailerTest < Test::Unit::TestCase def test_nested_parts_with_body created = nil - TestMailer.create_nested_multipart_with_body(@recipient) - assert_nothing_raised { created = TestMailer.create_nested_multipart_with_body(@recipient)} + TestMailer.nested_multipart_with_body(@recipient) + assert_nothing_raised { created = TestMailer.nested_multipart_with_body(@recipient)} assert_equal 1,created.parts.size assert_equal 2,created.parts.first.parts.size @@ -382,7 +382,7 @@ class ActionMailerTest < Test::Unit::TestCase def test_attachment_with_custom_header created = nil - assert_nothing_raised { created = TestMailer.create_attachment_with_custom_header(@recipient) } + assert_nothing_raised { created = TestMailer.attachment_with_custom_header(@recipient) } assert created.parts.any? { |p| p.header['content-id'].to_s == "" } end @@ -397,7 +397,7 @@ class ActionMailerTest < Test::Unit::TestCase expected.date = Time.now created = nil - assert_nothing_raised { created = TestMailer.create_signed_up(@recipient) } + assert_nothing_raised { created = TestMailer.signed_up(@recipient) } assert_not_nil created expected.message_id = '<123@456>' @@ -405,7 +405,7 @@ class ActionMailerTest < Test::Unit::TestCase assert_equal expected.encoded, created.encoded - assert_nothing_raised { TestMailer.deliver_signed_up(@recipient) } + assert_nothing_raised { TestMailer.signed_up(@recipient).deliver } delivered = ActionMailer::Base.deliveries.first assert_not_nil delivered @@ -425,7 +425,7 @@ class ActionMailerTest < Test::Unit::TestCase expected.date = Time.local(2004, 12, 12) created = nil - assert_nothing_raised { created = TestMailer.create_custom_template(@recipient) } + assert_nothing_raised { created = TestMailer.custom_template(@recipient) } assert_not_nil created expected.message_id = '<123@456>' created.message_id = '<123@456>' @@ -448,7 +448,7 @@ class ActionMailerTest < Test::Unit::TestCase # Now that the template is registered, there should be one part. The text/plain part. created = nil - assert_nothing_raised { created = TestMailer.create_custom_templating_extension(@recipient) } + assert_nothing_raised { created = TestMailer.custom_templating_extension(@recipient) } assert_not_nil created assert_equal 2, created.parts.length assert_equal 'text/plain', created.parts[0].mime_type @@ -464,13 +464,13 @@ class ActionMailerTest < Test::Unit::TestCase expected.date = Time.local(2004, 12, 12) created = nil - assert_nothing_raised { created = TestMailer.create_cancelled_account(@recipient) } + assert_nothing_raised { created = TestMailer.cancelled_account(@recipient) } assert_not_nil created expected.message_id = '<123@456>' created.message_id = '<123@456>' assert_equal expected.encoded, created.encoded - assert_nothing_raised { TestMailer.deliver_cancelled_account(@recipient) } + assert_nothing_raised { TestMailer.cancelled_account(@recipient).deliver } assert_not_nil ActionMailer::Base.deliveries.first delivered = ActionMailer::Base.deliveries.first expected.message_id = '<123@456>' @@ -491,7 +491,7 @@ class ActionMailerTest < Test::Unit::TestCase created = nil assert_nothing_raised do - created = TestMailer.create_cc_bcc @recipient + created = TestMailer.cc_bcc @recipient end assert_not_nil created expected.message_id = '<123@456>' @@ -499,7 +499,7 @@ class ActionMailerTest < Test::Unit::TestCase assert_equal expected.encoded, created.encoded assert_nothing_raised do - TestMailer.deliver_cc_bcc @recipient + TestMailer.cc_bcc(@recipient).deliver end assert_not_nil ActionMailer::Base.deliveries.first @@ -512,7 +512,7 @@ class ActionMailerTest < Test::Unit::TestCase def test_from_without_name_for_smtp ActionMailer::Base.delivery_method = :smtp - TestMailer.deliver_from_without_name + TestMailer.from_without_name.deliver mail = MockSMTP.deliveries.first assert_not_nil mail @@ -523,7 +523,7 @@ class ActionMailerTest < Test::Unit::TestCase def test_from_with_name_for_smtp ActionMailer::Base.delivery_method = :smtp - TestMailer.deliver_from_with_name + TestMailer.from_with_name.deliver mail = MockSMTP.deliveries.first assert_not_nil mail @@ -544,7 +544,7 @@ class ActionMailerTest < Test::Unit::TestCase created = nil assert_nothing_raised do - created = TestMailer.create_different_reply_to @recipient + created = TestMailer.different_reply_to @recipient end assert_not_nil created @@ -554,7 +554,7 @@ class ActionMailerTest < Test::Unit::TestCase assert_equal expected.encoded, created.encoded assert_nothing_raised do - TestMailer.deliver_different_reply_to @recipient + TestMailer.different_reply_to(@recipient).deliver end delivered = ActionMailer::Base.deliveries.first @@ -578,7 +578,7 @@ class ActionMailerTest < Test::Unit::TestCase created = nil assert_nothing_raised do - created = TestMailer.create_iso_charset @recipient + created = TestMailer.iso_charset @recipient end assert_not_nil created @@ -588,7 +588,7 @@ class ActionMailerTest < Test::Unit::TestCase assert_equal expected.encoded, created.encoded assert_nothing_raised do - TestMailer.deliver_iso_charset @recipient + TestMailer.iso_charset(@recipient).deliver end delivered = ActionMailer::Base.deliveries.first @@ -612,7 +612,7 @@ class ActionMailerTest < Test::Unit::TestCase created = nil assert_nothing_raised do - created = TestMailer.create_unencoded_subject @recipient + created = TestMailer.unencoded_subject @recipient end assert_not_nil created @@ -622,7 +622,7 @@ class ActionMailerTest < Test::Unit::TestCase assert_equal expected.encoded, created.encoded assert_nothing_raised do - TestMailer.deliver_unencoded_subject @recipient + TestMailer.unencoded_subject(@recipient).deliver end delivered = ActionMailer::Base.deliveries.first @@ -634,38 +634,33 @@ class ActionMailerTest < Test::Unit::TestCase assert_equal expected.encoded, delivered.encoded end - def test_instances_are_nil - assert_nil ActionMailer::Base.new - assert_nil TestMailer.new - end - def test_deliveries_array assert_not_nil ActionMailer::Base.deliveries assert_equal 0, ActionMailer::Base.deliveries.size - TestMailer.deliver_signed_up(@recipient) + TestMailer.signed_up(@recipient).deliver assert_equal 1, ActionMailer::Base.deliveries.size assert_not_nil ActionMailer::Base.deliveries.first end def test_perform_deliveries_flag ActionMailer::Base.perform_deliveries = false - TestMailer.deliver_signed_up(@recipient) + TestMailer.signed_up(@recipient).deliver assert_equal 0, ActionMailer::Base.deliveries.size ActionMailer::Base.perform_deliveries = true - TestMailer.deliver_signed_up(@recipient) + TestMailer.signed_up(@recipient).deliver assert_equal 1, ActionMailer::Base.deliveries.size end def test_doesnt_raise_errors_when_raise_delivery_errors_is_false ActionMailer::Base.raise_delivery_errors = false Mail::TestMailer.any_instance.expects(:deliver!).raises(Exception) - assert_nothing_raised { TestMailer.deliver_signed_up(@recipient) } + assert_nothing_raised { TestMailer.signed_up(@recipient).deliver } end def test_performs_delivery_via_sendmail IO.expects(:popen).once.with('/usr/sbin/sendmail -i -t -f "system@loudthinking.com" test@localhost', 'w+') ActionMailer::Base.delivery_method = :sendmail - TestMailer.deliver_signed_up(@recipient) + TestMailer.signed_up(@recipient).deliver end def test_unquote_quoted_printable_subject @@ -750,7 +745,7 @@ EOF created = nil assert_nothing_raised do - created = TestMailer.create_extended_headers @recipient + created = TestMailer.extended_headers @recipient end assert_not_nil created @@ -760,7 +755,7 @@ EOF assert_equal expected.encoded, created.encoded assert_nothing_raised do - TestMailer.deliver_extended_headers @recipient + TestMailer.extended_headers(@recipient).deliver end delivered = ActionMailer::Base.deliveries.first @@ -783,7 +778,7 @@ EOF expected.bcc = quote_address_if_necessary @recipient, "utf-8" expected.date = Time.local 2004, 12, 12 - created = TestMailer.create_utf8_body @recipient + created = TestMailer.utf8_body @recipient assert_match(/åœö blah/, created.encoded) end @@ -798,7 +793,7 @@ EOF expected.bcc = quote_address_if_necessary @recipient, "utf-8" expected.date = Time.local 2004, 12, 12 - created = TestMailer.create_utf8_body @recipient + created = TestMailer.utf8_body @recipient assert_match(/\nFrom: =\?utf-8\?Q\?Foo_.*?\?= \r/, created.encoded) assert_match(/\nTo: =\?utf-8\?Q\?Foo_.*?\?= , \r\n\tExample Recipient _Google}, mail.body.to_s end def test_various_newlines - mail = TestMailer.create_various_newlines(@recipient) + mail = TestMailer.various_newlines(@recipient) assert_equal("line #1\nline #2\nline #3\nline #4\n\n" + "line #5\n\nline#6\n\nline #7", mail.body.to_s) end def test_various_newlines_multipart - mail = TestMailer.create_various_newlines_multipart(@recipient) + mail = TestMailer.various_newlines_multipart(@recipient) assert_equal "line #1\nline #2\nline #3\nline #4\n\n", mail.parts[0].body.to_s assert_equal "

line #1

\n

line #2

\n

line #3

\n

line #4

\n\n", mail.parts[1].body.to_s assert_equal "line #1\r\nline #2\r\nline #3\r\nline #4\r\n\r\n", mail.parts[0].body.encoded @@ -962,7 +957,7 @@ EOF def test_headers_removed_on_smtp_delivery ActionMailer::Base.delivery_method = :smtp - TestMailer.deliver_cc_bcc(@recipient) + TestMailer.cc_bcc(@recipient).deliver assert MockSMTP.deliveries[0][2].include?("root@loudthinking.com") assert MockSMTP.deliveries[0][2].include?("nobody@loudthinking.com") assert MockSMTP.deliveries[0][2].include?(@recipient) @@ -975,7 +970,7 @@ EOF ActionMailer::Base.delivery_method = :file tmp_location = ActionMailer::Base.file_settings[:location] - TestMailer.deliver_cc_bcc(@recipient) + TestMailer.cc_bcc(@recipient).deliver assert File.exists?(tmp_location) assert File.directory?(tmp_location) assert File.exists?(File.join(tmp_location, @recipient)) @@ -1002,12 +997,12 @@ EOF expected = "01 Quien Te Dij\212at. Pitbull.mp3" if expected.respond_to?(:force_encoding) - result = attachment.original_filename.dup + result = attachment.filename.dup expected.force_encoding(Encoding::ASCII_8BIT) result.force_encoding(Encoding::ASCII_8BIT) assert_equal expected, result else - assert_equal expected, attachment.original_filename + assert_equal expected, attachment.filename end end @@ -1018,13 +1013,13 @@ EOF end def test_empty_header_values_omitted - result = TestMailer.create_unnamed_attachment(@recipient).encoded + result = TestMailer.unnamed_attachment(@recipient).encoded assert_match %r{Content-Type: application/octet-stream;}, result assert_match %r{Content-Disposition: attachment;}, result end def test_headers_with_nonalpha_chars - mail = TestMailer.create_headers_with_nonalpha_chars(@recipient) + mail = TestMailer.headers_with_nonalpha_chars(@recipient) assert !mail.from_addrs.empty? assert !mail.cc_addrs.empty? assert !mail.bcc_addrs.empty? @@ -1033,33 +1028,33 @@ EOF assert_match(/:/, mail[:bcc].decoded) end - def test_deliver_with_mail_object - mail = TestMailer.create_headers_with_nonalpha_chars(@recipient) - assert_nothing_raised { TestMailer.deliver(mail) } + def test_with_mail_object_deliver + mail = TestMailer.headers_with_nonalpha_chars(@recipient) + assert_nothing_raised { mail.deliver } assert_equal 1, TestMailer.deliveries.length end def test_multipart_with_template_path_with_dots - mail = FunkyPathMailer.create_multipart_with_template_path_with_dots(@recipient) + mail = FunkyPathMailer.multipart_with_template_path_with_dots(@recipient) assert_equal 2, mail.parts.length assert "text/plain", mail.parts[1].mime_type assert "utf-8", mail.parts[1].charset end def test_custom_content_type_attributes - mail = TestMailer.create_custom_content_type_attributes + mail = TestMailer.custom_content_type_attributes assert_match %r{format=flowed}, mail.content_type assert_match %r{charset=utf-8}, mail.content_type end def test_return_path_with_create - mail = TestMailer.create_return_path + mail = TestMailer.return_path assert_equal "another@somewhere.test", mail.return_path end def test_return_path_with_deliver ActionMailer::Base.delivery_method = :smtp - TestMailer.deliver_return_path + TestMailer.return_path.deliver assert_match %r{^Return-Path: }, MockSMTP.deliveries[0][0] assert_equal "another@somewhere.test", MockSMTP.deliveries[0][1].to_s end @@ -1069,7 +1064,7 @@ EOF MockSMTP.any_instance.expects(:respond_to?).with(:enable_starttls_auto).returns(true) MockSMTP.any_instance.expects(:enable_starttls_auto) ActionMailer::Base.delivery_method = :smtp - TestMailer.deliver_signed_up(@recipient) + TestMailer.signed_up(@recipient).deliver end def test_starttls_is_disabled_if_not_supported @@ -1077,16 +1072,16 @@ EOF MockSMTP.any_instance.expects(:respond_to?).with(:enable_starttls_auto).returns(false) MockSMTP.any_instance.expects(:enable_starttls_auto).never ActionMailer::Base.delivery_method = :smtp - TestMailer.deliver_signed_up(@recipient) + TestMailer.signed_up(@recipient).deliver end def test_starttls_is_not_enabled - ActionMailer::Base.delivery_settings[:smtp].merge!(:enable_starttls_auto => false) + TestMailer.delivery_settings[:smtp].merge!(:enable_starttls_auto => false) MockSMTP.any_instance.expects(:respond_to?).never - ActionMailer::Base.delivery_method = :smtp - TestMailer.deliver_signed_up(@recipient) + TestMailer.delivery_method = :smtp + TestMailer.signed_up(@recipient).deliver ensure - ActionMailer::Base.delivery_settings[:smtp].merge!(:enable_starttls_auto => true) + TestMailer.delivery_settings[:smtp].merge!(:enable_starttls_auto => true) end end @@ -1103,7 +1098,7 @@ class InheritableTemplateRootTest < Test::Unit::TestCase end end -class MethodNamingTest < Test::Unit::TestCase +class MethodNamingTest < ActiveSupport::TestCase class TestMailer < ActionMailer::Base def send render :text => 'foo' @@ -1123,12 +1118,13 @@ class MethodNamingTest < Test::Unit::TestCase def test_send_method assert_nothing_raised do assert_emails 1 do - TestMailer.deliver_send + assert_deprecated do + TestMailer.deliver_send + end end end end end - class RespondToTest < Test::Unit::TestCase class RespondToMailer < ActionMailer::Base; end @@ -1191,4 +1187,4 @@ class RespondToTest < Test::Unit::TestCase assert_match(/undefined method.*not_a_method/, error.message) end -end +end \ No newline at end of file diff --git a/actionmailer/test/subscriber_test.rb b/actionmailer/test/subscriber_test.rb index aed5d2ca7e9..6c347b83925 100644 --- a/actionmailer/test/subscriber_test.rb +++ b/actionmailer/test/subscriber_test.rb @@ -24,21 +24,21 @@ class AMSubscriberTest < ActionMailer::TestCase end def test_deliver_is_notified - TestMailer.deliver_basic + TestMailer.basic.deliver wait - assert_equal 1, @logger.logged(:info).size - assert_match /Sent mail to somewhere@example.com/, @logger.logged(:info).first - assert_equal 1, @logger.logged(:debug).size - assert_match /Hello world/, @logger.logged(:debug).first + assert_equal(1, @logger.logged(:info).size) + assert_match(/Sent mail to somewhere@example.com/, @logger.logged(:info).first) + assert_equal(1, @logger.logged(:debug).size) + assert_match(/Hello world/, @logger.logged(:debug).first) end def test_receive_is_notified fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email") TestMailer.receive(fixture) wait - assert_equal 1, @logger.logged(:info).size - assert_match /Received mail/, @logger.logged(:info).first - assert_equal 1, @logger.logged(:debug).size - assert_match /Jamis/, @logger.logged(:debug).first + assert_equal(1, @logger.logged(:info).size) + assert_match(/Received mail/, @logger.logged(:info).first) + assert_equal(1, @logger.logged(:debug).size) + assert_match(/Jamis/, @logger.logged(:debug).first) end end \ No newline at end of file diff --git a/actionmailer/test/test_helper_test.rb b/actionmailer/test/test_helper_test.rb index 48e4433e984..3a38a91c28f 100644 --- a/actionmailer/test/test_helper_test.rb +++ b/actionmailer/test/test_helper_test.rb @@ -44,7 +44,7 @@ class TestHelperMailerTest < ActionMailer::TestCase def test_assert_emails assert_nothing_raised do assert_emails 1 do - TestHelperMailer.deliver_test + TestHelperMailer.test.deliver end end end @@ -52,27 +52,27 @@ class TestHelperMailerTest < ActionMailer::TestCase def test_repeated_assert_emails_calls assert_nothing_raised do assert_emails 1 do - TestHelperMailer.deliver_test + TestHelperMailer.test.deliver end end assert_nothing_raised do assert_emails 2 do - TestHelperMailer.deliver_test - TestHelperMailer.deliver_test + TestHelperMailer.test.deliver + TestHelperMailer.test.deliver end end end def test_assert_emails_with_no_block assert_nothing_raised do - TestHelperMailer.deliver_test + TestHelperMailer.test.deliver assert_emails 1 end assert_nothing_raised do - TestHelperMailer.deliver_test - TestHelperMailer.deliver_test + TestHelperMailer.test.deliver + TestHelperMailer.test.deliver assert_emails 3 end end @@ -80,7 +80,7 @@ class TestHelperMailerTest < ActionMailer::TestCase def test_assert_no_emails assert_nothing_raised do assert_no_emails do - TestHelperMailer.create_test + TestHelperMailer.test end end end @@ -88,7 +88,7 @@ class TestHelperMailerTest < ActionMailer::TestCase def test_assert_emails_too_few_sent error = assert_raise ActiveSupport::TestCase::Assertion do assert_emails 2 do - TestHelperMailer.deliver_test + TestHelperMailer.test.deliver end end @@ -98,8 +98,8 @@ class TestHelperMailerTest < ActionMailer::TestCase def test_assert_emails_too_many_sent error = assert_raise ActiveSupport::TestCase::Assertion do assert_emails 1 do - TestHelperMailer.deliver_test - TestHelperMailer.deliver_test + TestHelperMailer.test.deliver + TestHelperMailer.test.deliver end end @@ -109,7 +109,7 @@ class TestHelperMailerTest < ActionMailer::TestCase def test_assert_no_emails_failure error = assert_raise ActiveSupport::TestCase::Assertion do assert_no_emails do - TestHelperMailer.deliver_test + TestHelperMailer.test.deliver end end diff --git a/actionmailer/test/tmail_compat_test.rb b/actionmailer/test/tmail_compat_test.rb index b7fcb3cfeae..7c1d9a07c1d 100644 --- a/actionmailer/test/tmail_compat_test.rb +++ b/actionmailer/test/tmail_compat_test.rb @@ -1,19 +1,23 @@ require 'abstract_unit' -class TmailCompatTest < Test::Unit::TestCase +class TmailCompatTest < ActiveSupport::TestCase def test_set_content_type_raises_deprecation_warning mail = Mail.new - assert_nothing_raised do - mail.set_content_type "text/plain" + assert_deprecated do + assert_nothing_raised do + mail.set_content_type "text/plain" + end end assert_equal mail.mime_type, "text/plain" end def test_transfer_encoding_raises_deprecation_warning mail = Mail.new - assert_nothing_raised do - mail.transfer_encoding "base64" + assert_deprecated do + assert_nothing_raised do + mail.transfer_encoding "base64" + end end assert_equal mail.content_transfer_encoding, "base64" end diff --git a/actionmailer/test/url_test.rb b/actionmailer/test/url_test.rb index b7ae5304ed3..10b6a36efd5 100644 --- a/actionmailer/test/url_test.rb +++ b/actionmailer/test/url_test.rb @@ -67,14 +67,14 @@ class ActionMailerUrlTest < Test::Unit::TestCase expected.date = Time.local(2004, 12, 12) created = nil - assert_nothing_raised { created = TestMailer.create_signed_up_with_url(@recipient) } + assert_nothing_raised { created = TestMailer.signed_up_with_url(@recipient) } assert_not_nil created expected.message_id = '<123@456>' created.message_id = '<123@456>' assert_equal expected.encoded, created.encoded - assert_nothing_raised { TestMailer.deliver_signed_up_with_url(@recipient) } + assert_nothing_raised { TestMailer.signed_up_with_url(@recipient).deliver } assert_not_nil ActionMailer::Base.deliveries.first delivered = ActionMailer::Base.deliveries.first From 5dead5bb88cf04b039df8a1a51ffdb18d0443efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sun, 24 Jan 2010 17:49:44 +0100 Subject: [PATCH 41/63] Maintain old_api and deprecated_api in different files. --- actionmailer/lib/action_mailer.rb | 1 + actionmailer/lib/action_mailer/base.rb | 1 + .../lib/action_mailer/deprecated_api.rb | 252 +----------------- actionmailer/lib/action_mailer/old_api.rb | 250 +++++++++++++++++ 4 files changed, 261 insertions(+), 243 deletions(-) create mode 100644 actionmailer/lib/action_mailer/old_api.rb diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 67466e15e2b..83398261974 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -36,6 +36,7 @@ module ActionMailer autoload :DeliveryMethods autoload :DeprecatedApi autoload :MailHelper + autoload :OldApi autoload :Quoting autoload :TestCase autoload :TestHelper diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 8e30c54c496..77b9ac4a976 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -267,6 +267,7 @@ module ActionMailer #:nodoc: helper ActionMailer::MailHelper extend ActionMailer::DeliveryMethods + include ActionMailer::OldApi include ActionMailer::DeprecatedApi add_delivery_method :smtp, Mail::SMTP, diff --git a/actionmailer/lib/action_mailer/deprecated_api.rb b/actionmailer/lib/action_mailer/deprecated_api.rb index f36b1befd63..530a9c19224 100644 --- a/actionmailer/lib/action_mailer/deprecated_api.rb +++ b/actionmailer/lib/action_mailer/deprecated_api.rb @@ -1,78 +1,10 @@ module ActionMailer - # Part of this API is deprecated and is going to be removed in Rails 3.1 (just check - # the methods which give you a warning). - # All the rest will be deprecated after 3.1 release instead, this allows a smoother - # migration path. + # This is the API which is deprecated and is going to be removed on Rails 3.1 release. + # Part of the old API will be deprecated after 3.1, for a smoother deprecation process. + # Chech those in OldApi instead. module DeprecatedApi #:nodoc: extend ActiveSupport::Concern - included do - extend ActionMailer::AdvAttrAccessor - - @@protected_instance_variables = %w(@parts) - cattr_reader :protected_instance_variables - - # Specify the BCC addresses for the message - adv_attr_accessor :bcc - - # Specify the CC addresses for the message. - adv_attr_accessor :cc - - # Specify the charset to use for the message. This defaults to the - # +default_charset+ specified for ActionMailer::Base. - adv_attr_accessor :charset - - # Specify the content type for the message. This defaults to text/plain - # in most cases, but can be automatically set in some situations. - adv_attr_accessor :content_type - - # Specify the from address for the message. - adv_attr_accessor :from - - # Specify the address (if different than the "from" address) to direct - # replies to this message. - adv_attr_accessor :reply_to - - # Specify additional headers to be added to the message. - adv_attr_accessor :headers - - # Specify the order in which parts should be sorted, based on content-type. - # This defaults to the value for the +default_implicit_parts_order+. - adv_attr_accessor :implicit_parts_order - - # Defaults to "1.0", but may be explicitly given if needed. - adv_attr_accessor :mime_version - - # The recipient addresses for the message, either as a string (for a single - # address) or an array (for multiple addresses). - adv_attr_accessor :recipients - - # The date on which the message was sent. If not set (the default), the - # header will be set by the delivery agent. - adv_attr_accessor :sent_on - - # Specify the subject of the message. - adv_attr_accessor :subject - - # Specify the template name to use for current message. This is the "base" - # template name, without the extension or directory, and may be used to - # have multiple mailer methods share the same template. - adv_attr_accessor :template - - # Override the mailer name, which defaults to an inflected version of the - # mailer's class name. If you want to use a template in a non-standard - # location, you can use this to specify that location. - adv_attr_accessor :mailer_name - - # Define the body of the message. This is either a Hash (in which case it - # specifies the variables to pass to the template when it is rendered), - # or a string, in which case it specifies the actual text of the message. - adv_attr_accessor :body - - # Alias controller_path to mailer_name so render :partial in views work. - alias :controller_path :mailer_name - end - module ClassMethods # Deliver the given mail object directly. This can be used to deliver @@ -107,7 +39,7 @@ module ActionMailer when 'deliver' ActiveSupport::Deprecation.warn "#{self}.deliver_#{match[2]} is deprecated, " << "use #{self}.#{match[2]}.deliver instead", caller[0,2] - new(match[2], *parameters).deliver + new(match[2], *parameters).message.deliver else super end else @@ -127,6 +59,8 @@ module ActionMailer # object (from the create! method). If no cached mail object exists, and # no alternate has been given as the parameter, this will fail. def deliver!(mail = @_message) + ActiveSupport::Deprecation.warn "Calling deliver in the AM::Base object is deprecated, " << + "please call deliver in the Mail instance", caller[0,2] self.class.deliver(mail, false) end alias :deliver :deliver! @@ -143,48 +77,6 @@ module ActionMailer super end - def process(method_name, *args) - initialize_defaults(method_name) - super - unless @mail_was_called - create_parts - create_mail - end - @_message - end - - # Add a part to a multipart message, with the given content-type. The - # part itself is yielded to the block so that other properties (charset, - # body, headers, etc.) can be set on it. - def part(params) - params = {:content_type => params} if String === params - - if custom_headers = params.delete(:headers) - params.merge!(custom_headers) - end - - part = Mail::Part.new(params) - - yield part if block_given? - @parts << part - end - - # Add an attachment to a multipart message. This is simply a part with the - # content-disposition set to "attachment". - def attachment(params, &block) - params = { :content_type => params } if String === params - - params[:content] ||= params.delete(:data) || params.delete(:body) - - if params[:filename] - params = normalize_file_hash(params) - else - params = normalize_nonfile_hash(params) - end - - part(params, &block) - end - # Render a message but does not set it as mail body. Useful for rendering # data for part and attachments. # @@ -201,140 +93,14 @@ module ActionMailer private - def normalize_nonfile_hash(params) - content_disposition = "attachment;" - - mime_type = params.delete(:mime_type) - - if content_type = params.delete(:content_type) - content_type = "#{mime_type || content_type};" - end - - params[:body] = params.delete(:data) if params[:data] - - { :content_type => content_type, - :content_disposition => content_disposition }.merge(params) - end - - def normalize_file_hash(params) - filename = File.basename(params.delete(:filename)) - content_disposition = "attachment; filename=\"#{File.basename(filename)}\"" - - mime_type = params.delete(:mime_type) - - if (content_type = params.delete(:content_type)) && (content_type !~ /filename=/) - content_type = "#{mime_type || content_type}; filename=\"#{filename}\"" - end - - params[:body] = params.delete(:data) if params[:data] - - { :content_type => content_type, - :content_disposition => content_disposition }.merge(params) - end - - def create_mail #:nodoc: - m = @_message - - quote_fields!({:subject => subject, :to => recipients, :from => from, - :bcc => bcc, :cc => cc, :reply_to => reply_to}, charset) - - m.mime_version = mime_version unless mime_version.nil? - m.date = sent_on.to_time rescue sent_on if sent_on - - @headers.each { |k, v| m[k] = v } - - real_content_type, ctype_attrs = parse_content_type - main_type, sub_type = split_content_type(real_content_type) - - if @parts.size == 1 && @parts.first.parts.empty? - m.content_type([main_type, sub_type, ctype_attrs]) - m.body = @parts.first.body.encoded - else - @parts.each do |p| - m.add_part(p) - end - - m.body.set_sort_order(@implicit_parts_order) - m.body.sort_parts! - - if real_content_type =~ /multipart/ - ctype_attrs.delete "charset" - m.content_type([main_type, sub_type, ctype_attrs]) - end - end - - wrap_delivery_behavior! - m.content_transfer_encoding = '8bit' unless m.body.only_us_ascii? - - @_message - end - - # Set up the default values for the various instance variables of this - # mailer. Subclasses may override this method to provide different - # defaults. - def initialize_defaults(method_name) #:nodoc: - @charset ||= self.class.default_charset.dup - @content_type ||= self.class.default_content_type.dup - @implicit_parts_order ||= self.class.default_implicit_parts_order.dup - @mime_version ||= self.class.default_mime_version.dup if self.class.default_mime_version - - @mailer_name ||= self.class.mailer_name.dup - @template ||= method_name - @mail_was_called = false - - @parts ||= [] - @headers ||= {} - @sent_on ||= Time.now - @body ||= {} - end def create_parts #:nodoc: - if String === @body - self.response_body = @body - elsif @body.is_a?(Hash) && !@body.empty? + if @body.is_a?(Hash) && !@body.empty? + ActiveSupport::Deprecation.warn "Giving a hash to body is deprecated, please use instance variables instead", caller[0,2] @body.each { |k, v| instance_variable_set(:"@#{k}", v) } end - if String === response_body - @parts.unshift create_inline_part(response_body) - else - self.class.template_root.find_all(@template, {}, @mailer_name).each do |template| - @parts << create_inline_part(render_to_body(:_template => template), template.mime_type) - end - - if @parts.size > 1 - @content_type = "multipart/alternative" if @content_type !~ /^multipart/ - end - - # If this is a multipart e-mail add the mime_version if it is not - # already set. - @mime_version ||= "1.0" if !@parts.empty? - end - end - - def create_inline_part(body, mime_type=nil) #:nodoc: - ct = mime_type || "text/plain" - main_type, sub_type = split_content_type(ct.to_s) - - Mail::Part.new( - :content_type => [main_type, sub_type, {:charset => charset}], - :content_disposition => "inline", - :body => body - ) - end - - def split_content_type(ct) #:nodoc: - ct.to_s.split("/") - end - - def parse_content_type(defaults=nil) #:nodoc: - if @content_type.blank? - [ nil, {} ] - else - ctype, *attrs = @content_type.split(/;\s*/) - attrs = attrs.inject({}) { |h,s| k,v = s.split(/\=/, 2); h[k] = v; h } - [ctype, {"charset" => @charset}.merge(attrs)] - end + super end end diff --git a/actionmailer/lib/action_mailer/old_api.rb b/actionmailer/lib/action_mailer/old_api.rb new file mode 100644 index 00000000000..204dc473e83 --- /dev/null +++ b/actionmailer/lib/action_mailer/old_api.rb @@ -0,0 +1,250 @@ +module ActionMailer + module OldApi + extend ActiveSupport::Concern + + included do + extend ActionMailer::AdvAttrAccessor + + @@protected_instance_variables = %w(@parts) + cattr_reader :protected_instance_variables + + # Specify the BCC addresses for the message + adv_attr_accessor :bcc + + # Specify the CC addresses for the message. + adv_attr_accessor :cc + + # Specify the charset to use for the message. This defaults to the + # +default_charset+ specified for ActionMailer::Base. + adv_attr_accessor :charset + + # Specify the content type for the message. This defaults to text/plain + # in most cases, but can be automatically set in some situations. + adv_attr_accessor :content_type + + # Specify the from address for the message. + adv_attr_accessor :from + + # Specify the address (if different than the "from" address) to direct + # replies to this message. + adv_attr_accessor :reply_to + + # Specify additional headers to be added to the message. + adv_attr_accessor :headers + + # Specify the order in which parts should be sorted, based on content-type. + # This defaults to the value for the +default_implicit_parts_order+. + adv_attr_accessor :implicit_parts_order + + # Defaults to "1.0", but may be explicitly given if needed. + adv_attr_accessor :mime_version + + # The recipient addresses for the message, either as a string (for a single + # address) or an array (for multiple addresses). + adv_attr_accessor :recipients + + # The date on which the message was sent. If not set (the default), the + # header will be set by the delivery agent. + adv_attr_accessor :sent_on + + # Specify the subject of the message. + adv_attr_accessor :subject + + # Specify the template name to use for current message. This is the "base" + # template name, without the extension or directory, and may be used to + # have multiple mailer methods share the same template. + adv_attr_accessor :template + + # Override the mailer name, which defaults to an inflected version of the + # mailer's class name. If you want to use a template in a non-standard + # location, you can use this to specify that location. + adv_attr_accessor :mailer_name + + # Define the body of the message. This is either a Hash (in which case it + # specifies the variables to pass to the template when it is rendered), + # or a string, in which case it specifies the actual text of the message. + adv_attr_accessor :body + + # Alias controller_path to mailer_name so render :partial in views work. + alias :controller_path :mailer_name + end + + def process(method_name, *args) + initialize_defaults(method_name) + super + unless @mail_was_called + create_parts + create_mail + end + @_message + end + + # Add a part to a multipart message, with the given content-type. The + # part itself is yielded to the block so that other properties (charset, + # body, headers, etc.) can be set on it. + def part(params) + params = {:content_type => params} if String === params + + if custom_headers = params.delete(:headers) + params.merge!(custom_headers) + end + + part = Mail::Part.new(params) + + yield part if block_given? + @parts << part + end + + # Add an attachment to a multipart message. This is simply a part with the + # content-disposition set to "attachment". + def attachment(params, &block) + params = { :content_type => params } if String === params + + params[:content] ||= params.delete(:data) || params.delete(:body) + + if params[:filename] + params = normalize_file_hash(params) + else + params = normalize_nonfile_hash(params) + end + + part(params, &block) + end + + protected + + def normalize_nonfile_hash(params) + content_disposition = "attachment;" + + mime_type = params.delete(:mime_type) + + if content_type = params.delete(:content_type) + content_type = "#{mime_type || content_type};" + end + + params[:body] = params.delete(:data) if params[:data] + + { :content_type => content_type, + :content_disposition => content_disposition }.merge(params) + end + + def normalize_file_hash(params) + filename = File.basename(params.delete(:filename)) + content_disposition = "attachment; filename=\"#{File.basename(filename)}\"" + + mime_type = params.delete(:mime_type) + + if (content_type = params.delete(:content_type)) && (content_type !~ /filename=/) + content_type = "#{mime_type || content_type}; filename=\"#{filename}\"" + end + + params[:body] = params.delete(:data) if params[:data] + + { :content_type => content_type, + :content_disposition => content_disposition }.merge(params) + end + + def create_mail #:nodoc: + m = @_message + + quote_fields!({:subject => subject, :to => recipients, :from => from, + :bcc => bcc, :cc => cc, :reply_to => reply_to}, charset) + + m.mime_version = mime_version unless mime_version.nil? + m.date = sent_on.to_time rescue sent_on if sent_on + + @headers.each { |k, v| m[k] = v } + + real_content_type, ctype_attrs = parse_content_type + main_type, sub_type = split_content_type(real_content_type) + + if @parts.size == 1 && @parts.first.parts.empty? + m.content_type([main_type, sub_type, ctype_attrs]) + m.body = @parts.first.body.encoded + else + @parts.each do |p| + m.add_part(p) + end + + m.body.set_sort_order(@implicit_parts_order) + m.body.sort_parts! + + if real_content_type =~ /multipart/ + ctype_attrs.delete "charset" + m.content_type([main_type, sub_type, ctype_attrs]) + end + end + + wrap_delivery_behavior! + m.content_transfer_encoding = '8bit' unless m.body.only_us_ascii? + + @_message + end + + # Set up the default values for the various instance variables of this + # mailer. Subclasses may override this method to provide different + # defaults. + def initialize_defaults(method_name) #:nodoc: + @charset ||= self.class.default_charset.dup + @content_type ||= self.class.default_content_type.dup + @implicit_parts_order ||= self.class.default_implicit_parts_order.dup + @mime_version ||= self.class.default_mime_version.dup if self.class.default_mime_version + + @mailer_name ||= self.class.mailer_name.dup + @template ||= method_name + @mail_was_called = false + + @parts ||= [] + @headers ||= {} + @sent_on ||= Time.now + @body ||= {} + end + + def create_parts #:nodoc: + if String === @body + self.response_body = @body + end + + if String === response_body + @parts.unshift create_inline_part(response_body) + else + self.class.template_root.find_all(@template, {}, @mailer_name).each do |template| + @parts << create_inline_part(render_to_body(:_template => template), template.mime_type) + end + + if @parts.size > 1 + @content_type = "multipart/alternative" if @content_type !~ /^multipart/ + end + + # If this is a multipart e-mail add the mime_version if it is not + # already set. + @mime_version ||= "1.0" if !@parts.empty? + end + end + + def create_inline_part(body, mime_type=nil) #:nodoc: + ct = mime_type || "text/plain" + main_type, sub_type = split_content_type(ct.to_s) + + Mail::Part.new( + :content_type => [main_type, sub_type, {:charset => charset}], + :content_disposition => "inline", + :body => body + ) + end + + def split_content_type(ct) #:nodoc: + ct.to_s.split("/") + end + + def parse_content_type(defaults=nil) #:nodoc: + if @content_type.blank? + [ nil, {} ] + else + ctype, *attrs = @content_type.split(/;\s*/) + attrs = attrs.inject({}) { |h,s| k,v = s.split(/\=/, 2); h[k] = v; h } + [ctype, {"charset" => @charset}.merge(attrs)] + end + end + end +end \ No newline at end of file From 0d931fecbb1132abb71a83bb91435812f2012d0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sun, 24 Jan 2010 18:11:57 +0100 Subject: [PATCH 42/63] Finish cleaning up delivery methods implementation. --- actionmailer/lib/action_mailer/base.rb | 29 +---- .../lib/action_mailer/delivery_methods.rb | 116 ++++++++++-------- ...ethod_test.rb => delivery_methods_test.rb} | 1 - actionmailer/test/mail_service_test.rb | 22 ++-- 4 files changed, 76 insertions(+), 92 deletions(-) rename actionmailer/test/{delivery_method_test.rb => delivery_methods_test.rb} (97%) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 77b9ac4a976..ad3a9c3c6d4 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -253,10 +253,9 @@ module ActionMailer #:nodoc: # and appear last in the mime encoded message. You can also pick a different order from inside a method with # +implicit_parts_order+. class Base < AbstractController::Base + include DeliveryMethods, Quoting abstract! - include Quoting - include AbstractController::Logger include AbstractController::Rendering include AbstractController::LocalizedCache @@ -266,31 +265,9 @@ module ActionMailer #:nodoc: helper ActionMailer::MailHelper - extend ActionMailer::DeliveryMethods include ActionMailer::OldApi include ActionMailer::DeprecatedApi - add_delivery_method :smtp, Mail::SMTP, - :address => "localhost", - :port => 25, - :domain => 'localhost.localdomain', - :user_name => nil, - :password => nil, - :authentication => nil, - :enable_starttls_auto => true - - add_delivery_method :file, Mail::FileDelivery, - :location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails" - - add_delivery_method :sendmail, Mail::Sendmail, - :location => '/usr/sbin/sendmail', - :arguments => '-i -t' - - add_delivery_method :test, Mail::TestMailer - - superclass_delegating_reader :delivery_method - self.delivery_method = :smtp - private_class_method :new #:nodoc: cattr_accessor :raise_delivery_errors @@ -493,10 +470,6 @@ module ActionMailer #:nodoc: [responses, sort_order] end - def wrap_delivery_behavior!(method=nil) #:nodoc: - self.class.wrap_delivery_behavior(@_message, method) - end - def create_parts_from_responses(m, responses, charset) #:nodoc: if responses.size == 1 && !m.has_attachments? m.body = responses[0][:body] diff --git a/actionmailer/lib/action_mailer/delivery_methods.rb b/actionmailer/lib/action_mailer/delivery_methods.rb index 16b84d41183..38325e512fc 100644 --- a/actionmailer/lib/action_mailer/delivery_methods.rb +++ b/actionmailer/lib/action_mailer/delivery_methods.rb @@ -1,63 +1,75 @@ +require 'tmpdir' + module ActionMailer - # This modules makes a DSL for adding delivery methods to ActionMailer + # Provides a DSL for adding delivery methods to ActionMailer. module DeliveryMethods - # TODO Make me class inheritable - def delivery_settings - @@delivery_settings ||= Hash.new { |h,k| h[k] = {} } + extend ActiveSupport::Concern + + included do + extlib_inheritable_accessor :delivery_methods, :delivery_method, + :instance_writer => false + + self.delivery_methods = {} + self.delivery_method = :smtp + + add_delivery_method :smtp, Mail::SMTP, + :address => "localhost", + :port => 25, + :domain => 'localhost.localdomain', + :user_name => nil, + :password => nil, + :authentication => nil, + :enable_starttls_auto => true + + add_delivery_method :file, Mail::FileDelivery, + :location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails" + + add_delivery_method :sendmail, Mail::Sendmail, + :location => '/usr/sbin/sendmail', + :arguments => '-i -t' + + add_delivery_method :test, Mail::TestMailer end - def delivery_methods - @@delivery_methods ||= {} - end - - def delivery_method=(method) - raise ArgumentError, "Unknown delivery method #{method.inspect}" unless delivery_methods[method] - @delivery_method = method - end - - def add_delivery_method(symbol, klass, default_options={}) - self.delivery_methods[symbol] = klass - self.delivery_settings[symbol] = default_options - end - - def wrap_delivery_behavior(mail, method=nil) - method ||= delivery_method - - mail.register_for_delivery_notification(self) - - if method.is_a?(Symbol) - mail.delivery_method(delivery_methods[method], - delivery_settings[method]) - else - mail.delivery_method(method) - end - - mail.perform_deliveries = perform_deliveries - mail.raise_delivery_errors = raise_delivery_errors - end - - - def respond_to?(method_symbol, include_private = false) #:nodoc: - matches_settings_method?(method_symbol) || super - end - - protected - - # TODO Get rid of this method missing magic - def method_missing(method_symbol, *parameters) #:nodoc: - if match = matches_settings_method?(method_symbol) - if match[2] - delivery_settings[match[1].to_sym] = parameters[0] - else - delivery_settings[match[1].to_sym] + module ClassMethods + # Adds a new delivery method through the given class using the given symbol + # as alias and the default options supplied: + # + # Example: + # + # add_delivery_method :sendmail, Mail::Sendmail, + # :location => '/usr/sbin/sendmail', + # :arguments => '-i -t' + # + def add_delivery_method(symbol, klass, default_options={}) + unless respond_to?(:"#{symbol}_settings") + extlib_inheritable_accessor(:"#{symbol}_settings", :instance_writer => false) end - else - super + + send(:"#{symbol}_settings=", default_options) + self.delivery_methods[symbol.to_sym] = klass + end + + def wrap_delivery_behavior(mail, method=delivery_method) #:nodoc: + mail.register_for_delivery_notification(self) + + if method.is_a?(Symbol) + if klass = delivery_methods[method.to_sym] + mail.delivery_method(klass, send(:"#{method}_settings")) + else + raise "Invalid delivery method #{method.inspect}" + end + else + mail.delivery_method(method) + end + + mail.perform_deliveries = perform_deliveries + mail.raise_delivery_errors = raise_delivery_errors end end - def matches_settings_method?(method_name) #:nodoc: - /(#{delivery_methods.keys.join('|')})_settings(=)?$/.match(method_name.to_s) + def wrap_delivery_behavior!(*args) #:nodoc: + self.class.wrap_delivery_behavior(message, *args) end end end \ No newline at end of file diff --git a/actionmailer/test/delivery_method_test.rb b/actionmailer/test/delivery_methods_test.rb similarity index 97% rename from actionmailer/test/delivery_method_test.rb rename to actionmailer/test/delivery_methods_test.rb index 1e7408d6d6f..de3d54197d3 100644 --- a/actionmailer/test/delivery_method_test.rb +++ b/actionmailer/test/delivery_methods_test.rb @@ -47,7 +47,6 @@ class CustomDeliveryMethodsTest < ActionMailer::TestCase def teardown ActionMailer::Base.delivery_methods.delete(:custom) - ActionMailer::Base.delivery_settings.delete(:custom) end def test_allow_to_add_a_custom_delivery_method diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index b3bf1b9acd7..c0a3f655b9d 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -511,7 +511,7 @@ class ActionMailerTest < Test::Unit::TestCase end def test_from_without_name_for_smtp - ActionMailer::Base.delivery_method = :smtp + TestMailer.delivery_method = :smtp TestMailer.from_without_name.deliver mail = MockSMTP.deliveries.first @@ -522,7 +522,7 @@ class ActionMailerTest < Test::Unit::TestCase end def test_from_with_name_for_smtp - ActionMailer::Base.delivery_method = :smtp + TestMailer.delivery_method = :smtp TestMailer.from_with_name.deliver mail = MockSMTP.deliveries.first @@ -659,7 +659,7 @@ class ActionMailerTest < Test::Unit::TestCase def test_performs_delivery_via_sendmail IO.expects(:popen).once.with('/usr/sbin/sendmail -i -t -f "system@loudthinking.com" test@localhost', 'w+') - ActionMailer::Base.delivery_method = :sendmail + TestMailer.delivery_method = :sendmail TestMailer.signed_up(@recipient).deliver end @@ -956,7 +956,7 @@ EOF end def test_headers_removed_on_smtp_delivery - ActionMailer::Base.delivery_method = :smtp + TestMailer.delivery_method = :smtp TestMailer.cc_bcc(@recipient).deliver assert MockSMTP.deliveries[0][2].include?("root@loudthinking.com") assert MockSMTP.deliveries[0][2].include?("nobody@loudthinking.com") @@ -1053,35 +1053,35 @@ EOF end def test_return_path_with_deliver - ActionMailer::Base.delivery_method = :smtp + TestMailer.delivery_method = :smtp TestMailer.return_path.deliver assert_match %r{^Return-Path: }, MockSMTP.deliveries[0][0] assert_equal "another@somewhere.test", MockSMTP.deliveries[0][1].to_s end def test_starttls_is_enabled_if_supported - ActionMailer::Base.delivery_settings[:smtp].merge!(:enable_starttls_auto => true) + TestMailer.smtp_settings.merge!(:enable_starttls_auto => true) MockSMTP.any_instance.expects(:respond_to?).with(:enable_starttls_auto).returns(true) MockSMTP.any_instance.expects(:enable_starttls_auto) - ActionMailer::Base.delivery_method = :smtp + TestMailer.delivery_method = :smtp TestMailer.signed_up(@recipient).deliver end def test_starttls_is_disabled_if_not_supported - ActionMailer::Base.delivery_settings[:smtp].merge!(:enable_starttls_auto => true) + TestMailer.smtp_settings.merge!(:enable_starttls_auto => true) MockSMTP.any_instance.expects(:respond_to?).with(:enable_starttls_auto).returns(false) MockSMTP.any_instance.expects(:enable_starttls_auto).never - ActionMailer::Base.delivery_method = :smtp + TestMailer.delivery_method = :smtp TestMailer.signed_up(@recipient).deliver end def test_starttls_is_not_enabled - TestMailer.delivery_settings[:smtp].merge!(:enable_starttls_auto => false) + TestMailer.smtp_settings.merge!(:enable_starttls_auto => false) MockSMTP.any_instance.expects(:respond_to?).never TestMailer.delivery_method = :smtp TestMailer.signed_up(@recipient).deliver ensure - TestMailer.delivery_settings[:smtp].merge!(:enable_starttls_auto => true) + TestMailer.smtp_settings.merge!(:enable_starttls_auto => true) end end From 99f960a3d73b62a957988bbee0906264f35afc2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sun, 24 Jan 2010 18:40:04 +0100 Subject: [PATCH 43/63] Handle some TODOs and deprecations. --- .../lib/action_mailer/adv_attr_accessor.rb | 2 - actionmailer/lib/action_mailer/base.rb | 39 ++++++++++--------- .../lib/action_mailer/deprecated_api.rb | 21 ++++++---- actionmailer/lib/action_mailer/old_api.rb | 16 ++++---- actionmailer/lib/action_mailer/test_helper.rb | 1 - actionmailer/test/mail_service_test.rb | 6 ++- 6 files changed, 45 insertions(+), 40 deletions(-) diff --git a/actionmailer/lib/action_mailer/adv_attr_accessor.rb b/actionmailer/lib/action_mailer/adv_attr_accessor.rb index 91992ed8396..be6b1feca98 100644 --- a/actionmailer/lib/action_mailer/adv_attr_accessor.rb +++ b/actionmailer/lib/action_mailer/adv_attr_accessor.rb @@ -1,8 +1,6 @@ module ActionMailer module AdvAttrAccessor #:nodoc: def adv_attr_accessor(*names) - - # TODO: ActiveSupport::Deprecation.warn() names.each do |name| ivar = "@#{name}" diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index ad3a9c3c6d4..7e984124b7f 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -302,7 +302,6 @@ module ActionMailer #:nodoc: @mailer_name ||= name.underscore end attr_writer :mailer_name - alias :controller_path :mailer_name # Receives a raw email, parses it into an email object, decodes it, @@ -324,23 +323,21 @@ module ActionMailer #:nodoc: end end - def template_root - self.view_paths && self.view_paths.first - end - - # Should template root overwrite the whole view_paths? - def template_root=(root) - self.view_paths = ActionView::Base.process_view_paths(root) - end - # TODO The delivery should happen inside the instrument block def delivered_email(mail) - ActiveSupport::Notifications.instrument("action_mailer.deliver", :mailer => self.name) do |payload| + ActiveSupport::Notifications.instrument("action_mailer.deliver") do |payload| self.set_payload_for_mail(payload, mail) end end + def respond_to?(method, *args) #:nodoc: + super || action_methods.include?(method.to_s) + end + + protected + def set_payload_for_mail(payload, mail) #:nodoc: + payload[:mailer] = self.name payload[:message_id] = mail.message_id payload[:subject] = mail.subject payload[:to] = mail.to @@ -351,13 +348,7 @@ module ActionMailer #:nodoc: payload[:mail] = mail.encoded end - def respond_to?(method, *args) - super || action_methods.include?(method.to_s) - end - - protected - - def method_missing(method, *args) + def method_missing(method, *args) #:nodoc: if action_methods.include?(method.to_s) new(method, *args).message else @@ -459,7 +450,7 @@ module ActionMailer #:nodoc: :content_type => self.class.default_content_type.dup } else - self.class.template_root.find_all(action_name, {}, self.class.mailer_name).each do |template| + each_template do |template| responses << { :body => render_to_body(:_template => template), :content_type => template.mime_type.to_s @@ -470,6 +461,16 @@ module ActionMailer #:nodoc: [responses, sort_order] end + def each_template(&block) #:nodoc: + self.class.view_paths.each do |load_paths| + templates = load_paths.find_all(action_name, {}, self.class.mailer_name) + unless templates.empty? + templates.each(&block) + return + end + end + end + def create_parts_from_responses(m, responses, charset) #:nodoc: if responses.size == 1 && !m.has_attachments? m.body = responses[0][:body] diff --git a/actionmailer/lib/action_mailer/deprecated_api.rb b/actionmailer/lib/action_mailer/deprecated_api.rb index 530a9c19224..0eb8d85676a 100644 --- a/actionmailer/lib/action_mailer/deprecated_api.rb +++ b/actionmailer/lib/action_mailer/deprecated_api.rb @@ -25,11 +25,20 @@ module ActionMailer mail end - def respond_to?(method_symbol, include_private = false) #:nodoc: + def template_root + self.view_paths && self.view_paths.first + end + + def template_root=(root) + ActiveSupport::Deprecation.warn "template_root= is deprecated, use view_paths.unshift instead", caller[0,2] + self.view_paths = ActionView::Base.process_view_paths(root) + end + + def respond_to?(method_symbol, include_private = false) matches_dynamic_method?(method_symbol) || super end - def method_missing(method_symbol, *parameters) #:nodoc: + def method_missing(method_symbol, *parameters) if match = matches_dynamic_method?(method_symbol) case match[1] when 'create' @@ -49,7 +58,7 @@ module ActionMailer private - def matches_dynamic_method?(method_name) #:nodoc: + def matches_dynamic_method?(method_name) method_name = method_name.to_s /^(create|deliver)_([_a-z]\w*)/.match(method_name) || /^(new)$/.match(method_name) end @@ -70,10 +79,8 @@ module ActionMailer if options[:body] ActiveSupport::Deprecation.warn(':body in render deprecated. Please use instance ' << 'variables as assigns instead', caller[0,1]) - body options.delete(:body) end - super end @@ -93,13 +100,11 @@ module ActionMailer private - - def create_parts #:nodoc: + def create_parts if @body.is_a?(Hash) && !@body.empty? ActiveSupport::Deprecation.warn "Giving a hash to body is deprecated, please use instance variables instead", caller[0,2] @body.each { |k, v| instance_variable_set(:"@#{k}", v) } end - super end diff --git a/actionmailer/lib/action_mailer/old_api.rb b/actionmailer/lib/action_mailer/old_api.rb index 204dc473e83..f5b077ab987 100644 --- a/actionmailer/lib/action_mailer/old_api.rb +++ b/actionmailer/lib/action_mailer/old_api.rb @@ -1,5 +1,5 @@ module ActionMailer - module OldApi + module OldApi #:nodoc: extend ActiveSupport::Concern included do @@ -144,7 +144,7 @@ module ActionMailer :content_disposition => content_disposition }.merge(params) end - def create_mail #:nodoc: + def create_mail m = @_message quote_fields!({:subject => subject, :to => recipients, :from => from, @@ -184,7 +184,7 @@ module ActionMailer # Set up the default values for the various instance variables of this # mailer. Subclasses may override this method to provide different # defaults. - def initialize_defaults(method_name) #:nodoc: + def initialize_defaults(method_name) @charset ||= self.class.default_charset.dup @content_type ||= self.class.default_content_type.dup @implicit_parts_order ||= self.class.default_implicit_parts_order.dup @@ -200,7 +200,7 @@ module ActionMailer @body ||= {} end - def create_parts #:nodoc: + def create_parts if String === @body self.response_body = @body end @@ -208,7 +208,7 @@ module ActionMailer if String === response_body @parts.unshift create_inline_part(response_body) else - self.class.template_root.find_all(@template, {}, @mailer_name).each do |template| + self.class.view_paths.first.find_all(@template, {}, @mailer_name).each do |template| @parts << create_inline_part(render_to_body(:_template => template), template.mime_type) end @@ -222,7 +222,7 @@ module ActionMailer end end - def create_inline_part(body, mime_type=nil) #:nodoc: + def create_inline_part(body, mime_type=nil) ct = mime_type || "text/plain" main_type, sub_type = split_content_type(ct.to_s) @@ -233,11 +233,11 @@ module ActionMailer ) end - def split_content_type(ct) #:nodoc: + def split_content_type(ct) ct.to_s.split("/") end - def parse_content_type(defaults=nil) #:nodoc: + def parse_content_type(defaults=nil) if @content_type.blank? [ nil, {} ] else diff --git a/actionmailer/lib/action_mailer/test_helper.rb b/actionmailer/lib/action_mailer/test_helper.rb index f234c0248c0..3a1612442f5 100644 --- a/actionmailer/lib/action_mailer/test_helper.rb +++ b/actionmailer/lib/action_mailer/test_helper.rb @@ -58,7 +58,6 @@ module ActionMailer end end -# TODO: Deprecate this module Test module Unit class TestCase diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index c0a3f655b9d..aa31c9a8035 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -1085,13 +1085,15 @@ EOF end end -class InheritableTemplateRootTest < Test::Unit::TestCase +class InheritableTemplateRootTest < ActiveSupport::TestCase def test_attr expected = File.expand_path("#{File.dirname(__FILE__)}/fixtures/path.with.dots") assert_equal expected, FunkyPathMailer.template_root.to_s sub = Class.new(FunkyPathMailer) - sub.template_root = 'test/path' + assert_deprecated do + sub.template_root = 'test/path' + end assert_equal File.expand_path('test/path'), sub.template_root.to_s assert_equal expected, FunkyPathMailer.template_root.to_s From bd96614101262e0ad0cc176ed8e2d95a5c17936b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sun, 24 Jan 2010 19:36:42 +0100 Subject: [PATCH 44/63] Move old tests to a specific folder and add some delivery method tests. --- actionmailer/Rakefile | 4 +- actionmailer/lib/action_mailer/base.rb | 10 +- .../lib/action_mailer/delivery_methods.rb | 16 ++- actionmailer/lib/action_mailer/mail_helper.rb | 2 +- actionmailer/test/abstract_unit.rb | 24 +--- actionmailer/test/base_test.rb | 8 -- actionmailer/test/delivery_methods_test.rb | 112 +++++++++++++++--- actionmailer/test/mail_test.rb | 2 - .../test/{ => old_base}/adv_attr_test.rb | 0 .../test/{ => old_base}/asset_host_test.rb | 0 .../test/{ => old_base}/mail_helper_test.rb | 0 .../test/{ => old_base}/mail_layout_test.rb | 0 .../test/{ => old_base}/mail_render_test.rb | 0 .../test/{ => old_base}/mail_service_test.rb | 26 ++-- .../test/{ => old_base}/tmail_compat_test.rb | 0 actionmailer/test/{ => old_base}/url_test.rb | 0 16 files changed, 131 insertions(+), 73 deletions(-) rename actionmailer/test/{ => old_base}/adv_attr_test.rb (100%) rename actionmailer/test/{ => old_base}/asset_host_test.rb (100%) rename actionmailer/test/{ => old_base}/mail_helper_test.rb (100%) rename actionmailer/test/{ => old_base}/mail_layout_test.rb (100%) rename actionmailer/test/{ => old_base}/mail_render_test.rb (100%) rename actionmailer/test/{ => old_base}/mail_service_test.rb (97%) rename actionmailer/test/{ => old_base}/tmail_compat_test.rb (100%) rename actionmailer/test/{ => old_base}/url_test.rb (100%) diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile index 6c193715144..2619d9359ee 100644 --- a/actionmailer/Rakefile +++ b/actionmailer/Rakefile @@ -22,14 +22,14 @@ task :default => [ :test ] # Run the unit tests Rake::TestTask.new { |t| t.libs << "test" - t.pattern = 'test/*_test.rb' + t.pattern = 'test/**/*_test.rb' t.warning = true } namespace :test do task :isolated do ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')) - Dir.glob("test/*_test.rb").all? do |file| + Dir.glob("test/**/*_test.rb").all? do |file| system(ruby, '-Ilib:test', file) end or raise "Failures" end diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 7e984124b7f..1b81cbf5af6 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -256,6 +256,7 @@ module ActionMailer #:nodoc: include DeliveryMethods, Quoting abstract! + # TODO Add some sanity tests for the included modules include AbstractController::Logger include AbstractController::Rendering include AbstractController::LocalizedCache @@ -270,12 +271,6 @@ module ActionMailer #:nodoc: private_class_method :new #:nodoc: - cattr_accessor :raise_delivery_errors - @@raise_delivery_errors = true - - cattr_accessor :perform_deliveries - @@perform_deliveries = true - extlib_inheritable_accessor :default_charset self.default_charset = "utf-8" @@ -295,9 +290,6 @@ module ActionMailer #:nodoc: self.default_implicit_parts_order = [ "text/plain", "text/enriched", "text/html" ] class << self - # Provides a list of emails that have been delivered by Mail - delegate :deliveries, :deliveries=, :to => Mail - def mailer_name @mailer_name ||= name.underscore end diff --git a/actionmailer/lib/action_mailer/delivery_methods.rb b/actionmailer/lib/action_mailer/delivery_methods.rb index 38325e512fc..21909d5d57d 100644 --- a/actionmailer/lib/action_mailer/delivery_methods.rb +++ b/actionmailer/lib/action_mailer/delivery_methods.rb @@ -1,7 +1,8 @@ require 'tmpdir' module ActionMailer - # Provides a DSL for adding delivery methods to ActionMailer. + # This modules handles everything related to the delivery, from registering new + # delivery methods to configuring the mail object to be send. module DeliveryMethods extend ActiveSupport::Concern @@ -9,6 +10,13 @@ module ActionMailer extlib_inheritable_accessor :delivery_methods, :delivery_method, :instance_writer => false + # Do not make this inheritable, because we always want it to propagate + cattr_accessor :raise_delivery_errors + self.raise_delivery_errors = true + + cattr_accessor :perform_deliveries + self.perform_deliveries = true + self.delivery_methods = {} self.delivery_method = :smtp @@ -32,6 +40,9 @@ module ActionMailer end module ClassMethods + # Provides a list of emails that have been delivered by Mail + delegate :deliveries, :deliveries=, :to => Mail + # Adds a new delivery method through the given class using the given symbol # as alias and the default options supplied: # @@ -50,7 +61,8 @@ module ActionMailer self.delivery_methods[symbol.to_sym] = klass end - def wrap_delivery_behavior(mail, method=delivery_method) #:nodoc: + def wrap_delivery_behavior(mail, method=nil) #:nodoc: + method ||= self.delivery_method mail.register_for_delivery_notification(self) if method.is_a?(Symbol) diff --git a/actionmailer/lib/action_mailer/mail_helper.rb b/actionmailer/lib/action_mailer/mail_helper.rb index adba94cbef0..01da954b030 100644 --- a/actionmailer/lib/action_mailer/mail_helper.rb +++ b/actionmailer/lib/action_mailer/mail_helper.rb @@ -23,7 +23,7 @@ module ActionMailer # Access the message instance. def message #:nodoc: - @message + @_message end end end diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb index 1fc5ab85e01..781e49ae053 100644 --- a/actionmailer/test/abstract_unit.rb +++ b/actionmailer/test/abstract_unit.rb @@ -8,7 +8,6 @@ $:.unshift(lib) unless $:.include?('lib') || $:.include?(lib) require 'rubygems' require 'test/unit' - require 'action_mailer' # Show backtraces for deprecated behavior for quicker cleanup. @@ -18,15 +17,11 @@ ActiveSupport::Deprecation.debug = true ActionView::Template.register_template_handler :haml, lambda { |template| "Look its HAML!".inspect } ActionView::Template.register_template_handler :bak, lambda { |template| "Lame backup".inspect } -ActionView::Base::DEFAULT_CONFIG = { :assets_dir => '/nowhere' } +FIXTURE_LOAD_PATH = File.expand_path('fixtures', File.dirname(__FILE__)) +ActionMailer::Base.view_paths = FIXTURE_LOAD_PATH +$:.unshift File.join(FIXTURE_LOAD_PATH, 'helpers') -$:.unshift "#{File.dirname(__FILE__)}/fixtures/helpers" - -FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures') -ActionMailer::Base.template_root = FIXTURE_LOAD_PATH - -class MockSMTP - +class MockSMTP def self.deliveries @@deliveries end @@ -42,7 +37,6 @@ class MockSMTP def start(*args) yield self end - end class Net::SMTP @@ -51,14 +45,6 @@ class Net::SMTP end end -def uses_gem(gem_name, test_name, version = '> 0') - gem gem_name.to_s, version - require gem_name.to_s - yield -rescue LoadError - $stderr.puts "Skipping #{test_name} tests. `gem install #{gem_name}` and try again." -end - def set_delivery_method(method) @old_delivery_method = ActionMailer::Base.delivery_method ActionMailer::Base.delivery_method = method @@ -66,4 +52,4 @@ end def restore_delivery_method ActionMailer::Base.delivery_method = @old_delivery_method -end +end \ No newline at end of file diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 3b2a072dce6..dd17bf868b5 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -310,14 +310,6 @@ class BaseTest < ActiveSupport::TestCase assert_equal(1, BaseMailer.deliveries.length) end - # Delivery hooks - test "ActionMailer should be told when Mail gets delivered" do - BaseMailer.deliveries.clear - BaseMailer.expects(:delivered_email).once - BaseMailer.welcome.deliver - assert_equal(1, BaseMailer.deliveries.length) - end - protected # Execute the block setting the given values and restoring old values after diff --git a/actionmailer/test/delivery_methods_test.rb b/actionmailer/test/delivery_methods_test.rb index de3d54197d3..5b2ce61ca94 100644 --- a/actionmailer/test/delivery_methods_test.rb +++ b/actionmailer/test/delivery_methods_test.rb @@ -4,20 +4,17 @@ require 'mail' class MyCustomDelivery end -class DefaultsDeliveryMethodsTest < ActionMailer::TestCase - def setup - set_delivery_method :smtp +class BogusDelivery + def initialize(*) end - def teardown - restore_delivery_method + def deliver!(mail) + raise "failed" end +end - def test_should_be_the_default_smtp - assert_equal :smtp, ActionMailer::Base.delivery_method - end - - def test_should_have_default_smtp_delivery_method_settings +class DefaultsDeliveryMethodsTest < ActiveSupport::TestCase + test "default smtp settings" do settings = { :address => "localhost", :port => 25, :domain => 'localhost.localdomain', @@ -28,45 +25,126 @@ class DefaultsDeliveryMethodsTest < ActionMailer::TestCase assert_equal settings, ActionMailer::Base.smtp_settings end - def test_should_have_default_file_delivery_method_settings + test "default file delivery settings" do settings = {:location => "#{Dir.tmpdir}/mails"} assert_equal settings, ActionMailer::Base.file_settings end - def test_should_have_default_sendmail_delivery_method_settings + test "default sendmail settings" do settings = {:location => '/usr/sbin/sendmail', :arguments => '-i -t'} assert_equal settings, ActionMailer::Base.sendmail_settings end end -class CustomDeliveryMethodsTest < ActionMailer::TestCase +class CustomDeliveryMethodsTest < ActiveSupport::TestCase def setup + @old_delivery_method = ActionMailer::Base.delivery_method ActionMailer::Base.add_delivery_method :custom, MyCustomDelivery end def teardown + ActionMailer::Base.delivery_method = @old_delivery_method ActionMailer::Base.delivery_methods.delete(:custom) end - def test_allow_to_add_a_custom_delivery_method + test "allow to add custom delivery method" do ActionMailer::Base.delivery_method = :custom assert_equal :custom, ActionMailer::Base.delivery_method end - def test_allow_to_customize_custom_settings + test "allow to customize custom settings" do ActionMailer::Base.custom_settings = { :foo => :bar } assert_equal Hash[:foo => :bar], ActionMailer::Base.custom_settings end - def test_respond_to_custom_method_settings + test "respond to custom settings" do assert_respond_to ActionMailer::Base, :custom_settings assert_respond_to ActionMailer::Base, :custom_settings= end - def test_should_not_respond_for_invalid_method_settings + test "does not respond to unknown settings" do assert_raise NoMethodError do ActionMailer::Base.another_settings end end end + +class MailDeliveryTest < ActiveSupport::TestCase + class DeliverMail < ActionMailer::Base + DEFAULT_HEADERS = { + :to => 'mikel@test.lindsaar.net', + :from => 'jose@test.plataformatec.com' + } + + def welcome(hash={}) + mail(DEFAULT_HEADERS.merge(hash)) + end + end + + def setup + ActionMailer::Base.delivery_method = :smtp + end + + def teardown + DeliverMail.delivery_method = :smtp + DeliverMail.perform_deliveries = true + DeliverMail.raise_delivery_errors = true + end + + test "ActionMailer should be told when Mail gets delivered" do + DeliverMail.deliveries.clear + DeliverMail.expects(:delivered_email).once + DeliverMail.welcome.deliver + assert_equal(1, DeliverMail.deliveries.length) + end + + test "delivery method can be customized per instance" do + email = DeliverMail.welcome.deliver + assert_instance_of Mail::SMTP, email.delivery_method + email = DeliverMail.welcome(:delivery_method => :test).deliver + assert_instance_of Mail::TestMailer, email.delivery_method + end + + test "delivery method can be customized in subclasses not changing the parent" do + DeliverMail.delivery_method = :test + assert_equal :smtp, ActionMailer::Base.delivery_method + $BREAK = true + email = DeliverMail.welcome.deliver + assert_instance_of Mail::TestMailer, email.delivery_method + end + + test "non registered delivery methods raises errors" do + DeliverMail.delivery_method = :unknown + assert_raise RuntimeError do + DeliverMail.welcome.deliver + end + end + + test "does not perform deliveries if requested" do + DeliverMail.perform_deliveries = false + DeliverMail.deliveries.clear + DeliverMail.expects(:delivered_email).never + DeliverMail.welcome.deliver + assert_equal(0, DeliverMail.deliveries.length) + end + + test "raise errors on bogus deliveries" do + DeliverMail.delivery_method = BogusDelivery + DeliverMail.deliveries.clear + DeliverMail.expects(:delivered_email).never + assert_raise RuntimeError do + DeliverMail.welcome.deliver + end + assert_equal(0, DeliverMail.deliveries.length) + end + + test "does not raise errors on bogus deliveries if set" do + DeliverMail.delivery_method = BogusDelivery + DeliverMail.raise_delivery_errors = false + DeliverMail.deliveries.clear + DeliverMail.expects(:delivered_email).once + DeliverMail.welcome.deliver + assert_equal(1, DeliverMail.deliveries.length) + end +end \ No newline at end of file diff --git a/actionmailer/test/mail_test.rb b/actionmailer/test/mail_test.rb index ea6f25d1579..f18dfdc1566 100644 --- a/actionmailer/test/mail_test.rb +++ b/actionmailer/test/mail_test.rb @@ -8,7 +8,6 @@ class MailTest < Test::Unit::TestCase quoted_body = [expected].pack('*M') m.body = quoted_body assert_equal "something_with_underscores=\r\n", m.body.encoded - # CHANGED: body returns object, not string, Changed m.body to m.body.to_s assert_equal expected, m.body.to_s end @@ -20,5 +19,4 @@ class MailTest < Test::Unit::TestCase assert_equal 1902, mail.attachments.first.decoded.length assert_equal "application/pkcs7-signature", mail.attachments.last.mime_type end - end diff --git a/actionmailer/test/adv_attr_test.rb b/actionmailer/test/old_base/adv_attr_test.rb similarity index 100% rename from actionmailer/test/adv_attr_test.rb rename to actionmailer/test/old_base/adv_attr_test.rb diff --git a/actionmailer/test/asset_host_test.rb b/actionmailer/test/old_base/asset_host_test.rb similarity index 100% rename from actionmailer/test/asset_host_test.rb rename to actionmailer/test/old_base/asset_host_test.rb diff --git a/actionmailer/test/mail_helper_test.rb b/actionmailer/test/old_base/mail_helper_test.rb similarity index 100% rename from actionmailer/test/mail_helper_test.rb rename to actionmailer/test/old_base/mail_helper_test.rb diff --git a/actionmailer/test/mail_layout_test.rb b/actionmailer/test/old_base/mail_layout_test.rb similarity index 100% rename from actionmailer/test/mail_layout_test.rb rename to actionmailer/test/old_base/mail_layout_test.rb diff --git a/actionmailer/test/mail_render_test.rb b/actionmailer/test/old_base/mail_render_test.rb similarity index 100% rename from actionmailer/test/mail_render_test.rb rename to actionmailer/test/old_base/mail_render_test.rb diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/old_base/mail_service_test.rb similarity index 97% rename from actionmailer/test/mail_service_test.rb rename to actionmailer/test/old_base/mail_service_test.rb index aa31c9a8035..20b0f30b84c 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/old_base/mail_service_test.rb @@ -2,7 +2,7 @@ require 'abstract_unit' class FunkyPathMailer < ActionMailer::Base - self.template_root = "#{File.dirname(__FILE__)}/fixtures/path.with.dots" + self.view_paths = "#{File.dirname(__FILE__)}/../fixtures/path.with.dots" def multipart_with_template_path_with_dots(recipient) recipients recipient @@ -799,13 +799,13 @@ EOF end def test_receive_decodes_base64_encoded_mail - fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email") + fixture = File.read(File.dirname(__FILE__) + "/../fixtures/raw_email") TestMailer.receive(fixture) assert_match(/Jamis/, TestMailer.received_body.to_s) end def test_receive_attachments - fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email2") + fixture = File.read(File.dirname(__FILE__) + "/../fixtures/raw_email2") mail = Mail.new(fixture) attachment = mail.attachments.last assert_equal "smime.p7s", attachment.filename @@ -813,21 +813,21 @@ EOF end def test_decode_attachment_without_charset - fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email3") + fixture = File.read(File.dirname(__FILE__) + "/../fixtures/raw_email3") mail = Mail.new(fixture) attachment = mail.attachments.last assert_equal 1026, attachment.read.length end def test_attachment_using_content_location - fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email12") + fixture = File.read(File.dirname(__FILE__) + "/../fixtures/raw_email12") mail = Mail.new(fixture) assert_equal 1, mail.attachments.length assert_equal "Photo25.jpg", mail.attachments.first.filename end def test_attachment_with_text_type - fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email13") + fixture = File.read(File.dirname(__FILE__) + "/../fixtures/raw_email13") mail = Mail.new(fixture) assert mail.has_attachments? assert_equal 1, mail.attachments.length @@ -835,19 +835,19 @@ EOF end def test_decode_part_without_content_type - fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email4") + fixture = File.read(File.dirname(__FILE__) + "/../fixtures/raw_email4") mail = Mail.new(fixture) assert_nothing_raised { mail.body } end def test_decode_message_without_content_type - fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email5") + fixture = File.read(File.dirname(__FILE__) + "/../fixtures/raw_email5") mail = Mail.new(fixture) assert_nothing_raised { mail.body } end def test_decode_message_with_incorrect_charset - fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email6") + fixture = File.read(File.dirname(__FILE__) + "/../fixtures/raw_email6") mail = Mail.new(fixture) assert_nothing_raised { mail.body } end @@ -979,7 +979,7 @@ EOF end def test_recursive_multipart_processing - fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email7") + fixture = File.read(File.dirname(__FILE__) + "/../fixtures/raw_email7") mail = Mail.new(fixture) assert_equal(2, mail.parts.length) assert_equal(4, mail.parts.first.parts.length) @@ -990,7 +990,7 @@ EOF end def test_decode_encoded_attachment_filename - fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email8") + fixture = File.read(File.dirname(__FILE__) + "/../fixtures/raw_email8") mail = Mail.new(fixture) attachment = mail.attachments.last @@ -1007,7 +1007,7 @@ EOF end def test_decode_message_with_unknown_charset - fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email10") + fixture = File.read(File.dirname(__FILE__) + "/../fixtures/raw_email10") mail = Mail.new(fixture) assert_nothing_raised { mail.body } end @@ -1087,7 +1087,7 @@ end class InheritableTemplateRootTest < ActiveSupport::TestCase def test_attr - expected = File.expand_path("#{File.dirname(__FILE__)}/fixtures/path.with.dots") + expected = File.expand_path("#{File.dirname(__FILE__)}/../fixtures/path.with.dots") assert_equal expected, FunkyPathMailer.template_root.to_s sub = Class.new(FunkyPathMailer) diff --git a/actionmailer/test/tmail_compat_test.rb b/actionmailer/test/old_base/tmail_compat_test.rb similarity index 100% rename from actionmailer/test/tmail_compat_test.rb rename to actionmailer/test/old_base/tmail_compat_test.rb diff --git a/actionmailer/test/url_test.rb b/actionmailer/test/old_base/url_test.rb similarity index 100% rename from actionmailer/test/url_test.rb rename to actionmailer/test/old_base/url_test.rb From a74a655648618a6ed568b9b4ef3a17a8970e7774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sun, 24 Jan 2010 19:52:44 +0100 Subject: [PATCH 45/63] Add tests to mail helper. --- actionmailer/lib/action_mailer/base.rb | 1 - actionmailer/lib/action_mailer/mail_helper.rb | 4 +- actionmailer/test/abstract_unit.rb | 1 - actionmailer/test/delivery_methods_test.rb | 60 ++++++------ .../helper_mailer/use_example_helper.erb | 1 - .../fixtures/helper_mailer/use_helper.erb | 1 - .../helper_mailer/use_helper_method.erb | 1 - .../helper_mailer/use_mail_helper.erb | 5 - .../test/fixtures/helpers/example_helper.rb | 5 - actionmailer/test/mail_helper_test.rb | 54 +++++++++++ .../test/old_base/mail_helper_test.rb | 94 ------------------- 11 files changed, 86 insertions(+), 141 deletions(-) delete mode 100644 actionmailer/test/fixtures/helper_mailer/use_example_helper.erb delete mode 100644 actionmailer/test/fixtures/helper_mailer/use_helper.erb delete mode 100644 actionmailer/test/fixtures/helper_mailer/use_helper_method.erb delete mode 100644 actionmailer/test/fixtures/helper_mailer/use_mail_helper.erb delete mode 100644 actionmailer/test/fixtures/helpers/example_helper.rb create mode 100644 actionmailer/test/mail_helper_test.rb delete mode 100644 actionmailer/test/old_base/mail_helper_test.rb diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 1b81cbf5af6..7101bfbb70c 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -256,7 +256,6 @@ module ActionMailer #:nodoc: include DeliveryMethods, Quoting abstract! - # TODO Add some sanity tests for the included modules include AbstractController::Logger include AbstractController::Rendering include AbstractController::LocalizedCache diff --git a/actionmailer/lib/action_mailer/mail_helper.rb b/actionmailer/lib/action_mailer/mail_helper.rb index 01da954b030..ab5c3469b23 100644 --- a/actionmailer/lib/action_mailer/mail_helper.rb +++ b/actionmailer/lib/action_mailer/mail_helper.rb @@ -17,12 +17,12 @@ module ActionMailer end # Access the mailer instance. - def mailer #:nodoc: + def mailer @_controller end # Access the message instance. - def message #:nodoc: + def message @_message end end diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb index 781e49ae053..ce09bb5d612 100644 --- a/actionmailer/test/abstract_unit.rb +++ b/actionmailer/test/abstract_unit.rb @@ -19,7 +19,6 @@ ActionView::Template.register_template_handler :bak, lambda { |template| "Lame b FIXTURE_LOAD_PATH = File.expand_path('fixtures', File.dirname(__FILE__)) ActionMailer::Base.view_paths = FIXTURE_LOAD_PATH -$:.unshift File.join(FIXTURE_LOAD_PATH, 'helpers') class MockSMTP def self.deliveries diff --git a/actionmailer/test/delivery_methods_test.rb b/actionmailer/test/delivery_methods_test.rb index 5b2ce61ca94..145f8ba30d7 100644 --- a/actionmailer/test/delivery_methods_test.rb +++ b/actionmailer/test/delivery_methods_test.rb @@ -71,7 +71,7 @@ class CustomDeliveryMethodsTest < ActiveSupport::TestCase end class MailDeliveryTest < ActiveSupport::TestCase - class DeliverMail < ActionMailer::Base + class DeliveryMailer < ActionMailer::Base DEFAULT_HEADERS = { :to => 'mikel@test.lindsaar.net', :from => 'jose@test.plataformatec.com' @@ -87,64 +87,64 @@ class MailDeliveryTest < ActiveSupport::TestCase end def teardown - DeliverMail.delivery_method = :smtp - DeliverMail.perform_deliveries = true - DeliverMail.raise_delivery_errors = true + DeliveryMailer.delivery_method = :smtp + DeliveryMailer.perform_deliveries = true + DeliveryMailer.raise_delivery_errors = true end test "ActionMailer should be told when Mail gets delivered" do - DeliverMail.deliveries.clear - DeliverMail.expects(:delivered_email).once - DeliverMail.welcome.deliver - assert_equal(1, DeliverMail.deliveries.length) + DeliveryMailer.deliveries.clear + DeliveryMailer.expects(:delivered_email).once + DeliveryMailer.welcome.deliver + assert_equal(1, DeliveryMailer.deliveries.length) end test "delivery method can be customized per instance" do - email = DeliverMail.welcome.deliver + email = DeliveryMailer.welcome.deliver assert_instance_of Mail::SMTP, email.delivery_method - email = DeliverMail.welcome(:delivery_method => :test).deliver + email = DeliveryMailer.welcome(:delivery_method => :test).deliver assert_instance_of Mail::TestMailer, email.delivery_method end test "delivery method can be customized in subclasses not changing the parent" do - DeliverMail.delivery_method = :test + DeliveryMailer.delivery_method = :test assert_equal :smtp, ActionMailer::Base.delivery_method $BREAK = true - email = DeliverMail.welcome.deliver + email = DeliveryMailer.welcome.deliver assert_instance_of Mail::TestMailer, email.delivery_method end test "non registered delivery methods raises errors" do - DeliverMail.delivery_method = :unknown + DeliveryMailer.delivery_method = :unknown assert_raise RuntimeError do - DeliverMail.welcome.deliver + DeliveryMailer.welcome.deliver end end test "does not perform deliveries if requested" do - DeliverMail.perform_deliveries = false - DeliverMail.deliveries.clear - DeliverMail.expects(:delivered_email).never - DeliverMail.welcome.deliver - assert_equal(0, DeliverMail.deliveries.length) + DeliveryMailer.perform_deliveries = false + DeliveryMailer.deliveries.clear + DeliveryMailer.expects(:delivered_email).never + DeliveryMailer.welcome.deliver + assert_equal(0, DeliveryMailer.deliveries.length) end test "raise errors on bogus deliveries" do - DeliverMail.delivery_method = BogusDelivery - DeliverMail.deliveries.clear - DeliverMail.expects(:delivered_email).never + DeliveryMailer.delivery_method = BogusDelivery + DeliveryMailer.deliveries.clear + DeliveryMailer.expects(:delivered_email).never assert_raise RuntimeError do - DeliverMail.welcome.deliver + DeliveryMailer.welcome.deliver end - assert_equal(0, DeliverMail.deliveries.length) + assert_equal(0, DeliveryMailer.deliveries.length) end test "does not raise errors on bogus deliveries if set" do - DeliverMail.delivery_method = BogusDelivery - DeliverMail.raise_delivery_errors = false - DeliverMail.deliveries.clear - DeliverMail.expects(:delivered_email).once - DeliverMail.welcome.deliver - assert_equal(1, DeliverMail.deliveries.length) + DeliveryMailer.delivery_method = BogusDelivery + DeliveryMailer.raise_delivery_errors = false + DeliveryMailer.deliveries.clear + DeliveryMailer.expects(:delivered_email).once + DeliveryMailer.welcome.deliver + assert_equal(1, DeliveryMailer.deliveries.length) end end \ No newline at end of file diff --git a/actionmailer/test/fixtures/helper_mailer/use_example_helper.erb b/actionmailer/test/fixtures/helper_mailer/use_example_helper.erb deleted file mode 100644 index fcff3bb1b43..00000000000 --- a/actionmailer/test/fixtures/helper_mailer/use_example_helper.erb +++ /dev/null @@ -1 +0,0 @@ -So, <%= example_format(@text) %> diff --git a/actionmailer/test/fixtures/helper_mailer/use_helper.erb b/actionmailer/test/fixtures/helper_mailer/use_helper.erb deleted file mode 100644 index 378777f8bb5..00000000000 --- a/actionmailer/test/fixtures/helper_mailer/use_helper.erb +++ /dev/null @@ -1 +0,0 @@ -Hello, <%= person_name %>. Thanks for registering! diff --git a/actionmailer/test/fixtures/helper_mailer/use_helper_method.erb b/actionmailer/test/fixtures/helper_mailer/use_helper_method.erb deleted file mode 100644 index d5b8b285e71..00000000000 --- a/actionmailer/test/fixtures/helper_mailer/use_helper_method.erb +++ /dev/null @@ -1 +0,0 @@ -This message brought to you by <%= name_of_the_mailer_class %>. diff --git a/actionmailer/test/fixtures/helper_mailer/use_mail_helper.erb b/actionmailer/test/fixtures/helper_mailer/use_mail_helper.erb deleted file mode 100644 index 96ec49d18af..00000000000 --- a/actionmailer/test/fixtures/helper_mailer/use_mail_helper.erb +++ /dev/null @@ -1,5 +0,0 @@ -From "Romeo and Juliet": - -<%= block_format @text %> - -Good ol' Shakespeare. diff --git a/actionmailer/test/fixtures/helpers/example_helper.rb b/actionmailer/test/fixtures/helpers/example_helper.rb deleted file mode 100644 index f6a6a49cedf..00000000000 --- a/actionmailer/test/fixtures/helpers/example_helper.rb +++ /dev/null @@ -1,5 +0,0 @@ -module ExampleHelper - def example_format(text) - "#{h(text)}".html_safe! - end -end diff --git a/actionmailer/test/mail_helper_test.rb b/actionmailer/test/mail_helper_test.rb new file mode 100644 index 00000000000..7cc0804323b --- /dev/null +++ b/actionmailer/test/mail_helper_test.rb @@ -0,0 +1,54 @@ +require 'abstract_unit' + +class HelperMailer < ActionMailer::Base + def use_mail_helper + @text = "But soft! What light through yonder window breaks? It is the east, " + + "and Juliet is the sun. Arise, fair sun, and kill the envious moon, " + + "which is sick and pale with grief that thou, her maid, art far more " + + "fair than she. Be not her maid, for she is envious! Her vestal " + + "livery is but sick and green, and none but fools do wear it. Cast " + + "it off!" + + mail_with_defaults do |format| + format.html { render(:inline => "<%= block_format @text %>") } + end + end + + def use_mailer + mail_with_defaults do |format| + format.html { render(:inline => "<%= mailer.message.subject %>") } + end + end + + def use_message + mail_with_defaults do |format| + format.html { render(:inline => "<%= message.subject %>") } + end + end + + protected + + def mail_with_defaults(&block) + mail(:to => "test@localhost", :from => "tester@example.com", + :subject => "using helpers", &block) + end +end + +class MailerHelperTest < ActionMailer::TestCase + def test_use_mail_helper + mail = HelperMailer.use_mail_helper + assert_match %r{ But soft!}, mail.body.encoded + assert_match %r{east, and\r\n Juliet}, mail.body.encoded + end + + def test_use_mailer + mail = HelperMailer.use_mailer + assert_match "using helpers", mail.body.encoded + end + + def test_use_message + mail = HelperMailer.use_message + assert_match "using helpers", mail.body.encoded + end +end + diff --git a/actionmailer/test/old_base/mail_helper_test.rb b/actionmailer/test/old_base/mail_helper_test.rb deleted file mode 100644 index a9b3cd3ce1d..00000000000 --- a/actionmailer/test/old_base/mail_helper_test.rb +++ /dev/null @@ -1,94 +0,0 @@ -require 'abstract_unit' - -module MailerHelper - def person_name - "Mr. Joe Person" - end -end - -class HelperMailer < ActionMailer::Base - helper MailerHelper - helper :example - - def use_helper - recipients 'test@localhost' - subject "using helpers" - from "tester@example.com" - end - - def use_example_helper - recipients 'test@localhost' - subject "using helpers" - from "tester@example.com" - - @text = "emphasize me!" - end - - def use_mail_helper - recipients 'test@localhost' - subject "using mailing helpers" - from "tester@example.com" - - @text = "But soft! What light through yonder window breaks? It is the east, " + - "and Juliet is the sun. Arise, fair sun, and kill the envious moon, " + - "which is sick and pale with grief that thou, her maid, art far more " + - "fair than she. Be not her maid, for she is envious! Her vestal " + - "livery is but sick and green, and none but fools do wear it. Cast " + - "it off!" - end - - def use_helper_method - recipients 'test@localhost' - subject "using helpers" - from "tester@example.com" - - @text = "emphasize me!" - end - - private - - def name_of_the_mailer_class - self.class.name - end - helper_method :name_of_the_mailer_class -end - -class MailerHelperTest < ActiveSupport::TestCase - def new_mail( charset="utf-8" ) - mail = Mail.new - mail.set_content_type "text", "plain", { "charset" => charset } if charset - mail - end - - def setup - set_delivery_method :test - ActionMailer::Base.perform_deliveries = true - ActionMailer::Base.deliveries.clear - end - - def teardown - restore_delivery_method - end - - def test_use_helper - mail = HelperMailer.use_helper - assert_match %r{Mr. Joe Person}, mail.encoded - end - - def test_use_example_helper - mail = HelperMailer.use_example_helper - assert_match %r{emphasize me!}, mail.encoded - end - - def test_use_helper_method - mail = HelperMailer.use_helper_method - assert_match %r{HelperMailer}, mail.encoded - end - - def test_use_mail_helper - mail = HelperMailer.use_mail_helper - assert_match %r{ But soft!}, mail.encoded - assert_match %r{east, and\r\n Juliet}, mail.encoded - end -end - From 0361414ae328c10de8ed778e826d8244ba0aa63a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sun, 24 Jan 2010 23:28:52 +0100 Subject: [PATCH 46/63] Add uniq_by and uniq_by! to Array. --- .../lib/active_support/core_ext/array.rb | 1 + .../active_support/core_ext/array/uniq_by.rb | 17 ++++++++++++ activesupport/test/core_ext/array_ext_test.rb | 26 ++++++++++++++++--- 3 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 activesupport/lib/active_support/core_ext/array/uniq_by.rb diff --git a/activesupport/lib/active_support/core_ext/array.rb b/activesupport/lib/active_support/core_ext/array.rb index b583c7533e8..4688468a8fa 100644 --- a/activesupport/lib/active_support/core_ext/array.rb +++ b/activesupport/lib/active_support/core_ext/array.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/array/access' +require 'active_support/core_ext/array/uniq_by' require 'active_support/core_ext/array/conversions' require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/array/grouping' diff --git a/activesupport/lib/active_support/core_ext/array/uniq_by.rb b/activesupport/lib/active_support/core_ext/array/uniq_by.rb new file mode 100644 index 00000000000..a09b2302fd1 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/array/uniq_by.rb @@ -0,0 +1,17 @@ +class Array + # Return an unique array based on the criteria given as a proc. + # + # [1, 2, 3, 4].uniq_by { |i| i.odd? } + # #=> [1, 2] + # + def uniq_by + hash, array = {}, [] + each { |i| hash[yield(i)] ||= (array << i) } + array + end + + # Same as uniq_by, but modifies self. + def uniq_by! + replace(uniq_by{ |i| yield(i) }) + end +end diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb index f5f91ddd80d..6f3b28682ae 100644 --- a/activesupport/test/core_ext/array_ext_test.rb +++ b/activesupport/test/core_ext/array_ext_test.rb @@ -51,9 +51,7 @@ class ArrayExtToParamTests < Test::Unit::TestCase end end -class ArrayExtToSentenceTests < Test::Unit::TestCase - include ActiveSupport::Testing::Deprecation - +class ArrayExtToSentenceTests < ActiveSupport::TestCase def test_plain_array_to_sentence assert_equal "", [].to_sentence assert_equal "one", ['one'].to_sentence @@ -320,6 +318,28 @@ class ArrayExtractOptionsTests < Test::Unit::TestCase end end +class ArrayUniqByTests < Test::Unit::TestCase + def test_uniq_by + assert_equal [1,2], [1,2,3,4].uniq_by { |i| i.odd? } + assert_equal [1,2], [1,2,3,4].uniq_by(&:even?) + assert_equal (-5..0).to_a, (-5..5).to_a.uniq_by{ |i| i**2 } + end + + def test_uniq_by! + a = [1,2,3,4] + a.uniq_by! { |i| i.odd? } + assert_equal [1,2], a + + a = [1,2,3,4] + a.uniq_by! { |i| i.even? } + assert_equal [1,2], a + + a = (-5..5).to_a + a.uniq_by! { |i| i**2 } + assert_equal (-5..0).to_a, a + end +end + class ArrayExtRandomTests < Test::Unit::TestCase def test_random_element_from_array assert_nil [].rand From 328b0b1268596347975349c6df5ce713ad377bd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sun, 24 Jan 2010 23:32:09 +0100 Subject: [PATCH 47/63] Remove deprecated behavior since 2.3. --- .../core_ext/array/conversions.rb | 13 ------------- activesupport/test/core_ext/array_ext_test.rb | 18 +----------------- 2 files changed, 1 insertion(+), 30 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index 7fcef383722..814567a5a6b 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -13,19 +13,6 @@ class Array default_two_words_connector = I18n.translate(:'support.array.two_words_connector', :locale => options[:locale]) default_last_word_connector = I18n.translate(:'support.array.last_word_connector', :locale => options[:locale]) - # Try to emulate to_sentences previous to 2.3 - if options.has_key?(:connector) || options.has_key?(:skip_last_comma) - ::ActiveSupport::Deprecation.warn(":connector has been deprecated. Use :words_connector instead", caller) if options.has_key? :connector - ::ActiveSupport::Deprecation.warn(":skip_last_comma has been deprecated. Use :last_word_connector instead", caller) if options.has_key? :skip_last_comma - - skip_last_comma = options.delete :skip_last_comma - if connector = options.delete(:connector) - options[:last_word_connector] ||= skip_last_comma ? connector : ", #{connector}" - else - options[:last_word_connector] ||= skip_last_comma ? default_two_words_connector : default_last_word_connector - end - end - options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale) options.reverse_merge! :words_connector => default_words_connector, :two_words_connector => default_two_words_connector, :last_word_connector => default_last_word_connector diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb index 6f3b28682ae..d4cd5ddbde5 100644 --- a/activesupport/test/core_ext/array_ext_test.rb +++ b/activesupport/test/core_ext/array_ext_test.rb @@ -51,7 +51,7 @@ class ArrayExtToParamTests < Test::Unit::TestCase end end -class ArrayExtToSentenceTests < ActiveSupport::TestCase +class ArrayExtToSentenceTests < Test::Unit::TestCase def test_plain_array_to_sentence assert_equal "", [].to_sentence assert_equal "one", ['one'].to_sentence @@ -60,28 +60,12 @@ class ArrayExtToSentenceTests < ActiveSupport::TestCase end def test_to_sentence_with_words_connector - assert_deprecated(":connector has been deprecated. Use :words_connector instead") do - assert_equal "one, two, three", ['one', 'two', 'three'].to_sentence(:connector => '') - end - - assert_deprecated(":connector has been deprecated. Use :words_connector instead") do - assert_equal "one, two, and three", ['one', 'two', 'three'].to_sentence(:connector => 'and ') - end - assert_equal "one two, and three", ['one', 'two', 'three'].to_sentence(:words_connector => ' ') assert_equal "one & two, and three", ['one', 'two', 'three'].to_sentence(:words_connector => ' & ') assert_equal "onetwo, and three", ['one', 'two', 'three'].to_sentence(:words_connector => nil) end def test_to_sentence_with_last_word_connector - assert_deprecated(":skip_last_comma has been deprecated. Use :last_word_connector instead") do - assert_equal "one, two and three", ['one', 'two', 'three'].to_sentence(:skip_last_comma => true) - end - - assert_deprecated(":skip_last_comma has been deprecated. Use :last_word_connector instead") do - assert_equal "one, two, and three", ['one', 'two', 'three'].to_sentence(:skip_last_comma => false) - end - assert_equal "one, two, and also three", ['one', 'two', 'three'].to_sentence(:last_word_connector => ', and also ') assert_equal "one, twothree", ['one', 'two', 'three'].to_sentence(:last_word_connector => nil) assert_equal "one, two three", ['one', 'two', 'three'].to_sentence(:last_word_connector => ' ') From c0262827cacc1baf16668af65c35a09138166394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sun, 24 Jan 2010 23:40:45 +0100 Subject: [PATCH 48/63] Speed up some Hash core extensions. --- .../active_support/core_ext/hash/deep_merge.rb | 13 ++++++------- .../lib/active_support/core_ext/hash/keys.rb | 15 ++++++--------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/hash/deep_merge.rb b/activesupport/lib/active_support/core_ext/hash/deep_merge.rb index 24d0a2a4815..af771c86ff1 100644 --- a/activesupport/lib/active_support/core_ext/hash/deep_merge.rb +++ b/activesupport/lib/active_support/core_ext/hash/deep_merge.rb @@ -1,17 +1,16 @@ class Hash # Returns a new hash with +self+ and +other_hash+ merged recursively. def deep_merge(other_hash) - target = dup - other_hash.each_pair do |k,v| - tv = target[k] - target[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_merge(v) : v - end - target + dup.deep_merge!(other_hash) end # Returns a new hash with +self+ and +other_hash+ merged recursively. # Modifies the receiver in place. def deep_merge!(other_hash) - replace(deep_merge(other_hash)) + other_hash.each_pair do |k,v| + tv = self[k] + self[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_merge(v) : v + end + self end end diff --git a/activesupport/lib/active_support/core_ext/hash/keys.rb b/activesupport/lib/active_support/core_ext/hash/keys.rb index ecd63293b4f..045a6944fa3 100644 --- a/activesupport/lib/active_support/core_ext/hash/keys.rb +++ b/activesupport/lib/active_support/core_ext/hash/keys.rb @@ -1,10 +1,7 @@ class Hash # Return a new hash with all keys converted to strings. def stringify_keys - inject({}) do |options, (key, value)| - options[key.to_s] = value - options - end + dup.stringify_keys! end # Destructively convert all keys to strings. @@ -18,16 +15,16 @@ class Hash # Return a new hash with all keys converted to symbols, as long as # they respond to +to_sym+. def symbolize_keys - inject({}) do |options, (key, value)| - options[(key.to_sym rescue key) || key] = value - options - end + dup.symbolize_keys! end # Destructively convert all keys to symbols, as long as they respond # to +to_sym+. def symbolize_keys! - self.replace(self.symbolize_keys) + keys.each do |key| + self[(key.to_sym rescue key) || key] = delete(key) + end + self end alias_method :to_options, :symbolize_keys From 0ece244feec236f57fb2f55ea564409f25475923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Sun, 24 Jan 2010 23:59:12 +0100 Subject: [PATCH 49/63] Ensure implicit multipart templates with locale works as expected. --- actionmailer/lib/action_mailer/base.rb | 5 ++++ actionmailer/test/base_test.rb | 27 +++++++++++++++++++ .../implicit_with_locale.en.html.erb | 1 + .../base_mailer/implicit_with_locale.html.erb | 1 + .../implicit_with_locale.pl.text.erb | 1 + .../base_mailer/implicit_with_locale.text.erb | 1 + 6 files changed, 36 insertions(+) create mode 100644 actionmailer/test/fixtures/base_mailer/implicit_with_locale.en.html.erb create mode 100644 actionmailer/test/fixtures/base_mailer/implicit_with_locale.html.erb create mode 100644 actionmailer/test/fixtures/base_mailer/implicit_with_locale.pl.text.erb create mode 100644 actionmailer/test/fixtures/base_mailer/implicit_with_locale.text.erb diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 7101bfbb70c..84d50fa1f4c 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -1,5 +1,8 @@ require 'active_support/core_ext/class' +require 'active_support/core_ext/object/blank' +require 'active_support/core_ext/array/uniq_by' require 'active_support/core_ext/module/delegation' +require 'active_support/core_ext/string/inflections' require 'mail' require 'action_mailer/tmail_compat' require 'action_mailer/collector' @@ -455,6 +458,8 @@ module ActionMailer #:nodoc: def each_template(&block) #:nodoc: self.class.view_paths.each do |load_paths| templates = load_paths.find_all(action_name, {}, self.class.mailer_name) + templates = templates.uniq_by { |t| t.details[:formats] } + unless templates.empty? templates.each(&block) return diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index dd17bf868b5..71845d4c42e 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -34,6 +34,10 @@ class BaseTest < ActiveSupport::TestCase mail(DEFAULT_HEADERS.merge(hash)) end + def implicit_with_locale(hash = {}) + mail(DEFAULT_HEADERS.merge(hash)) + end + def explicit_multipart(hash = {}) attachments['invoice.pdf'] = 'This is test File content' if hash.delete(:attachments) mail(DEFAULT_HEADERS.merge(hash)) do |format| @@ -310,6 +314,29 @@ class BaseTest < ActiveSupport::TestCase assert_equal(1, BaseMailer.deliveries.length) end + test "implicit multipart with default locale" do + email = BaseMailer.implicit_with_locale.deliver + assert_equal(2, email.parts.size) + assert_equal("multipart/alternate", email.mime_type) + assert_equal("text/plain", email.parts[0].mime_type) + assert_equal("Implicit with locale TEXT", email.parts[0].body.encoded) + assert_equal("text/html", email.parts[1].mime_type) + assert_equal("Implicit with locale EN HTML", email.parts[1].body.encoded) + end + + test "implicit multipart with other locale" do + swap I18n, :locale => :pl do + email = BaseMailer.implicit_with_locale.deliver + assert_equal(2, email.parts.size) + assert_equal("multipart/alternate", email.mime_type) + assert_equal("text/plain", email.parts[0].mime_type) + assert_equal("Implicit with locale PL TEXT", email.parts[0].body.encoded) + assert_equal("text/html", email.parts[1].mime_type) + assert_equal("Implicit with locale HTML", email.parts[1].body.encoded) + end + end + + protected # Execute the block setting the given values and restoring old values after diff --git a/actionmailer/test/fixtures/base_mailer/implicit_with_locale.en.html.erb b/actionmailer/test/fixtures/base_mailer/implicit_with_locale.en.html.erb new file mode 100644 index 00000000000..34e39c8fdb6 --- /dev/null +++ b/actionmailer/test/fixtures/base_mailer/implicit_with_locale.en.html.erb @@ -0,0 +1 @@ +Implicit with locale EN HTML \ No newline at end of file diff --git a/actionmailer/test/fixtures/base_mailer/implicit_with_locale.html.erb b/actionmailer/test/fixtures/base_mailer/implicit_with_locale.html.erb new file mode 100644 index 00000000000..5ce283f221c --- /dev/null +++ b/actionmailer/test/fixtures/base_mailer/implicit_with_locale.html.erb @@ -0,0 +1 @@ +Implicit with locale HTML \ No newline at end of file diff --git a/actionmailer/test/fixtures/base_mailer/implicit_with_locale.pl.text.erb b/actionmailer/test/fixtures/base_mailer/implicit_with_locale.pl.text.erb new file mode 100644 index 00000000000..c49cbae60a9 --- /dev/null +++ b/actionmailer/test/fixtures/base_mailer/implicit_with_locale.pl.text.erb @@ -0,0 +1 @@ +Implicit with locale PL TEXT \ No newline at end of file diff --git a/actionmailer/test/fixtures/base_mailer/implicit_with_locale.text.erb b/actionmailer/test/fixtures/base_mailer/implicit_with_locale.text.erb new file mode 100644 index 00000000000..5a18ff62cda --- /dev/null +++ b/actionmailer/test/fixtures/base_mailer/implicit_with_locale.text.erb @@ -0,0 +1 @@ +Implicit with locale TEXT \ No newline at end of file From 48faf53be19c569e85f43a4dbfa63fe81618b09f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Mon, 25 Jan 2010 00:20:38 +0100 Subject: [PATCH 50/63] Add some view paths tests. --- actionmailer/test/base_test.rb | 65 ++++++++++++------- .../another.path/base_mailer/welcome.erb | 1 + 2 files changed, 43 insertions(+), 23 deletions(-) create mode 100644 actionmailer/test/fixtures/another.path/base_mailer/welcome.erb diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 71845d4c42e..b8d21132def 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -238,6 +238,48 @@ class BaseTest < ActiveSupport::TestCase end end + test "implicit multipart with default locale" do + email = BaseMailer.implicit_with_locale.deliver + assert_equal(2, email.parts.size) + assert_equal("multipart/alternate", email.mime_type) + assert_equal("text/plain", email.parts[0].mime_type) + assert_equal("Implicit with locale TEXT", email.parts[0].body.encoded) + assert_equal("text/html", email.parts[1].mime_type) + assert_equal("Implicit with locale EN HTML", email.parts[1].body.encoded) + end + + test "implicit multipart with other locale" do + swap I18n, :locale => :pl do + email = BaseMailer.implicit_with_locale.deliver + assert_equal(2, email.parts.size) + assert_equal("multipart/alternate", email.mime_type) + assert_equal("text/plain", email.parts[0].mime_type) + assert_equal("Implicit with locale PL TEXT", email.parts[0].body.encoded) + assert_equal("text/html", email.parts[1].mime_type) + assert_equal("Implicit with locale HTML", email.parts[1].body.encoded) + end + end + + test "implicit multipart with several view paths uses the first one with template" do + begin + BaseMailer.view_paths.unshift(File.join(FIXTURE_LOAD_PATH, "another.path")) + email = BaseMailer.welcome.deliver + assert_equal("Welcome from another path", email.body.encoded) + ensure + BaseMailer.view_paths.shift + end + end + + test "implicit multipart with inexistent templates uses the next view path" do + begin + BaseMailer.view_paths.unshift(File.join(FIXTURE_LOAD_PATH, "unknown")) + email = BaseMailer.welcome.deliver + assert_equal("Welcome", email.body.encoded) + ensure + BaseMailer.view_paths.shift + end + end + # Explicit multipart test "explicit multipart" do email = BaseMailer.explicit_multipart.deliver @@ -314,29 +356,6 @@ class BaseTest < ActiveSupport::TestCase assert_equal(1, BaseMailer.deliveries.length) end - test "implicit multipart with default locale" do - email = BaseMailer.implicit_with_locale.deliver - assert_equal(2, email.parts.size) - assert_equal("multipart/alternate", email.mime_type) - assert_equal("text/plain", email.parts[0].mime_type) - assert_equal("Implicit with locale TEXT", email.parts[0].body.encoded) - assert_equal("text/html", email.parts[1].mime_type) - assert_equal("Implicit with locale EN HTML", email.parts[1].body.encoded) - end - - test "implicit multipart with other locale" do - swap I18n, :locale => :pl do - email = BaseMailer.implicit_with_locale.deliver - assert_equal(2, email.parts.size) - assert_equal("multipart/alternate", email.mime_type) - assert_equal("text/plain", email.parts[0].mime_type) - assert_equal("Implicit with locale PL TEXT", email.parts[0].body.encoded) - assert_equal("text/html", email.parts[1].mime_type) - assert_equal("Implicit with locale HTML", email.parts[1].body.encoded) - end - end - - protected # Execute the block setting the given values and restoring old values after diff --git a/actionmailer/test/fixtures/another.path/base_mailer/welcome.erb b/actionmailer/test/fixtures/another.path/base_mailer/welcome.erb new file mode 100644 index 00000000000..ef451298e26 --- /dev/null +++ b/actionmailer/test/fixtures/another.path/base_mailer/welcome.erb @@ -0,0 +1 @@ +Welcome from another path \ No newline at end of file From e1c131863897390d04bd5515765236590747f2c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Mon, 25 Jan 2010 00:37:12 +0100 Subject: [PATCH 51/63] Added delivers_from. --- actionmailer/lib/action_mailer/base.rb | 14 ++++++++++++-- actionmailer/test/base_test.rb | 9 ++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 84d50fa1f4c..552fd7ccb8d 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -273,6 +273,9 @@ module ActionMailer #:nodoc: private_class_method :new #:nodoc: + extlib_inheritable_accessor :default_from + self.default_from = nil + extlib_inheritable_accessor :default_charset self.default_charset = "utf-8" @@ -298,6 +301,12 @@ module ActionMailer #:nodoc: attr_writer :mailer_name alias :controller_path :mailer_name + # Sets who is the default sender for the e-mail + def delivers_from(value = nil) + self.default_from = value if value + self.default_from + end + # Receives a raw email, parses it into an email object, decodes it, # instantiates a new mailer, and passes the email object to the mailer # object's +receive+ method. If you want your mailer to be able to @@ -318,7 +327,7 @@ module ActionMailer #:nodoc: end # TODO The delivery should happen inside the instrument block - def delivered_email(mail) + def delivered_email(mail) #:nodoc: ActiveSupport::Notifications.instrument("action_mailer.deliver") do |payload| self.set_payload_for_mail(payload, mail) end @@ -387,8 +396,9 @@ module ActionMailer #:nodoc: charset = headers[:charset] || m.charset || self.class.default_charset.dup mime_version = headers[:mime_version] || m.mime_version || self.class.default_mime_version.dup - # Set subjects and fields quotings + # Set fields quotings headers[:subject] ||= default_subject + headers[:from] ||= self.class.default_from.dup quote_fields!(headers, charset) # Render the templates and blocks diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index b8d21132def..d8616ee3be4 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -1,16 +1,14 @@ # encoding: utf-8 require 'abstract_unit' -# class Notifier < ActionMailer::Base -# delivers_from 'notifications@example.com' class BaseTest < ActiveSupport::TestCase DEFAULT_HEADERS = { :to => 'mikel@test.lindsaar.net', - :from => 'jose@test.plataformatec.com', :subject => 'The first email on new API!' } class BaseMailer < ActionMailer::Base + delivers_from 'jose@test.plataformatec.com' self.mailer_name = "base_mailer" def welcome(hash = {}) @@ -72,6 +70,11 @@ class BaseTest < ActiveSupport::TestCase assert_equal(email.subject, 'The first email on new API!') end + test "mail() with from overwrites the class level default" do + email = BaseMailer.welcome(:from => 'someone@else.com').deliver + assert_equal(email.from, ['someone@else.com']) + end + test "mail() with bcc, cc, content_type, charset, mime_version, reply_to and date" do @time = Time.now email = BaseMailer.welcome(:bcc => 'bcc@test.lindsaar.net', From e4a989e9d99b9860859ad14b5a6fca62a4009cf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Mon, 25 Jan 2010 13:39:48 +1100 Subject: [PATCH 52/63] Added delivery_handler method to mail and implemented in ActionMailer to deliver inside of instrumentation --- actionmailer/lib/action_mailer/base.rb | 13 ++++++-- .../lib/action_mailer/delivery_methods.rb | 2 +- actionmailer/test/base_test.rb | 7 ++++- actionmailer/test/delivery_methods_test.rb | 30 +++++++++++++++---- 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 552fd7ccb8d..4e5e1bbb292 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -326,10 +326,19 @@ module ActionMailer #:nodoc: end end - # TODO The delivery should happen inside the instrument block - def delivered_email(mail) #:nodoc: + def deliver_mail(mail) #:nodoc: ActiveSupport::Notifications.instrument("action_mailer.deliver") do |payload| self.set_payload_for_mail(payload, mail) + + if mail.perform_deliveries + begin + mail.deliver! + rescue Exception => e + raise e if mail.raise_delivery_errors + end + Mail.deliveries << mail + end + end end diff --git a/actionmailer/lib/action_mailer/delivery_methods.rb b/actionmailer/lib/action_mailer/delivery_methods.rb index 21909d5d57d..7a6b4109325 100644 --- a/actionmailer/lib/action_mailer/delivery_methods.rb +++ b/actionmailer/lib/action_mailer/delivery_methods.rb @@ -63,7 +63,7 @@ module ActionMailer def wrap_delivery_behavior(mail, method=nil) #:nodoc: method ||= self.delivery_method - mail.register_for_delivery_notification(self) + mail.delivery_handler = self if method.is_a?(Symbol) if klass = delivery_methods[method.to_sym] diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index d8616ee3be4..81e1df3911d 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -354,7 +354,12 @@ class BaseTest < ActiveSupport::TestCase test "calling deliver on the action should deliver the mail object" do BaseMailer.deliveries.clear - BaseMailer.expects(:delivered_email).once + BaseMailer.expects(:deliver_mail).once + BaseMailer.welcome.deliver + end + + test "calling deliver on the action should increment the deliveries collection" do + BaseMailer.deliveries.clear BaseMailer.welcome.deliver assert_equal(1, BaseMailer.deliveries.length) end diff --git a/actionmailer/test/delivery_methods_test.rb b/actionmailer/test/delivery_methods_test.rb index 145f8ba30d7..e70e8acd9b3 100644 --- a/actionmailer/test/delivery_methods_test.rb +++ b/actionmailer/test/delivery_methods_test.rb @@ -94,9 +94,8 @@ class MailDeliveryTest < ActiveSupport::TestCase test "ActionMailer should be told when Mail gets delivered" do DeliveryMailer.deliveries.clear - DeliveryMailer.expects(:delivered_email).once + DeliveryMailer.expects(:deliver_mail).once DeliveryMailer.welcome.deliver - assert_equal(1, DeliveryMailer.deliveries.length) end test "delivery method can be customized per instance" do @@ -124,7 +123,13 @@ class MailDeliveryTest < ActiveSupport::TestCase test "does not perform deliveries if requested" do DeliveryMailer.perform_deliveries = false DeliveryMailer.deliveries.clear - DeliveryMailer.expects(:delivered_email).never + Mail::Message.any_instance.expects(:deliver!).never + DeliveryMailer.welcome.deliver + end + + test "does not append the deliveries collection if told not to perform the delivery" do + DeliveryMailer.perform_deliveries = false + DeliveryMailer.deliveries.clear DeliveryMailer.welcome.deliver assert_equal(0, DeliveryMailer.deliveries.length) end @@ -132,7 +137,14 @@ class MailDeliveryTest < ActiveSupport::TestCase test "raise errors on bogus deliveries" do DeliveryMailer.delivery_method = BogusDelivery DeliveryMailer.deliveries.clear - DeliveryMailer.expects(:delivered_email).never + assert_raise RuntimeError do + DeliveryMailer.welcome.deliver + end + end + + test "does not increment the deliveries collection on error" do + DeliveryMailer.delivery_method = BogusDelivery + DeliveryMailer.deliveries.clear assert_raise RuntimeError do DeliveryMailer.welcome.deliver end @@ -140,11 +152,19 @@ class MailDeliveryTest < ActiveSupport::TestCase end test "does not raise errors on bogus deliveries if set" do + DeliveryMailer.delivery_method = BogusDelivery + DeliveryMailer.raise_delivery_errors = false + assert_nothing_raised do + DeliveryMailer.welcome.deliver + end + end + + test "increments the deliveries collection on bogus deliveries if set to ignore" do DeliveryMailer.delivery_method = BogusDelivery DeliveryMailer.raise_delivery_errors = false DeliveryMailer.deliveries.clear - DeliveryMailer.expects(:delivered_email).once DeliveryMailer.welcome.deliver assert_equal(1, DeliveryMailer.deliveries.length) end + end \ No newline at end of file From 4240369a4399b944a0652807c545ffe99d7fb0cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Mon, 25 Jan 2010 13:46:44 +1100 Subject: [PATCH 53/63] changed test to get TestMailer to use :file delivery method directly (as setup action was resetting delivery_method to :test on init --- actionmailer/test/old_base/mail_service_test.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/actionmailer/test/old_base/mail_service_test.rb b/actionmailer/test/old_base/mail_service_test.rb index 20b0f30b84c..2e33abf9d64 100644 --- a/actionmailer/test/old_base/mail_service_test.rb +++ b/actionmailer/test/old_base/mail_service_test.rb @@ -967,10 +967,10 @@ EOF end def test_file_delivery_should_create_a_file - ActionMailer::Base.delivery_method = :file - tmp_location = ActionMailer::Base.file_settings[:location] + TestMailer.delivery_method = :file + tmp_location = TestMailer.file_settings[:location] - TestMailer.cc_bcc(@recipient).deliver + result = TestMailer.cc_bcc(@recipient).deliver assert File.exists?(tmp_location) assert File.directory?(tmp_location) assert File.exists?(File.join(tmp_location, @recipient)) From ace74974cf3575bbd3bf7ff4d8a83c3100fd22a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Mon, 25 Jan 2010 21:47:03 +1100 Subject: [PATCH 54/63] Got AM working with Mail yield on delivery_handler and updated tests --- actionmailer/actionmailer.gemspec | 2 +- actionmailer/lib/action_mailer/base.rb | 9 +-------- actionmailer/lib/action_mailer/delivery_methods.rb | 4 ++-- actionmailer/test/base_test.rb | 7 +++++++ actionmailer/test/delivery_methods_test.rb | 4 ++-- actionmailer/test/old_base/mail_service_test.rb | 8 ++++++++ actionmailer/test/old_base/url_test.rb | 2 ++ 7 files changed, 23 insertions(+), 13 deletions(-) diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec index 4f53c709c45..a3d466c9865 100644 --- a/actionmailer/actionmailer.gemspec +++ b/actionmailer/actionmailer.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |s| s.homepage = "http://www.rubyonrails.org" s.add_dependency('actionpack', '= 3.0.pre') - s.add_dependency('mail', '~> 2.0.5') + s.add_dependency('mail', '~> 2.0.6') s.files = Dir['CHANGELOG', 'README', 'MIT-LICENSE', 'lib/**/*'] s.has_rdoc = true diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 4e5e1bbb292..47d0ffaf687 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -330,14 +330,7 @@ module ActionMailer #:nodoc: ActiveSupport::Notifications.instrument("action_mailer.deliver") do |payload| self.set_payload_for_mail(payload, mail) - if mail.perform_deliveries - begin - mail.deliver! - rescue Exception => e - raise e if mail.raise_delivery_errors - end - Mail.deliveries << mail - end + yield # Let Mail do the delivery actions end end diff --git a/actionmailer/lib/action_mailer/delivery_methods.rb b/actionmailer/lib/action_mailer/delivery_methods.rb index 7a6b4109325..34bfe6000a1 100644 --- a/actionmailer/lib/action_mailer/delivery_methods.rb +++ b/actionmailer/lib/action_mailer/delivery_methods.rb @@ -40,8 +40,8 @@ module ActionMailer end module ClassMethods - # Provides a list of emails that have been delivered by Mail - delegate :deliveries, :deliveries=, :to => Mail + # Provides a list of emails that have been delivered by Mail::TestMailer + delegate :deliveries, :deliveries=, :to => Mail::TestMailer # Adds a new delivery method through the given class using the given symbol # as alias and the default options supplied: diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 81e1df3911d..0705f22df8e 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -363,6 +363,13 @@ class BaseTest < ActiveSupport::TestCase BaseMailer.welcome.deliver assert_equal(1, BaseMailer.deliveries.length) end + + 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 + end protected diff --git a/actionmailer/test/delivery_methods_test.rb b/actionmailer/test/delivery_methods_test.rb index e70e8acd9b3..4907ca09035 100644 --- a/actionmailer/test/delivery_methods_test.rb +++ b/actionmailer/test/delivery_methods_test.rb @@ -159,12 +159,12 @@ class MailDeliveryTest < ActiveSupport::TestCase end end - test "increments the deliveries collection on bogus deliveries if set to ignore" do + test "does not increment the deliveries collection on bogus deliveries" do DeliveryMailer.delivery_method = BogusDelivery DeliveryMailer.raise_delivery_errors = false DeliveryMailer.deliveries.clear DeliveryMailer.welcome.deliver - assert_equal(1, DeliveryMailer.deliveries.length) + assert_equal(0, DeliveryMailer.deliveries.length) end end \ No newline at end of file diff --git a/actionmailer/test/old_base/mail_service_test.rb b/actionmailer/test/old_base/mail_service_test.rb index 2e33abf9d64..fb784328bc0 100644 --- a/actionmailer/test/old_base/mail_service_test.rb +++ b/actionmailer/test/old_base/mail_service_test.rb @@ -387,6 +387,8 @@ class ActionMailerTest < Test::Unit::TestCase end def test_signed_up + TestMailer.delivery_method = :test + Time.stubs(:now => Time.now) expected = new_mail @@ -533,6 +535,8 @@ class ActionMailerTest < Test::Unit::TestCase end def test_reply_to + TestMailer.delivery_method = :test + expected = new_mail expected.to = @recipient @@ -567,6 +571,8 @@ class ActionMailerTest < Test::Unit::TestCase end def test_iso_charset + TestMailer.delivery_method = :test + expected = new_mail( "iso-8859-1" ) expected.to = @recipient expected.subject = encode "testing isø charsets", "iso-8859-1" @@ -601,6 +607,7 @@ class ActionMailerTest < Test::Unit::TestCase end def test_unencoded_subject + TestMailer.delivery_method = :test expected = new_mail expected.to = @recipient expected.subject = "testing unencoded subject" @@ -1029,6 +1036,7 @@ EOF end def test_with_mail_object_deliver + TestMailer.delivery_method = :test mail = TestMailer.headers_with_nonalpha_chars(@recipient) assert_nothing_raised { mail.deliver } assert_equal 1, TestMailer.deliveries.length diff --git a/actionmailer/test/old_base/url_test.rb b/actionmailer/test/old_base/url_test.rb index 10b6a36efd5..5affb479979 100644 --- a/actionmailer/test/old_base/url_test.rb +++ b/actionmailer/test/old_base/url_test.rb @@ -54,6 +54,8 @@ class ActionMailerUrlTest < Test::Unit::TestCase end def test_signed_up_with_url + TestMailer.delivery_method = :test + ActionController::Routing::Routes.draw do |map| map.connect ':controller/:action/:id' map.welcome 'welcome', :controller=>"foo", :action=>"bar" From d5e4e9185ba09e7f461f3b62243e7b5db4d4b277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Mon, 25 Jan 2010 21:47:24 +1100 Subject: [PATCH 55/63] Updated gemspec for Mail --- actionmailer/actionmailer.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec index a3d466c9865..e348871c256 100644 --- a/actionmailer/actionmailer.gemspec +++ b/actionmailer/actionmailer.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |s| s.homepage = "http://www.rubyonrails.org" s.add_dependency('actionpack', '= 3.0.pre') - s.add_dependency('mail', '~> 2.0.6') + s.add_dependency('mail', '~> 2.1.0') s.files = Dir['CHANGELOG', 'README', 'MIT-LICENSE', 'lib/**/*'] s.has_rdoc = true From ad8f5d4106c77ed1d054bf3b29bec94df557a7cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Mon, 25 Jan 2010 23:15:16 +1100 Subject: [PATCH 56/63] Updated to mail 2.1.1 --- actionmailer/actionmailer.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec index e348871c256..d73f86cd651 100644 --- a/actionmailer/actionmailer.gemspec +++ b/actionmailer/actionmailer.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |s| s.homepage = "http://www.rubyonrails.org" s.add_dependency('actionpack', '= 3.0.pre') - s.add_dependency('mail', '~> 2.1.0') + s.add_dependency('mail', '~> 2.1.1') s.files = Dir['CHANGELOG', 'README', 'MIT-LICENSE', 'lib/**/*'] s.has_rdoc = true From 9cb3ca1d29eb770c1a7adac3798666847fceee2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Mon, 25 Jan 2010 13:13:29 +0100 Subject: [PATCH 57/63] Change mailer generator templates and refactor the whole naming schema. --- .../generators/erb/mailer/templates/view.erb | 2 +- .../rails/mailer/templates/mailer.rb | 19 ++-- .../rails/resource/resource_generator.rb | 4 +- .../scaffold_controller_generator.rb | 6 +- .../test_unit/mailer/templates/fixture | 2 +- .../mailer/templates/functional_test.rb | 12 ++- railties/lib/rails/generators/named_base.rb | 89 ++++++++----------- .../lib/rails/generators/resource_helpers.rb | 45 +++++----- .../test/generators/mailer_generator_test.rb | 40 +++++++-- railties/test/generators/named_base_test.rb | 62 +++++++++---- 10 files changed, 171 insertions(+), 110 deletions(-) diff --git a/railties/lib/generators/erb/mailer/templates/view.erb b/railties/lib/generators/erb/mailer/templates/view.erb index fcce7bd8052..6d597256a64 100644 --- a/railties/lib/generators/erb/mailer/templates/view.erb +++ b/railties/lib/generators/erb/mailer/templates/view.erb @@ -1,3 +1,3 @@ <%= class_name %>#<%= @action %> -Find me in app/views/<%= @path %> +<%%= @greeting %>, find me in app/views/<%= @path %> diff --git a/railties/lib/generators/rails/mailer/templates/mailer.rb b/railties/lib/generators/rails/mailer/templates/mailer.rb index 90e0b712d6d..5e7ef42370d 100644 --- a/railties/lib/generators/rails/mailer/templates/mailer.rb +++ b/railties/lib/generators/rails/mailer/templates/mailer.rb @@ -1,14 +1,15 @@ class <%= class_name %> < ActionMailer::Base + delivers_from "mail@<%= application_name %>.com" <% for action in actions -%> - def <%= action %>(sent_at = Time.now) - subject '<%= class_name %>#<%= action %>' - recipients '' - from '' - sent_on sent_at - - body :greeting => 'Hi,' + # Subject can be set in your I18n file at config/locales/en.yml + # with the following lookup: + # + # en.actionmailer.<%= file_name %>.<%= action %>.subject + # + def <%= action %> + @greeting = "Hi" + mail(:to => "") end - <% end -%> -end +end \ No newline at end of file diff --git a/railties/lib/generators/rails/resource/resource_generator.rb b/railties/lib/generators/rails/resource/resource_generator.rb index 43c7cc85f4d..5acb839f39a 100644 --- a/railties/lib/generators/rails/resource/resource_generator.rb +++ b/railties/lib/generators/rails/resource/resource_generator.rb @@ -6,8 +6,8 @@ module Rails class ResourceGenerator < ModelGenerator #metagenerator include ResourceHelpers - hook_for :resource_controller, :required => true do |base, controller| - base.invoke controller, [ base.controller_name, base.options[:actions] ] + hook_for :resource_controller, :required => true do |controller| + invoke controller, [ controller_name, options[:actions] ] end class_option :actions, :type => :array, :banner => "ACTION ACTION", :default => [], diff --git a/railties/lib/generators/rails/scaffold_controller/scaffold_controller_generator.rb b/railties/lib/generators/rails/scaffold_controller/scaffold_controller_generator.rb index e544e29892c..49af2974cd1 100644 --- a/railties/lib/generators/rails/scaffold_controller/scaffold_controller_generator.rb +++ b/railties/lib/generators/rails/scaffold_controller/scaffold_controller_generator.rb @@ -18,9 +18,9 @@ module Rails hook_for :template_engine, :test_framework, :as => :scaffold - # Invoke the helper using the controller (pluralized) name. - hook_for :helper, :as => :scaffold do |base, invoked| - base.invoke invoked, [ base.controller_name ] + # Invoke the helper using the controller name (pluralized) + hook_for :helper, :as => :scaffold do |invoked| + invoke invoked, [ controller_name ] end end end diff --git a/railties/lib/generators/test_unit/mailer/templates/fixture b/railties/lib/generators/test_unit/mailer/templates/fixture index fcce7bd8052..171648d6fd3 100644 --- a/railties/lib/generators/test_unit/mailer/templates/fixture +++ b/railties/lib/generators/test_unit/mailer/templates/fixture @@ -1,3 +1,3 @@ <%= class_name %>#<%= @action %> -Find me in app/views/<%= @path %> +Hi, find me in app/views/<%= @path %> diff --git a/railties/lib/generators/test_unit/mailer/templates/functional_test.rb b/railties/lib/generators/test_unit/mailer/templates/functional_test.rb index d7366fea5fc..2f694e431c5 100644 --- a/railties/lib/generators/test_unit/mailer/templates/functional_test.rb +++ b/railties/lib/generators/test_unit/mailer/templates/functional_test.rb @@ -3,11 +3,17 @@ require 'test_helper' class <%= class_name %>Test < ActionMailer::TestCase <% for action in actions -%> test "<%= action %>" do - @expected.subject = '<%= class_name %>#<%= action %>' - @expected.body = read_fixture('<%= action %>') + @actual = <%= class_name %>.<%= action %> + + @expected.subject = <%= action.to_s.humanize.inspect %> + @expected.body = read_fixture("<%= action %>") @expected.date = Time.now - assert_equal @expected, <%= class_name %>.create_<%= action %>(@expected.date) + assert_difference "<%= class_name %>.deliveries.size" do + @actual.deliver + end + + assert_equal @expected.encoded, @actual.encoded end <% end -%> diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb index 3e851bf888f..12e918731ed 100644 --- a/railties/lib/rails/generators/named_base.rb +++ b/railties/lib/rails/generators/named_base.rb @@ -6,17 +6,9 @@ module Rails class NamedBase < Base argument :name, :type => :string - no_tasks { - attr_reader :class_name, :singular_name, :plural_name, :table_name, - :class_path, :file_path, :class_nesting_depth - - alias :file_name :singular_name - } - def initialize(args, *options) #:nodoc: # Unfreeze name in case it's given as a frozen string args[0] = args[0].dup if args[0].is_a?(String) && args[0].frozen? - super assign_names!(self.name) parse_attributes! if respond_to?(:attributes) @@ -24,28 +16,48 @@ module Rails protected - def assign_names!(given_name) #:nodoc: - base_name, @class_path, @file_path, class_nesting, @class_nesting_depth = extract_modules(given_name) - class_name_without_nesting, @singular_name, @plural_name = inflect_names(base_name) + attr_reader :class_path, :file_name + alias :singular_name :file_name - @table_name = if pluralize_table_names? - plural_name - else - singular_name - end - - if class_nesting.empty? - @class_name = class_name_without_nesting - else - @table_name = class_nesting.underscore << "_" << @table_name - @class_name = "#{class_nesting}::#{class_name_without_nesting}" - end - - @table_name.gsub!('/', '_') + def file_path + @file_path ||= (class_path + [file_name]).join('/') end - # Convert attributes hash into an array with GeneratedAttribute objects. - # + def class_name + @class_name ||= (class_path + [file_name]).map!{ |m| m.camelize }.join('::') + end + + def plural_name + @plural_name ||= singular_name.pluralize + end + + def i18n_scope + @i18n_scope ||= file_path.gsub('/', '.') + end + + def table_name + @table_name ||= begin + base = pluralize_table_names? ? plural_name : singular_name + (class_path + [base]).join('_') + end + end + + # Tries to retrieve the application name or simple return application. + def application_name + if defined?(Rails) && Rails.application + Rails.application.class.name.split('::').first.underscore + else + "application" + end + end + + def assign_names!(name) #:nodoc: + @class_path = name.include?('/') ? name.split('/') : name.split('::') + @class_path.map! { |m| m.underscore } + @file_name = @class_path.pop + end + + # Convert attributes array into GeneratedAttribute objects. def parse_attributes! #:nodoc: self.attributes = (attributes || []).map do |key_value| name, type = key_value.split(':') @@ -53,29 +65,6 @@ module Rails end end - # Extract modules from filesystem-style or ruby-style path. Both - # good/fun/stuff and Good::Fun::Stuff produce the same results. - # - def extract_modules(name) #:nodoc: - modules = name.include?('/') ? name.split('/') : name.split('::') - name = modules.pop - path = modules.map { |m| m.underscore } - - file_path = (path + [name.underscore]).join('/') - nesting = modules.map { |m| m.camelize }.join('::') - - [name, path, file_path, nesting, modules.size] - end - - # Receives name and return camelized, underscored and pluralized names. - # - def inflect_names(name) #:nodoc: - camel = name.camelize - under = camel.underscore - plural = under.pluralize - [camel, under, plural] - end - def pluralize_table_names? !defined?(ActiveRecord::Base) || ActiveRecord::Base.pluralize_table_names end diff --git a/railties/lib/rails/generators/resource_helpers.rb b/railties/lib/rails/generators/resource_helpers.rb index 7e00a222eda..3a98a8f9c1d 100644 --- a/railties/lib/rails/generators/resource_helpers.rb +++ b/railties/lib/rails/generators/resource_helpers.rb @@ -9,14 +9,7 @@ module Rails mattr_accessor :skip_warn def self.included(base) #:nodoc: - base.class_eval do - class_option :force_plural, :type => :boolean, :desc => "Forces the use of a plural ModelName" - - no_tasks { - attr_reader :controller_name, :controller_class_name, :controller_file_name, - :controller_class_path, :controller_file_path - } - end + base.class_option :force_plural, :type => :boolean, :desc => "Forces the use of a plural ModelName" end # Set controller variables on initialization. @@ -29,29 +22,40 @@ module Rails say "Plural version of the model detected, using singularized version. Override with --force-plural." ResourceHelpers.skip_warn = true end - name.replace name.singularize - assign_names!(self.name) + assign_names!(name) end @controller_name = name.pluralize - - base_name, @controller_class_path, @controller_file_path, class_nesting, class_nesting_depth = extract_modules(@controller_name) - class_name_without_nesting, @controller_file_name, controller_plural_name = inflect_names(base_name) - - @controller_class_name = if class_nesting.empty? - class_name_without_nesting - else - "#{class_nesting}::#{class_name_without_nesting}" - end end protected + attr_reader :controller_name + + def controller_class_path + @class_path + end + + def controller_file_name + @controller_file_name ||= file_name.pluralize + end + + def controller_file_path + @controller_file_path ||= (controller_class_path + [controller_file_name]).join('/') + end + + def controller_class_name + @controller_class_name ||= (controller_class_path + [controller_file_name]).map!{ |m| m.camelize }.join('::') + end + + def controller_i18n_scope + @controller_i18n_scope ||= controller_file_path.gsub('/', '.') + end + # Loads the ORM::Generators::ActiveModel class. This class is responsable # to tell scaffold entities how to generate an specific method for the # ORM. Check Rails::Generators::ActiveModel for more information. - # def orm_class @orm_class ||= begin # Raise an error if the class_option :orm was not defined. @@ -68,7 +72,6 @@ module Rails end # Initialize ORM::Generators::ActiveModel to access instance methods. - # def orm_instance(name=file_name) @orm_instance ||= @orm_class.new(name) end diff --git a/railties/test/generators/mailer_generator_test.rb b/railties/test/generators/mailer_generator_test.rb index dfc3130f77c..0203eb314c6 100644 --- a/railties/test/generators/mailer_generator_test.rb +++ b/railties/test/generators/mailer_generator_test.rb @@ -6,8 +6,20 @@ class MailerGeneratorTest < Rails::Generators::TestCase arguments %w(notifier foo bar) def test_mailer_skeleton_is_created + Rails.stubs(:application).returns(Object.new) run_generator - assert_file "app/mailers/notifier.rb", /class Notifier < ActionMailer::Base/ + assert_file "app/mailers/notifier.rb" do |mailer| + assert_match /class Notifier < ActionMailer::Base/, mailer + assert_match /delivers_from "mail@object.com"/, mailer + end + end + + def test_mailer_with_i18n_helper + run_generator + assert_file "app/mailers/notifier.rb" do |mailer| + assert_match /en\.actionmailer\.notifier\.foo\.subject/, mailer + assert_match /en\.actionmailer\.notifier\.bar\.subject/, mailer + end end def test_check_class_collision @@ -24,8 +36,15 @@ class MailerGeneratorTest < Rails::Generators::TestCase def test_invokes_default_template_engine run_generator - assert_file "app/views/notifier/foo.erb", /app\/views\/notifier\/foo/ - assert_file "app/views/notifier/bar.erb", /app\/views\/notifier\/bar/ + assert_file "app/views/notifier/foo.erb" do |view| + assert_match /app\/views\/notifier\/foo/, view + assert_match /<%= @greeting %>/, view + end + + assert_file "app/views/notifier/bar.erb" do |view| + assert_match /app\/views\/notifier\/bar/, view + assert_match /<%= @greeting %>/, view + end end def test_invokes_default_template_engine_even_with_no_action @@ -40,7 +59,18 @@ class MailerGeneratorTest < Rails::Generators::TestCase def test_actions_are_turned_into_methods run_generator - assert_file "app/mailers/notifier.rb", /def foo/ - assert_file "app/mailers/notifier.rb", /def bar/ + + assert_file "app/mailers/notifier.rb" do |mailer| + assert_instance_method :foo, mailer do |foo| + assert_match /mail\(:to => ""\)/, foo + assert_match /@greeting = "Hi"/, foo + end + + assert_instance_method :bar, mailer do |bar| + assert_match /mail\(:to => ""\)/, bar + assert_match /@greeting = "Hi"/, bar + end + end + end end diff --git a/railties/test/generators/named_base_test.rb b/railties/test/generators/named_base_test.rb index 99eb431a490..7514bc32bdf 100644 --- a/railties/test/generators/named_base_test.rb +++ b/railties/test/generators/named_base_test.rb @@ -16,28 +16,60 @@ class NamedBaseTest < Rails::Generators::TestCase tests Rails::Generators::ScaffoldControllerGenerator def test_named_generator_attributes - g = generator ["admin/foo"] - assert_equal 'admin/foo', g.name - assert_equal %w(admin), g.class_path - assert_equal 1, g.class_nesting_depth - assert_equal 'Admin::Foo', g.class_name - assert_equal 'foo', g.singular_name - assert_equal 'foos', g.plural_name - assert_equal g.singular_name, g.file_name - assert_equal "admin_#{g.plural_name}", g.table_name + g = generator ['admin/foo'] + assert_name g, 'admin/foo', :name + assert_name g, %w(admin), :class_path + assert_name g, 'Admin::Foo', :class_name + assert_name g, 'admin/foo', :file_path + assert_name g, 'foo', :file_name + assert_name g, 'foo', :singular_name + assert_name g, 'foos', :plural_name + assert_name g, 'admin.foo', :i18n_scope + assert_name g, 'admin_foos', :table_name + end + + def test_named_generator_attributes_as_ruby + g = generator ['Admin::Foo'] + assert_name g, 'Admin::Foo', :name + assert_name g, %w(admin), :class_path + assert_name g, 'Admin::Foo', :class_name + assert_name g, 'admin/foo', :file_path + assert_name g, 'foo', :file_name + assert_name g, 'foo', :singular_name + assert_name g, 'foos', :plural_name + assert_name g, 'admin.foo', :i18n_scope + assert_name g, 'admin_foos', :table_name end def test_named_generator_attributes_without_pluralized ActiveRecord::Base.pluralize_table_names = false - g = generator ["admin/foo"] - assert_equal "admin_#{g.singular_name}", g.table_name + g = generator ['admin/foo'] + assert_name g, 'admin_foo', :table_name end def test_scaffold_plural_names - g = generator ["ProductLine"] - assert_equal "ProductLines", g.controller_name - assert_equal "ProductLines", g.controller_class_name - assert_equal "product_lines", g.controller_file_name + g = generator ['admin/foo'] + assert_name g, 'admin/foos', :controller_name + assert_name g, %w(admin), :controller_class_path + assert_name g, 'Admin::Foos', :controller_class_name + assert_name g, 'admin/foos', :controller_file_path + assert_name g, 'foos', :controller_file_name + assert_name g, 'admin.foos', :controller_i18n_scope end + def test_scaffold_plural_names_as_ruby + g = generator ['Admin::Foo'] + assert_name g, 'Admin::Foos', :controller_name + assert_name g, %w(admin), :controller_class_path + assert_name g, 'Admin::Foos', :controller_class_name + assert_name g, 'admin/foos', :controller_file_path + assert_name g, 'foos', :controller_file_name + assert_name g, 'admin.foos', :controller_i18n_scope + end + + protected + + def assert_name(generator, value, method) + assert_equal value, generator.send(method) + end end From 4a6eba3232fec13892f36fc4730bb2deef342fc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Mon, 25 Jan 2010 23:46:09 +1100 Subject: [PATCH 58/63] Added initial documentation for the new API --- actionmailer/CHANGELOG | 2 + actionmailer/README | 62 ++++-- actionmailer/lib/action_mailer/base.rb | 255 +++++++++++++++---------- 3 files changed, 201 insertions(+), 118 deletions(-) diff --git a/actionmailer/CHANGELOG b/actionmailer/CHANGELOG index 785bf98c550..0018a2ed5db 100644 --- a/actionmailer/CHANGELOG +++ b/actionmailer/CHANGELOG @@ -1,5 +1,7 @@ *Rails 3.0 (pending)* +* Whole new API added with tests. See base.rb for full details. Old API is deprecated. + * The Mail::Message class has helped methods for all the field types that return 'common' defaults for the common use case, so to get the subject, mail.subject will give you a string, mail.date will give you a DateTime object, mail.from will give you an array of address specs (mikel@test.lindsaar.net) etc. If you want to access the field object itself, call mail[:field_name] which will return the field object you want, which you can then chain, like mail[:from].formatted * Mail#content_type now returns the content_type field as a string. If you want the mime type of a mail, then you call Mail#mime_type (eg, text/plain), if you want the parameters of the content type field, you call Mail#content_type_parameters which gives you a hash, eg {'format' => 'flowed', 'charset' => 'utf-8'} diff --git a/actionmailer/README b/actionmailer/README index 0e16ea6ec6b..542996f87bf 100644 --- a/actionmailer/README +++ b/actionmailer/README @@ -5,51 +5,74 @@ are used to consolidate code for sending out forgotten passwords, welcome wishes on signup, invoices for billing, and any other use case that requires a written notification to either a person or another system. +Action Mailer is in essence a wrapper around Action Controller and the +Mail gem. It provides a way to make emails using templates in the same +way that Action Controller renders views using templates. + Additionally, an Action Mailer class can be used to process incoming email, such as allowing a weblog to accept new posts from an email (which could even have been sent from a phone). == Sending emails -The framework works by setting up all the email details, except the body, -in methods on the service layer. Subject, recipients, sender, and timestamp -are all set up this way. An example of such a method: +The framework works by initializing any instance variables you want to be +available in the email template, followed by a call to +mail+ to deliver +the email. + +This can be as simple as: + + class Notifier < ActionMailer::Base + + delivers_from 'system@loudthinking.com' + + def welcome(recipient) + @recipient = recipient + mail(:to => recipient, + :subject => "[Signed up] Welcome #{recipient}") + end - def signed_up(recipient) - recipients recipient - subject "[Signed up] Welcome #{recipient}" - from "system@loudthinking.com" - body :recipient => recipient end The body of the email is created by using an Action View template (regular -ERb) that has the content of the body hash parameter available as instance variables. +ERb) that has the instance variables that are declared in the mailer action. + So the corresponding body template for the method above could look like this: Hello there, Mr. <%= @recipient %> + + Thank you for signing up! And if the recipient was given as "david@loudthinking.com", the email generated would look like this: - Date: Sun, 12 Dec 2004 00:00:00 +0100 + Date: Mon, 25 Jan 2010 22:48:09 +1100 From: system@loudthinking.com To: david@loudthinking.com + Message-ID: <4b5d84f9dd6a5_7380800b81ac29578@void.loudthinking.com.mail> Subject: [Signed up] Welcome david@loudthinking.com + Mime-Version: 1.0 + Content-Type: text/plain; + charset="US-ASCII"; + Content-Transfer-Encoding: 7bit Hello there, Mr. david@loudthinking.com -You never actually call the instance methods like signed_up directly. Instead, -you call class methods like deliver_* and create_* that are automatically -created for each instance method. So if the signed_up method sat on -ApplicationMailer, it would look like this: +In previous version of rails you would call create_method_name and +deliver_method_name. Rails 3.0 has a much simpler interface, you +simply call the method and optionally call +deliver+ on the return value. - ApplicationMailer.create_signed_up("david@loudthinking.com") # => tmail object for testing - ApplicationMailer.deliver_signed_up("david@loudthinking.com") # sends the email - ApplicationMailer.new.signed_up("david@loudthinking.com") # won't work! +Calling the method returns a Mail Message object: + + message = Notifier.welcome #=> Returns a Mail::Message object + message.deliver #=> delivers the email + +Or you can just chain the methods together like: + + Notifier.welcome.deliver # Creates the email and sends it immediately == Receiving emails @@ -103,16 +126,13 @@ The Base class has the full list of configuration options. Here's an example: Action Mailer requires that the Action Pack is either available to be required immediately or is accessible as a GEM. +Additionally, Action Mailer requires the Mail gem, http://github.com/mikel/mail == Bundled software -* tmail 0.10.8 by Minero Aoki released under LGPL - Read more on http://i.loveruby.net/en/prog/tmail.html - * Text::Format 0.63 by Austin Ziegler released under OpenSource Read more on http://www.halostatue.ca/ruby/Text__Format.html - == Download The latest version of Action Mailer can be found at diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 47d0ffaf687..51a8c07a283 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -16,46 +16,57 @@ module ActionMailer #:nodoc: # # $ script/generate mailer Notifier # - # The generated model inherits from ActionMailer::Base. Emails are defined by creating methods within the model which are then - # used to set variables to be used in the mail template, to change options on the mail, or - # to add attachments. + # The generated model inherits from ActionMailer::Base. Emails are defined by creating methods + # within the model which are then used to set variables to be used in the mail template, to + # change options on the mail, or to add attachments. # # Examples: # # class Notifier < ActionMailer::Base - # def signup_notification(recipient) - # recipients recipient.email_address_with_name - # bcc ["bcc@example.com", "Order Watcher "] - # from "system@example.com" - # subject "New account information" - # body :account => recipient + # delivers_from 'system@example.com' + # + # def welcome(recipient) + # @account = recipient + # mail { :to => recipient.email_address_with_name, + # :bcc => ["bcc@example.com", "Order Watcher "], + # :subject => "New account information" } + # end # end - # end - # - # Mailer methods have the following configuration methods available. - # - # * recipients - Takes one or more email addresses. These addresses are where your email will be delivered to. Sets the To: header. - # * subject - The subject of your email. Sets the Subject: header. - # * from - Who the email you are sending is from. Sets the From: header. - # * cc - Takes one or more email addresses. These addresses will receive a carbon copy of your email. Sets the Cc: header. - # * bcc - Takes one or more email addresses. These addresses will receive a blind carbon copy of your email. Sets the Bcc: header. - # * reply_to - Takes one or more email addresses. These addresses will be listed as the default recipients when replying to your email. Sets the Reply-To: header. - # * sent_on - The date on which the message was sent. If not set, the header will be set by the delivery agent. - # * content_type - Specify the content type of the message. Defaults to text/plain. - # * headers - Specify additional headers to be set for the message, e.g. headers 'X-Mail-Count' => 107370. - # - # When a headers 'return-path' is specified, that value will be used as the 'envelope from' - # address. Setting this is useful when you want delivery notifications sent to a different address than - # the one in from. + # + # Within the mailer method, you have access to the following methods: + # + # * attachments[]= - Allows you to add attachments to your email in an intuitive + # manner; attachments['filename.png'] = File.read('path/to/filename.png') + # * headers[]= - Allows you to specify non standard headers in your email such + # as headers['X-No-Spam'] = 'True' + # * mail - Allows you to specify your email to send. + # + # The hash passed to the mail method allows you to specify the most used headers in an email + # message, such as Subject, To, From, Cc, Bcc, + # Reply-To and Date. See the ActionMailer#mail method for more details. + # + # If you need other headers not listed above, use the headers['name'] = value method. # + # The mail method, if not passed a block, will inspect your views and send all the views with + # the same name as the method, so the above action would send the +welcome.plain.erb+ view file + # as well as the +welcome.html.erb+ view file in a +multipart/alternate+ email. + # + # If you want to explicitly render only certain templates, pass a block: + # + # mail(:to => user.emai) do |format| + # format.text + # format.enriched, {:content_type => 'text/rtf'} + # format.html + # end # # = Mailer views # - # 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 .erb file with the same name as the method - # in your mailer model. For example, in the mailer defined above, the template at - # app/views/notifier/signup_notification.erb would be used to generate the email. + # 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 .erb file with the same + # name as the method in your mailer model. For example, in the mailer defined above, the template at + # app/views/notifier/signup_notification.text.erb would be used to generate the email. # # Variables defined in the model are accessible as instance variables in the view. # @@ -111,54 +122,13 @@ module ActionMailer #:nodoc: # Once a mailer action and template are defined, you can deliver your message or create it and save it # for delivery later: # - # Notifier.deliver_signup_notification(david) # sends the email - # mail = Notifier.create_signup_notification(david) # => a tmail object - # Notifier.deliver(mail) + # Notifier.welcome(david).deliver # sends the email + # mail = Notifier.welcome(david) # => a Mail::Message object + # mail.deliver # sends the email # - # You never instantiate your mailer class. Rather, your delivery instance - # methods are automatically wrapped in class methods that start with the word - # deliver_ followed by the name of the mailer method that you would - # like to deliver. The signup_notification method defined above is - # delivered by invoking Notifier.deliver_signup_notification. + # You never instantiate your mailer class. Rather, you just call the method on the class itself. # - # - # = HTML email - # - # To send mail as HTML, make sure your view (the .erb file) generates HTML and - # set the content type to html. - # - # class MyMailer < ActionMailer::Base - # def signup_notification(recipient) - # recipients recipient.email_address_with_name - # subject "New account information" - # from "system@example.com" - # body :account => recipient - # content_type "text/html" - # end - # end - # - # - # = Multipart email - # - # You can explicitly specify multipart messages: - # - # class ApplicationMailer < ActionMailer::Base - # def signup_notification(recipient) - # recipients recipient.email_address_with_name - # subject "New account information" - # from "system@example.com" - # content_type "multipart/alternative" - # body :account => recipient - # - # part :content_type => "text/html", - # :data => render_message("signup-as-html") - # - # part "text/plain" do |p| - # p.body = render_message("signup-as-plain") - # p.content_transfer_encoding = "base64" - # end - # end - # end + # = Multipart Emails # # 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 @@ -170,11 +140,10 @@ module ActionMailer #:nodoc: # * signup_notification.text.xml.builder # * signup_notification.text.x-yaml.erb # - # Each would be rendered and added as a separate part to the message, - # with the corresponding content type. The content type for the entire - # message is automatically set to multipart/alternative, which indicates - # that the email contains multiple different representations of the same email - # body. The same body hash is passed to each template. + # Each would be rendered and added as a separate part to the message, with the corresponding content + # type. The content type for the entire message is automatically set to multipart/alternative, + # which indicates that the email contains multiple different representations of the same email + # body. The same instance variables defined in the action are passed to all email templates. # # Implicit template rendering is not performed if any attachments or parts have been added to the email. # This means that you'll have to manually add each part to the email and set the content type of the email @@ -182,31 +151,31 @@ module ActionMailer #:nodoc: # # = Attachments # - # Attachments can be added by using the +attachment+ method. - # - # Example: + # You can see above how to make a multipart HTML / Text email, to send attachments is just + # as easy: # # class ApplicationMailer < ActionMailer::Base - # # attachments - # def signup_notification(recipient) - # recipients recipient.email_address_with_name - # subject "New account information" - # from "system@example.com" - # - # attachment :content_type => "image/jpeg", - # :body => File.read("an-image.jpg") - # - # attachment "application/pdf" do |a| - # a.body = generate_your_pdf_here() - # end + # def welcome(recipient) + # attachments['free_book.pdf'] = { :data => File.read('path/to/file.pdf') } + # mail(:to => recipient, :subject => "New account information") # end # end + # + # Which will (if it had both a .text.erb and .html.erb tempalte in the view + # directory), send a complete multipart/mixed email with two parts, the first part being + # a multipart/alternate with the text and HTML email parts inside, and the second being + # a application/pdf with a Base64 encoded copy of the file.pdf book with the filename + # +free_book.pdf+. # # # = Configuration options # # These options are specified on the class level, like ActionMailer::Base.template_root = "/my/templates" # + # * delivers_from - Pass this the address that then defaults as the +from+ address on all the + # emails sent. Can be overridden on a per mail basis by passing :from => 'another@address' in + # the +mail+ method. + # # * template_root - Determines the base from which template references will be made. # # * logger - the logger is used for generating information on the mailing run if available. @@ -326,6 +295,9 @@ module ActionMailer #:nodoc: end end + # Delivers a mail object. This is actually called by the Mail::Message object + # itself through a call back when you call :deliver on the Mail::Message, + # calling +deliver_mail+ directly and passing an Mail::Message will do nothing. def deliver_mail(mail) #:nodoc: ActiveSupport::Notifications.instrument("action_mailer.deliver") do |payload| self.set_payload_for_mail(payload, mail) @@ -374,6 +346,14 @@ module ActionMailer #:nodoc: process(method_name, *args) if method_name end + # 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" + # + # The resulting Mail::Message will have the following in it's header: + # + # X-Special-Domain-Specific-Header: SecretValue def headers(args=nil) if args ActiveSupport::Deprecation.warn "headers(Hash) is deprecated, please do headers[key] = value instead", caller[0,2] @@ -383,10 +363,91 @@ module ActionMailer #:nodoc: end end + # Allows you to add attachments to an email, like so: + # + # 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. + # + # 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', + # :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: + # + # file_content = SpecialEncode(File.read('/path/to/filename.jpg')) + # mail.attachments['filename.jpg'] = {:mime_type => 'application/x-gzip', + # :encoding => 'SpecialEncoding', + # :content => file_content } + # + # You can also search for specific attachments: + # + # # By Filename + # mail.attachments['filename.jpg'] #=> Mail::Part object or nil + # + # # or by index + # mail.attachments[0] #=> Mail::Part (first attachment) + # def attachments @_message.attachments end + # The main method that creates the message and renders the email templates. There are + # two ways to call this method, with a block, or without a block. + # + # Both methods accept a headers hash. This hash allows you to specify the most used headers + # in an email message, these are: + # + # * :subject - The subject of the message, if this is omitted, ActionMailer will + # ask the Rails I18n class for a translated :subject in the scope of + # [:actionmailer, mailer_scope, action_name] or if this is missing, will translate the + # humanized version of the action_name + # * :to - Who the message is destined for, can be a string of addresses, or an array + # of addresses. + # * :from - Who the message is from, if missing, will use the :delivers_from + # value in the class (if it exists) + # * :cc - Who you would like to Carbon-Copy on this email, can be a string of addresses, + # or an array of addresses. + # * :bcc - Who you would like to Blind-Carbon-Copy on this email, can be a string of + # addresses, or an array of addresses. + # * :reply_to - Who to set the Reply-To header of the email to. + # * :date - The date to say the email was sent on. + # + # If you need other headers not listed above, use the headers['name'] = value method. + # + # When a :return_path is specified, that value will be used as the 'envelope from' + # address for the Mail message. Setting this is useful when you want delivery notifications + # sent to a different address than the one in :from. Mail will actually use the + # :return_path in preference to the :sender in preference to the :from + # field for the 'envelope from' value. + # + # If you do not pass a block to the +mail+ method, it will find all templates in the + # template path that match 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. + # + # If you do pass a block, you can render specific templates of your choice: + # + # mail(:to => 'mikel@test.lindsaar.net') do |format| + # format.text + # format.html + # end + # + # You can even render text directly without using a template: + # + # mail(:to => 'mikel@test.lindsaar.net') do |format| + # format.text { render :text => "Hello Mikel!" } + # format.html { render :text => "

Hello Mikel!

" } + # end + # + # Which will render a multipart/alternate email with text/plain and + # text/html parts. def mail(headers={}, &block) # Guard flag to prevent both the old and the new API from firing # Should be removed when old API is removed From f14390091c40149dcd0982ada097be5bcbb37003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Tue, 26 Jan 2010 00:09:18 +1100 Subject: [PATCH 59/63] We don't support enriched yet --- actionmailer/lib/action_mailer/base.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 51a8c07a283..6c70ce8998b 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -55,7 +55,6 @@ module ActionMailer #:nodoc: # # mail(:to => user.emai) do |format| # format.text - # format.enriched, {:content_type => 'text/rtf'} # format.html # end # From 1b3cb54ebae685d4db9eefc99ce68b36d5641751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Tue, 26 Jan 2010 01:06:48 +0100 Subject: [PATCH 60/63] More work on generated mailer templates. --- .../lib/generators/erb/mailer/mailer_generator.rb | 2 +- .../erb/mailer/templates/{view.erb => view.text.erb} | 0 .../lib/generators/rails/mailer/templates/mailer.rb | 4 ++-- .../test_unit/mailer/templates/functional_test.rb | 10 +++------- railties/test/generators/mailer_generator_test.rb | 11 +++++------ railties/test/generators/named_base_test.rb | 8 ++++++++ 6 files changed, 19 insertions(+), 16 deletions(-) rename railties/lib/generators/erb/mailer/templates/{view.erb => view.text.erb} (100%) diff --git a/railties/lib/generators/erb/mailer/mailer_generator.rb b/railties/lib/generators/erb/mailer/mailer_generator.rb index 4ec2f4c9f45..408c942cef0 100644 --- a/railties/lib/generators/erb/mailer/mailer_generator.rb +++ b/railties/lib/generators/erb/mailer/mailer_generator.rb @@ -12,7 +12,7 @@ module Erb def create_view_files actions.each do |action| @action, @path = action, File.join(file_path, action) - template "view.erb", File.join("app/views", "#{@path}.erb") + template "view.text.erb", File.join("app/views", "#{@path}.text.erb") end end end diff --git a/railties/lib/generators/erb/mailer/templates/view.erb b/railties/lib/generators/erb/mailer/templates/view.text.erb similarity index 100% rename from railties/lib/generators/erb/mailer/templates/view.erb rename to railties/lib/generators/erb/mailer/templates/view.text.erb diff --git a/railties/lib/generators/rails/mailer/templates/mailer.rb b/railties/lib/generators/rails/mailer/templates/mailer.rb index 5e7ef42370d..1685b736332 100644 --- a/railties/lib/generators/rails/mailer/templates/mailer.rb +++ b/railties/lib/generators/rails/mailer/templates/mailer.rb @@ -1,5 +1,5 @@ class <%= class_name %> < ActionMailer::Base - delivers_from "mail@<%= application_name %>.com" + delivers_from "from@example.com" <% for action in actions -%> # Subject can be set in your I18n file at config/locales/en.yml @@ -9,7 +9,7 @@ class <%= class_name %> < ActionMailer::Base # def <%= action %> @greeting = "Hi" - mail(:to => "") + mail(:to => "to@example.com") end <% end -%> end \ No newline at end of file diff --git a/railties/lib/generators/test_unit/mailer/templates/functional_test.rb b/railties/lib/generators/test_unit/mailer/templates/functional_test.rb index 2f694e431c5..fcebb401350 100644 --- a/railties/lib/generators/test_unit/mailer/templates/functional_test.rb +++ b/railties/lib/generators/test_unit/mailer/templates/functional_test.rb @@ -3,17 +3,13 @@ require 'test_helper' class <%= class_name %>Test < ActionMailer::TestCase <% for action in actions -%> test "<%= action %>" do - @actual = <%= class_name %>.<%= action %> - @expected.subject = <%= action.to_s.humanize.inspect %> + @expected.to = "to@example.com" + @expected.from = "from@example.com" @expected.body = read_fixture("<%= action %>") @expected.date = Time.now - assert_difference "<%= class_name %>.deliveries.size" do - @actual.deliver - end - - assert_equal @expected.encoded, @actual.encoded + assert_equal @expected, <%= class_name %>.<%= action %> end <% end -%> diff --git a/railties/test/generators/mailer_generator_test.rb b/railties/test/generators/mailer_generator_test.rb index 0203eb314c6..99ce53323eb 100644 --- a/railties/test/generators/mailer_generator_test.rb +++ b/railties/test/generators/mailer_generator_test.rb @@ -6,11 +6,10 @@ class MailerGeneratorTest < Rails::Generators::TestCase arguments %w(notifier foo bar) def test_mailer_skeleton_is_created - Rails.stubs(:application).returns(Object.new) run_generator assert_file "app/mailers/notifier.rb" do |mailer| assert_match /class Notifier < ActionMailer::Base/, mailer - assert_match /delivers_from "mail@object.com"/, mailer + assert_match /delivers_from "from@example.com"/, mailer end end @@ -36,12 +35,12 @@ class MailerGeneratorTest < Rails::Generators::TestCase def test_invokes_default_template_engine run_generator - assert_file "app/views/notifier/foo.erb" do |view| + assert_file "app/views/notifier/foo.text.erb" do |view| assert_match /app\/views\/notifier\/foo/, view assert_match /<%= @greeting %>/, view end - assert_file "app/views/notifier/bar.erb" do |view| + assert_file "app/views/notifier/bar.text.erb" do |view| assert_match /app\/views\/notifier\/bar/, view assert_match /<%= @greeting %>/, view end @@ -62,12 +61,12 @@ class MailerGeneratorTest < Rails::Generators::TestCase assert_file "app/mailers/notifier.rb" do |mailer| assert_instance_method :foo, mailer do |foo| - assert_match /mail\(:to => ""\)/, foo + assert_match /mail\(:to => "to@example.com"\)/, foo assert_match /@greeting = "Hi"/, foo end assert_instance_method :bar, mailer do |bar| - assert_match /mail\(:to => ""\)/, bar + assert_match /mail\(:to => "to@example.com"\)/, bar assert_match /@greeting = "Hi"/, bar end end diff --git a/railties/test/generators/named_base_test.rb b/railties/test/generators/named_base_test.rb index 7514bc32bdf..f327fb12822 100644 --- a/railties/test/generators/named_base_test.rb +++ b/railties/test/generators/named_base_test.rb @@ -67,6 +67,14 @@ class NamedBaseTest < Rails::Generators::TestCase assert_name g, 'admin.foos', :controller_i18n_scope end + def test_application_name + g = generator ['Admin::Foo'] + Rails.stubs(:application).returns(Object.new) + assert_name g, "object", :application_name + Rails.stubs(:application).returns(nil) + assert_name g, "application", :application_name + end + protected def assert_name(generator, value, method) From 6589976533b7a6850390ed5d6526ca719e56c5ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Tue, 26 Jan 2010 01:43:41 +0100 Subject: [PATCH 61/63] Remove old files, add some information to docs and improve test suite. --- actionmailer/README | 2 -- actionmailer/lib/action_mailer/base.rb | 48 ++++++++++++++++++-------- actionmailer/test/base_test.rb | 24 +++++++++++++ actionmailer/test/mail_test.rb | 22 ------------ 4 files changed, 58 insertions(+), 38 deletions(-) delete mode 100644 actionmailer/test/mail_test.rb diff --git a/actionmailer/README b/actionmailer/README index 542996f87bf..e0e2ee436a0 100644 --- a/actionmailer/README +++ b/actionmailer/README @@ -22,7 +22,6 @@ the email. This can be as simple as: class Notifier < ActionMailer::Base - delivers_from 'system@loudthinking.com' def welcome(recipient) @@ -30,7 +29,6 @@ This can be as simple as: mail(:to => recipient, :subject => "[Signed up] Welcome #{recipient}") end - end The body of the email is created by using an Action View template (regular diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 6c70ce8998b..65d1ba47d9d 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -27,9 +27,8 @@ module ActionMailer #:nodoc: # # def welcome(recipient) # @account = recipient - # mail { :to => recipient.email_address_with_name, - # :bcc => ["bcc@example.com", "Order Watcher "], - # :subject => "New account information" } + # mail(:to => recipient.email_address_with_name, + # :bcc => ["bcc@example.com", "Order Watcher "]) # end # end # @@ -37,13 +36,15 @@ module ActionMailer #:nodoc: # # * attachments[]= - Allows you to add attachments to your email in an intuitive # manner; attachments['filename.png'] = File.read('path/to/filename.png') + # # * headers[]= - Allows you to specify non standard headers in your email such # as headers['X-No-Spam'] = 'True' + # # * mail - Allows you to specify your email to send. # # The hash passed to the mail method allows you to specify the most used headers in an email # message, such as Subject, To, From, Cc, Bcc, - # Reply-To and Date. See the ActionMailer#mail method for more details. + # Reply-To and Date. See the ActionMailer#mail method for more details. # # If you need other headers not listed above, use the headers['name'] = value method. # @@ -58,6 +59,20 @@ module ActionMailer #:nodoc: # format.html # end # + # The block syntax is useful if also need to specify information specific to a part: + # + # mail(:to => user.emai) do |format| + # format.text(:content_transfer_encoding => "base64") + # format.html + # end + # + # Or even to renderize a special view: + # + # mail(:to => user.emai) do |format| + # format.text + # format.html { render "some_other_template" } + # end + # # = Mailer views # # Like Action Controller, each mailer class has a corresponding view directory in which each @@ -79,9 +94,9 @@ module ActionMailer #:nodoc: # You got a new note! # <%= truncate(@note.body, 25) %> # - # If you need to access the subject, from or the recipients in the view, you can do that through mailer object: + # If you need to access the subject, from or the recipients in the view, you can do that through message object: # - # You got a new note from <%= mailer.from %>! + # You got a new note from <%= message.from %>! # <%= truncate(@note.body, 25) %> # # @@ -137,7 +152,7 @@ module ActionMailer #:nodoc: # * signup_notification.text.plain.erb # * signup_notification.text.html.erb # * signup_notification.text.xml.builder - # * signup_notification.text.x-yaml.erb + # * signup_notification.text.yaml.erb # # Each would be rendered and added as a separate part to the message, with the corresponding content # type. The content type for the entire message is automatically set to multipart/alternative, @@ -174,8 +189,6 @@ module ActionMailer #:nodoc: # * delivers_from - Pass this the address that then defaults as the +from+ address on all the # emails sent. Can be overridden on a per mail basis by passing :from => 'another@address' in # the +mail+ method. - # - # * template_root - Determines the base from which template references will be made. # # * 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. @@ -300,9 +313,7 @@ module ActionMailer #:nodoc: def deliver_mail(mail) #:nodoc: ActiveSupport::Notifications.instrument("action_mailer.deliver") do |payload| self.set_payload_for_mail(payload, mail) - yield # Let Mail do the delivery actions - end end @@ -399,7 +410,7 @@ module ActionMailer #:nodoc: # The main method that creates the message and renders the email templates. There are # two ways to call this method, with a block, or without a block. # - # Both methods accept a headers hash. This hash allows you to specify the most used headers + # Both methods accept a headers hash. This hash allows you to specify the most used headers # in an email message, these are: # # * :subject - The subject of the message, if this is omitted, ActionMailer will @@ -419,7 +430,7 @@ module ActionMailer #:nodoc: # # If you need other headers not listed above, use the headers['name'] = value method. # - # When a :return_path is specified, that value will be used as the 'envelope from' + # When a :return_path is specified as header, that value will be used as the 'envelope from' # address for the Mail message. Setting this is useful when you want delivery notifications # sent to a different address than the one in :from. Mail will actually use the # :return_path in preference to the :sender in preference to the :from @@ -447,6 +458,14 @@ module ActionMailer #:nodoc: # # Which will render a multipart/alternate email with text/plain and # text/html parts. + # + # The block syntax also allows you to customize the part headers if desired: + # + # mail(:to => 'mikel@test.lindsaar.net') do |format| + # format.text(:content_transfer_encoding => "base64") + # format.html + # end + # def mail(headers={}, &block) # Guard flag to prevent both the old and the new API from firing # Should be removed when old API is removed @@ -541,7 +560,8 @@ module ActionMailer #:nodoc: def create_parts_from_responses(m, responses, charset) #:nodoc: if responses.size == 1 && !m.has_attachments? - m.body = responses[0][:body] + headers = responses[0] + headers.each { |k,v| m[k] = v } return responses[0][:content_type] elsif responses.size > 1 && m.has_attachments? container = Mail::Part.new diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 0705f22df8e..856b5b2d3c2 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -56,6 +56,13 @@ class BaseTest < ActiveSupport::TestCase format.any(:text, :html){ render :text => "Format with any!" } end end + + def custom_block(include_html=false) + mail(DEFAULT_HEADERS) do |format| + format.text(:content_transfer_encoding => "base64"){ render "welcome" } + format.html{ render "welcome" } if include_html + end + end end test "method call to mail does not raise error" do @@ -337,6 +344,23 @@ class BaseTest < ActiveSupport::TestCase assert_equal("Format with any!", email.parts[1].body.encoded) end + test "explicit multipart with options" do + email = BaseMailer.custom_block(true).deliver + assert_equal(2, email.parts.size) + assert_equal("multipart/alternate", email.mime_type) + assert_equal("text/plain", email.parts[0].mime_type) + assert_equal("base64", email.parts[0].content_transfer_encoding) + assert_equal("text/html", email.parts[1].mime_type) + assert_equal("7bit", email.parts[1].content_transfer_encoding) + end + + test "explicit multipart with one part is rendered as body" do + email = BaseMailer.custom_block.deliver + assert_equal(0, email.parts.size) + assert_equal("text/plain", email.mime_type) + assert_equal("base64", email.content_transfer_encoding) + end + # Class level API with method missing test "should respond to action methods" do assert BaseMailer.respond_to?(:welcome) diff --git a/actionmailer/test/mail_test.rb b/actionmailer/test/mail_test.rb deleted file mode 100644 index f18dfdc1566..00000000000 --- a/actionmailer/test/mail_test.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'abstract_unit' - -class MailTest < Test::Unit::TestCase - def test_body - m = Mail.new - expected = 'something_with_underscores' - m.content_transfer_encoding = 'quoted-printable' - quoted_body = [expected].pack('*M') - m.body = quoted_body - assert_equal "something_with_underscores=\r\n", m.body.encoded - assert_equal expected, m.body.to_s - end - - def test_nested_attachments_are_recognized_correctly - fixture = File.read("#{File.dirname(__FILE__)}/fixtures/raw_email_with_nested_attachment") - mail = Mail.new(fixture) - assert_equal 2, mail.attachments.length - assert_equal "image/png", mail.attachments.first.mime_type - assert_equal 1902, mail.attachments.first.decoded.length - assert_equal "application/pkcs7-signature", mail.attachments.last.mime_type - end -end From 74a5889abef1212d373ea994f1c93daedee8932c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Tue, 26 Jan 2010 11:49:32 +1100 Subject: [PATCH 62/63] Refactor content type setting, added tests to ensure boundary exists on multipart and fixed typo --- actionmailer/lib/action_mailer/base.rb | 31 ++++++++++++++++++-------- actionmailer/test/base_test.rb | 23 +++++++++++-------- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 6c70ce8998b..782e9d2c46a 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -49,7 +49,7 @@ module ActionMailer #:nodoc: # # The mail method, if not passed a block, will inspect your views and send all the views with # the same name as the method, so the above action would send the +welcome.plain.erb+ view file - # as well as the +welcome.html.erb+ view file in a +multipart/alternate+ email. + # as well as the +welcome.html.erb+ view file in a +multipart/alternative+ email. # # If you want to explicitly render only certain templates, pass a block: # @@ -162,7 +162,7 @@ module ActionMailer #:nodoc: # # Which will (if it had both a .text.erb and .html.erb tempalte in the view # directory), send a complete multipart/mixed email with two parts, the first part being - # a multipart/alternate with the text and HTML email parts inside, and the second being + # a multipart/alternative with the text and HTML email parts inside, and the second being # a application/pdf with a Base64 encoded copy of the file.pdf book with the filename # +free_book.pdf+. # @@ -445,7 +445,7 @@ module ActionMailer #:nodoc: # format.html { render :text => "

Hello Mikel!

" } # end # - # Which will render a multipart/alternate email with text/plain and + # Which will render a multipart/alternative email with text/plain and # text/html parts. def mail(headers={}, &block) # Guard flag to prevent both the old and the new API from firing @@ -465,10 +465,11 @@ module ActionMailer #:nodoc: # Render the templates and blocks responses, sort_order = collect_responses_and_sort_order(headers, &block) - content_type ||= create_parts_from_responses(m, responses, charset) + + create_parts_from_responses(m, responses, charset) # Tidy up content type, charset, mime version and sort order - m.content_type = content_type + m.content_type = set_content_type(m, content_type) m.charset = charset m.mime_version = mime_version sort_order = headers[:parts_order] || sort_order || self.class.default_implicit_parts_order.dup @@ -485,6 +486,20 @@ module ActionMailer #:nodoc: protected + def set_content_type(m, user_content_type) + params = m.content_type_parameters || {} + case + when user_content_type.present? + user_content_type + when m.has_attachments? + ["multipart", "mixed", params] + when m.multipart? + ["multipart", "alternative", params] + else + self.class.default_content_type.dup + end + end + def default_subject #:nodoc: mailer_scope = self.class.mailer_name.gsub('/', '.') I18n.t(:subject, :scope => [:actionmailer, mailer_scope, action_name], :default => action_name.humanize) @@ -543,16 +558,14 @@ module ActionMailer #:nodoc: if responses.size == 1 && !m.has_attachments? m.body = responses[0][:body] return responses[0][:content_type] - elsif responses.size > 1 && m.has_attachments? + elsif responses.size > 1 && m.has_attachments? container = Mail::Part.new - container.content_type = "multipart/alternate" + container.content_type = "multipart/alternative" responses.each { |r| insert_part(container, r, charset) } m.add_part(container) else responses.each { |r| insert_part(m, r, charset) } end - - m.has_attachments? ? "multipart/mixed" : "multipart/alternate" end def insert_part(container, response, charset) #:nodoc: diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 0705f22df8e..14feef5a008 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -200,7 +200,7 @@ class BaseTest < ActiveSupport::TestCase test "implicit multipart" do email = BaseMailer.implicit_multipart.deliver assert_equal(2, email.parts.size) - assert_equal("multipart/alternate", email.mime_type) + assert_equal("multipart/alternative", email.mime_type) assert_equal("text/plain", email.parts[0].mime_type) assert_equal("TEXT Implicit Multipart", email.parts[0].body.encoded) assert_equal("text/html", email.parts[1].mime_type) @@ -223,7 +223,7 @@ class BaseTest < ActiveSupport::TestCase test "implicit multipart with attachments creates nested parts" do email = BaseMailer.implicit_multipart(:attachments => true).deliver assert_equal("application/pdf", email.parts[0].mime_type) - assert_equal("multipart/alternate", email.parts[1].mime_type) + assert_equal("multipart/alternative", email.parts[1].mime_type) assert_equal("text/plain", email.parts[1].parts[0].mime_type) assert_equal("TEXT Implicit Multipart", email.parts[1].parts[0].body.encoded) assert_equal("text/html", email.parts[1].parts[1].mime_type) @@ -235,7 +235,7 @@ class BaseTest < ActiveSupport::TestCase swap BaseMailer, :default_implicit_parts_order => order do email = BaseMailer.implicit_multipart(:attachments => true).deliver assert_equal("application/pdf", email.parts[0].mime_type) - assert_equal("multipart/alternate", email.parts[1].mime_type) + assert_equal("multipart/alternative", email.parts[1].mime_type) assert_equal("text/plain", email.parts[1].parts[1].mime_type) assert_equal("text/html", email.parts[1].parts[0].mime_type) end @@ -244,7 +244,7 @@ class BaseTest < ActiveSupport::TestCase test "implicit multipart with default locale" do email = BaseMailer.implicit_with_locale.deliver assert_equal(2, email.parts.size) - assert_equal("multipart/alternate", email.mime_type) + assert_equal("multipart/alternative", email.mime_type) assert_equal("text/plain", email.parts[0].mime_type) assert_equal("Implicit with locale TEXT", email.parts[0].body.encoded) assert_equal("text/html", email.parts[1].mime_type) @@ -255,7 +255,7 @@ class BaseTest < ActiveSupport::TestCase swap I18n, :locale => :pl do email = BaseMailer.implicit_with_locale.deliver assert_equal(2, email.parts.size) - assert_equal("multipart/alternate", email.mime_type) + assert_equal("multipart/alternative", email.mime_type) assert_equal("text/plain", email.parts[0].mime_type) assert_equal("Implicit with locale PL TEXT", email.parts[0].body.encoded) assert_equal("text/html", email.parts[1].mime_type) @@ -287,7 +287,7 @@ class BaseTest < ActiveSupport::TestCase test "explicit multipart" do email = BaseMailer.explicit_multipart.deliver assert_equal(2, email.parts.size) - assert_equal("multipart/alternate", email.mime_type) + assert_equal("multipart/alternative", email.mime_type) assert_equal("text/plain", email.parts[0].mime_type) assert_equal("TEXT Explicit Multipart", email.parts[0].body.encoded) assert_equal("text/html", email.parts[1].mime_type) @@ -310,7 +310,7 @@ class BaseTest < ActiveSupport::TestCase test "explicit multipart with attachments creates nested parts" do email = BaseMailer.explicit_multipart(:attachments => true).deliver assert_equal("application/pdf", email.parts[0].mime_type) - assert_equal("multipart/alternate", email.parts[1].mime_type) + assert_equal("multipart/alternative", email.parts[1].mime_type) assert_equal("text/plain", email.parts[1].parts[0].mime_type) assert_equal("TEXT Explicit Multipart", email.parts[1].parts[0].body.encoded) assert_equal("text/html", email.parts[1].parts[1].mime_type) @@ -320,7 +320,7 @@ class BaseTest < ActiveSupport::TestCase test "explicit multipart with templates" do email = BaseMailer.explicit_multipart_templates.deliver assert_equal(2, email.parts.size) - assert_equal("multipart/alternate", email.mime_type) + assert_equal("multipart/alternative", email.mime_type) assert_equal("text/html", email.parts[0].mime_type) assert_equal("HTML Explicit Multipart Templates", email.parts[0].body.encoded) assert_equal("text/plain", email.parts[1].mime_type) @@ -330,7 +330,7 @@ class BaseTest < ActiveSupport::TestCase test "explicit multipart with any" do email = BaseMailer.explicit_multipart_with_any.deliver assert_equal(2, email.parts.size) - assert_equal("multipart/alternate", email.mime_type) + assert_equal("multipart/alternative", email.mime_type) assert_equal("text/plain", email.parts[0].mime_type) assert_equal("Format with any!", email.parts[0].body.encoded) assert_equal("text/html", email.parts[1].mime_type) @@ -370,6 +370,11 @@ class BaseTest < ActiveSupport::TestCase BaseMailer.expects(:welcome).returns(mail) BaseMailer.welcome.deliver end + + test "explicit multipart should be multipart" do + mail = BaseMailer.explicit_multipart + assert_not_nil(mail.content_type_parameters[:boundary]) + end protected From c02391f8f97182e818d22a0f0ec4a5589d2fff15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim=20and=20Mikel=20Lindsaar?= Date: Tue, 26 Jan 2010 01:54:23 +0100 Subject: [PATCH 63/63] Fix small typo. --- actionmailer/test/base_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 83707137e14..4b8427fb926 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -347,7 +347,7 @@ class BaseTest < ActiveSupport::TestCase test "explicit multipart with options" do email = BaseMailer.custom_block(true).deliver assert_equal(2, email.parts.size) - assert_equal("multipart/alternate", email.mime_type) + assert_equal("multipart/alternative", email.mime_type) assert_equal("text/plain", email.parts[0].mime_type) assert_equal("base64", email.parts[0].content_transfer_encoding) assert_equal("text/html", email.parts[1].mime_type)