Skip to content

Commit

Permalink
Explicit multipart messages respect :parts_order
Browse files Browse the repository at this point in the history
As issue #7978, the order in which ActionMailer
sends multipart messages could be unintentionally
overwritten if a block is passed to the mail
method. This changes the mail method such that
:parts_order is always respected, regardless of
whether a block is passed to mail.
  • Loading branch information
nateberkopec committed Nov 19, 2012
1 parent 3b99653 commit 9cf33b5
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 46 deletions.
3 changes: 3 additions & 0 deletions actionmailer/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## Rails 4.0.0 (unreleased) ##

* Explicit multipart messages no longer set the order of the MIME parts.
*Nate Berkopec*

* Do not render views when mail() isn't called.
Fix #7761

Expand Down
14 changes: 6 additions & 8 deletions actionmailer/lib/action_mailer/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -698,16 +698,15 @@ def mail(headers={}, &block)
assignable.each { |k, v| m[k] = v }

# Render the templates and blocks
responses, explicit_order = collect_responses_and_parts_order(headers, &block)
responses = collect_responses(headers, &block)
create_parts_from_responses(m, responses)

# Setup content type, reapply charset and handle parts order
m.content_type = set_content_type(m, content_type, headers[:content_type])
m.charset = charset

if m.multipart?
parts_order ||= explicit_order || headers[:parts_order]
m.body.set_sort_order(parts_order)
m.body.set_sort_order(headers[:parts_order])
m.body.sort_parts!
end

Expand Down Expand Up @@ -742,14 +741,13 @@ def default_i18n_subject #:nodoc:
I18n.t(:subject, scope: [mailer_scope, action_name], default: action_name.humanize)
end

def collect_responses_and_parts_order(headers) #:nodoc:
responses, parts_order = [], nil
def collect_responses(headers) #:nodoc:
responses = []

if block_given?
collector = ActionMailer::Collector.new(lookup_context) { render(action_name) }
yield(collector)
parts_order = collector.responses.map { |r| r[:content_type] }
responses = collector.responses
responses = collector.responses
elsif headers[:body]
responses << {
body: headers.delete(:body),
Expand All @@ -769,7 +767,7 @@ def collect_responses_and_parts_order(headers) #:nodoc:
end
end

[responses, parts_order]
responses
end

def each_template(paths, name, &block) #:nodoc:
Expand Down
42 changes: 21 additions & 21 deletions actionmailer/test/base_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -322,19 +322,6 @@ def teardown
assert_not_nil(mail.content_type_parameters[:boundary])
end

test "explicit multipart does not sort order" do
order = ["text/html", "text/plain"]
with_default BaseMailer, parts_order: order do
email = BaseMailer.explicit_multipart
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("text/html", email.parts[1].mime_type)

email = BaseMailer.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.explicit_multipart(attachments: true)
assert_equal("application/pdf", email.parts[0].mime_type)
Expand All @@ -349,10 +336,10 @@ def teardown
email = BaseMailer.explicit_multipart_templates
assert_equal(2, email.parts.size)
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)
assert_equal("TEXT Explicit Multipart Templates", email.parts[1].body.encoded)
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("TEXT Explicit Multipart Templates", email.parts[0].body.encoded)
assert_equal("text/html", email.parts[1].mime_type)
assert_equal("HTML Explicit Multipart Templates", email.parts[1].body.encoded)
end

test "explicit multipart with format.any" do
Expand Down Expand Up @@ -387,10 +374,23 @@ def teardown
email = BaseMailer.explicit_multipart_with_one_template
assert_equal(2, email.parts.size)
assert_equal("multipart/alternative", email.mime_type)
assert_equal("text/html", email.parts[0].mime_type)
assert_equal("[:html]", email.parts[0].body.encoded)
assert_equal("text/plain", email.parts[1].mime_type)
assert_equal("[:text]", email.parts[1].body.encoded)
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("[:text]", email.parts[0].body.encoded)
assert_equal("text/html", email.parts[1].mime_type)
assert_equal("[:html]", email.parts[1].body.encoded)
end

test "explicit multipart with sort order" do
order = ["text/html", "text/plain"]
with_default BaseMailer, parts_order: order do
email = BaseMailer.explicit_multipart
assert_equal("text/html", email.parts[0].mime_type)
assert_equal("text/plain", email.parts[1].mime_type)

email = BaseMailer.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

# Class level API with method missing
Expand Down
18 changes: 1 addition & 17 deletions guides/source/action_mailer_basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -377,23 +377,7 @@ If you use this setting, you should pass the `only_path: false` option when usin
Action Mailer will automatically send multipart emails if you have different templates for the same action. So, for our UserMailer example, if you have `welcome_email.text.erb` and `welcome_email.html.erb` in `app/views/user_mailer`, Action Mailer will automatically send a multipart email with the HTML and text versions setup as different parts.
The order of the parts getting inserted is determined by the `:parts_order` inside of the `ActionMailer::Base.default` method. If you want to explicitly alter the order, you can either change the `:parts_order` or explicitly render the parts in a different order:
```ruby
class UserMailer < ActionMailer::Base
def welcome_email(user)
@user = user
@url = user_url(@user)
mail(to: user.email,
subject: 'Welcome to My Awesome Site') do |format|
format.html
format.text
end
end
end
```
Will put the HTML part first, and the plain text part second.
The order of the parts getting inserted is determined by the `:parts_order` inside of the `ActionMailer::Base.default` method.
### Sending Emails with Attachments
Expand Down

0 comments on commit 9cf33b5

Please sign in to comment.