Permalink
Browse files

Make implicit and explicit templates pass through the same part creat…

…ion process.
  • Loading branch information...
1 parent 5a19d24 commit 6ba944608e527b8d7fc564a6e450e2d4c4355ddb José Valim and Mikel Lindsaar committed Jan 23, 2010
Showing with 104 additions and 84 deletions.
  1. +64 −62 actionmailer/lib/action_mailer/base.rb
  2. +15 −10 actionmailer/lib/action_mailer/collector.rb
  3. +25 −12 actionmailer/test/base_test.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,105 +391,107 @@ def set_payload_for_mail(payload, mail) #:nodoc:
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"
+ # method, for instance).
+ def initialize(method_name=nil, *args)
+ super()
+ @message = Mail.new
+ process(method_name, *args) if method_name
+ 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 = @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
# 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
+ m, sort_parts = @message, true
- # Give preference to headers and fallbacks to the ones set in mail
+ # 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
- 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]
+ headers[:subject] ||= default_subject
+ quote_fields(m, headers, charset)
- if headers[:body]
- templates = [ActionView::Template::Text.new(headers[:body], format_for_text)]
+ responses = if headers[:body]
+ [ { :body => headers[:body], :content_type => self.class.default_content_type.dup } ]
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
-
+ 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)
- 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) }
+
+ templates.map do |template|
+ { :body => render_to_body(:_template => template),
+ :content_type => template.mime_type.to_s }
end
end
- content_type ||= (m.has_attachments? ? "multipart/mixed" : "multipart/alternate")
+ content_type ||= create_parts_from_responses(m, responses, charset)
- # 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.content_type = content_type
m.charset = charset
m.mime_version = mime_version
- if m.parts.present? && templates
+ 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
- def default_subject
+ 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 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)
+ 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
- # 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
- process(method_name, *args) if method_name
+ 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
- # 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 = @message)
- self.class.deliver(mail)
+ def insert_part(container, response, charset) #:nodoc:
+ response[:charset] ||= charset
+ part = Mail::Part.new(response)
+ container.add_part(part)
end
end
@@ -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 @@ def custom(mime, options={}, &block)
@responses << options
@context.formats = @default_formats
end
-
end
end
@@ -54,9 +54,9 @@ def welcome(hash = {})
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 @@ def implicit_multipart(hash = {})
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 @@ def explicit_multipart(hash = {})
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 @@ def explicit_multipart(hash = {})
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 @@ def explicit_multipart(hash = {})
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

0 comments on commit 6ba9446

Please sign in to comment.