diff options
18 files changed, 468 insertions, 437 deletions
diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec index 96549bf29c..cc1c25401e 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.1') s.files = Dir['CHANGELOG', 'README', 'MIT-LICENSE', 'lib/**/*'] s.has_rdoc = true diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 55ddbb24f4..1765aee9cc 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -32,8 +32,9 @@ module ActionMailer autoload :AdvAttrAccessor autoload :Base - autoload :DeliveryMethod + 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 be6b1feca9..91992ed839 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 356861b591..39ddafe7fe 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' @@ -263,6 +264,9 @@ module ActionMailer #:nodoc: helper ActionMailer::MailHelper include ActionMailer::DeprecatedBody + include ActionMailer::DeprecatedApi + + include ActionMailer::DeliveryMethods private_class_method :new #:nodoc: @@ -293,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 @@ -348,27 +352,23 @@ 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 + # 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 - 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) - end + alias :controller_path :mailer_name def respond_to?(method_symbol, include_private = false) #:nodoc: matches_dynamic_method?(method_symbol) || super @@ -377,7 +377,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 @@ -413,7 +413,27 @@ 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 + + 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 @@ -444,52 +464,29 @@ module ActionMailer #:nodoc: 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. - 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 + 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 - # 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 + 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.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 @@ -498,6 +495,8 @@ 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 @@ -505,160 +504,29 @@ 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 <tt>create!</tt> 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 - - ActiveSupport::Notifications.instrument("action_mailer.deliver", - :template => template, :mailer => self.class.name) do |payload| - - self.class.set_payload_for_mail(payload, mail) - - begin - self.delivery_method.perform_delivery(mail) if perform_deliveries - rescue Exception => e # Net::SMTP errors or sendmail pipe errors - raise e if raise_delivery_errors - end - end - - mail + def deliver!(mail = @message) + 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!' %>" - 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 - @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/delivery_method.rb b/actionmailer/lib/action_mailer/delivery_method.rb deleted file mode 100644 index 4f7d3afc3c..0000000000 --- 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 571e32df49..0000000000 --- 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 db55af79f1..0000000000 --- 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 af30c498b5..0000000000 --- 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 6e3239d52a..0000000000 --- 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 diff --git a/actionmailer/lib/action_mailer/delivery_methods.rb b/actionmailer/lib/action_mailer/delivery_methods.rb new file mode 100644 index 0000000000..c8c4148353 --- /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_api.rb b/actionmailer/lib/action_mailer/deprecated_api.rb new file mode 100644 index 0000000000..e11dd4c46a --- /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 = @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? + 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? + + @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 + # 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/lib/action_mailer/deprecated_body.rb b/actionmailer/lib/action_mailer/deprecated_body.rb index 5379b33a54..c82610014f 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/abstract_unit.rb b/actionmailer/test/abstract_unit.rb index 50b8a53006..1fc5ab85e0 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/base_test.rb b/actionmailer/test/base_test.rb new file mode 100644 index 0000000000..b8d2e46241 --- /dev/null +++ b/actionmailer/test/base_test.rb @@ -0,0 +1,99 @@ +# encoding: utf-8 +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 < 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 + 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_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_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 + + # def test_that_class_defaults_are_set_on_instantiation + # pending + # end + # + # def test_should_set_the_subject_from_i18n + # pending + # end + +end
\ No newline at end of file diff --git a/actionmailer/test/delivery_method_test.rb b/actionmailer/test/delivery_method_test.rb index 8f8c6b0275..1e7408d6d6 100644 --- a/actionmailer/test/delivery_method_test.rb +++ b/actionmailer/test/delivery_method_test.rb @@ -1,101 +1,73 @@ 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 - attr_accessor :custom_deliveries - def initialize() - @customer_deliveries = [] - end - - def self.perform_delivery(mail) - self.custom_deliveries << mail - end -end - -class CustomerDeliveryMailer < ActionMailer::Base - self.delivery_method = CustomDeliveryMethod.new -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 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 -end -class DefaultDeliveryMethodMailer_delivery_method_Test < Test::Unit::TestCase - def setup - set_delivery_method :smtp - end - - def teardown - restore_delivery_method + 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 - - def test_should_be_the_default_smtp - assert_instance_of ActionMailer::DeliveryMethod::Smtp, DefaultDeliveryMethodMailer.delivery_method - end -end -class NonDefaultDeliveryMethodMailer_delivery_method_Test < Test::Unit::TestCase - def setup - set_delivery_method :smtp - end - - def teardown - restore_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_be_the_set_delivery_method - assert_instance_of ActionMailer::DeliveryMethod::Sendmail, NonDefaultDeliveryMethodMailer.delivery_method + def test_should_have_default_sendmail_delivery_method_settings + settings = {:location => '/usr/sbin/sendmail', + :arguments => '-i -t'} + 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 - end - - def test_should_be_the_set_delivery_method - assert_instance_of ActionMailer::DeliveryMethod::File, FileDeliveryMethodMailer.delivery_method + ActionMailer::Base.delivery_methods.delete(:custom) + ActionMailer::Base.delivery_settings.delete(:custom) end - def test_should_default_location_to_the_tmpdir - assert_equal "#{Dir.tmpdir}/mails", ActionMailer::Base.file_settings[:location] + def test_allow_to_add_a_custom_delivery_method + ActionMailer::Base.delivery_method = :custom + assert_equal :custom, ActionMailer::Base.delivery_method end -end -class CustomDeliveryMethodMailer_delivery_method_Test < Test::Unit::TestCase - def setup - set_delivery_method :smtp + def test_allow_to_customize_custom_settings + ActionMailer::Base.custom_settings = { :foo => :bar } + assert_equal Hash[:foo => :bar], ActionMailer::Base.custom_settings end - def teardown - restore_delivery_method + 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_be_the_set_delivery_method - assert_instance_of CustomDeliveryMethod, CustomerDeliveryMailer.delivery_method + def test_should_not_respond_for_invalid_method_settings + assert_raise NoMethodError do + ActionMailer::Base.another_settings + end end end diff --git a/actionmailer/test/mail_layout_test.rb b/actionmailer/test/mail_layout_test.rb index 0877e7b2cb..b9ff075461 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" diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index f87d9b2e5b..396dd00a91 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -545,7 +545,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 @@ -674,15 +674,12 @@ 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) + 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 @@ -1072,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 @@ -1093,7 +1085,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 @@ -1101,7 +1093,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 @@ -1109,13 +1101,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 - MockSMTP.any_instance.expects(:enable_starttls_auto).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 diff --git a/actionmailer/test/test_helper_test.rb b/actionmailer/test/test_helper_test.rb index 1fed26f78f..48e4433e98 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 |