A very thorough refactoring, resulting in new mail property setters and support for attachments and multipart messages.

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1359 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
Jamis Buck
2005-05-29 16:36:22 +00:00
parent 79d9794f09
commit 359caef33c
12 changed files with 558 additions and 171 deletions

View File

@@ -1,5 +1,9 @@
*SVN*
* Support attachments and multipart messages.
* Added new accessors for the various mail properties.
* Fix to only perform the charset conversion if a 'from' and a 'to' charset are given (make no assumptions about what the charset was) #1276 [Jamis Buck]
* Fix attachments and content-type problems #1276 [Jamis Buck]
@@ -117,4 +121,4 @@
*0.3*
* First release
* First release

View File

@@ -36,11 +36,13 @@ $:.unshift(File.dirname(__FILE__) + "/action_mailer/vendor/")
require 'action_mailer/base'
require 'action_mailer/mail_helper'
require 'action_mailer/quoting'
require 'action_mailer/vendor/tmail'
require 'net/smtp'
ActionView::Base.class_eval { include MailHelper }
ActionMailer::Base.class_eval { include ActionMailer::Quoting }
old_verbose, $VERBOSE = $VERBOSE, nil
TMail::Encoder.const_set("MAX_LINE_LEN", 200)
$VERBOSE = old_verbose
$VERBOSE = old_verbose

View File

@@ -0,0 +1,58 @@
module ActionMailer
module AdvAttrAccessor
def self.append_features(base)
super
base.extend(ClassMethods)
end
module ClassMethods
def adv_attr_accessor(*names)
names.each do |name|
define_method("#{name}=") do |value|
instance_variable_set("@#{name}", value)
end
define_method(name) do |*parameters|
raise ArgumentError, "expected 0 or 1 parameters" unless parameters.length <= 1
if parameters.empty?
instance_variable_get("@#{name}")
else
instance_variable_set("@#{name}", parameters.first)
end
end
end
end
end
end
end
module ActionMailer
module AdvAttrAccessor
def self.append_features(base)
super
base.extend(ClassMethods)
end
module ClassMethods
def adv_attr_accessor(*names)
names.each do |name|
define_method("#{name}=") do |value|
instance_variable_set("@#{name}", value)
end
define_method(name) do |*parameters|
raise ArgumentError, "expected 0 or 1 parameters" unless parameters.length <= 1
if parameters.empty?
instance_variable_get("@#{name}")
else
instance_variable_set("@#{name}", parameters.first)
end
end
end
end
end
end
end

View File

@@ -1,23 +1,70 @@
require 'action_mailer/adv_attr_accessor'
require 'action_mailer/part'
module ActionMailer #:nodoc:
# Usage:
#
# class ApplicationMailer < ActionMailer::Base
# def post_notification(recipients, post)
# @recipients = recipients
# @from = post.author.email_address_with_name
# @headers["bcc"] = SYSTEM_ADMINISTRATOR_EMAIL
# @headers["reply-to"] = "notifications@example.com"
# @subject = "[#{post.account.name} #{post.title}]"
# @body["post"] = post
# # Set up properties
# # (Properties can also be specified via accessor methods
# # i.e. self.subject = "foo") and instance variables (@subject = "foo").
# def signup_notification(recipient)
# recipients recipient.email_address_with_name
# subject "New account information"
# body Hash.new("account" => recipient)
# from "system@example.com"
# end
#
# def comment_notification(recipient, comment)
# @recipients = recipient.email_address_with_name
# @subject = "[#{comment.post.project.client.firm.account.name}]" +
# " Re: #{comment.post.title}"
# @body["comment"] = comment
# @from = comment.author.email_address_with_name
# @sent_on = comment.posted_on
#
# # explicitly specify multipart messages
# def signup_notification(recipient)
# recipients recipient.email_address_with_name
# subject "New account information"
# from "system@example.com"
#
# part :content_type => "text/html",
# :body => render_message("signup-as-html", :account => recipient)
#
# part "text/plain" do |p|
# p.body = render_message("signup-as-plain", :account => recipient)
# p.transfer_encoding = "base64"
# end
# end
#
# # 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
# end
#
# # implicitly multipart messages
# def signup_notification(recipient)
# recipients recipient.email_address_with_name
# subject "New account information"
# from "system@example.com"
# body(:account => "recipient")
#
# # ActionMailer will automatically detect and use multipart templates,
# # where each template is named after the name of the action, followed
# # by the content type. Each such detected template will be added as
# # a separate part to the message.
# #
# # for example, if the following templates existed:
# # * signup_notification.text.plain.rhtml
# # * signup_notification.text.html.rhtml
# # * signup_notification.text.xml.rxml
# # * signup_notification.text.x-yaml.rhtml
# #
# # Each would be rendered and added as a separate part to the message,
# # with the corresponding content type. The same body hash is passed to
# # each template.
# end
# end
#
@@ -57,8 +104,10 @@ module ActionMailer #:nodoc:
# for unit and functional testing.
#
# * <tt>default_charset</tt> - The default charset used for the body and to encode the subject. Defaults to UTF-8. You can also
# pick a different charset from inside a method with <tt>@encoding</tt>.
# pick a different charset from inside a method with <tt>@charset</tt>.
class Base
include ActionMailer::AdvAttrAccessor
private_class_method :new #:nodoc:
cattr_accessor :template_root
@@ -89,96 +138,169 @@ module ActionMailer #:nodoc:
@@default_charset = "utf-8"
cattr_accessor :default_charset
attr_accessor :recipients, :subject, :body, :from, :sent_on, :headers, :bcc, :cc, :charset
adv_attr_accessor :recipients, :subject, :body, :from, :sent_on, :headers,
:bcc, :cc, :charset
def initialize
@bcc = @cc = @from = @recipients = @sent_on = @subject = @body = nil
@charset = @@default_charset.dup
@headers = {}
attr_reader :mail
# 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, *parameters)
create!(method_name, *parameters) if method_name
end
# Initialize the mailer via the given +method_name+. The body will be
# rendered and a new TMail::Mail object created.
def create!(method_name, *parameters)
@bcc = @cc = @from = @recipients = @sent_on = @subject = nil
@charset = @@default_charset.dup
@parts = []
@headers = {}
@body = {}
send(method_name, *parameters)
# If an explicit, textual body has not been set, we check assumptions.
unless String === @body
# First, we look to see if there are any likely templates that match,
# which include the content-type in their file name (i.e.,
# "the_template_file.text.html.rhtml", etc.).
if @parts.empty?
templates = Dir.glob("#{template_path}/#{method_name}.*")
templates.each do |path|
type = (File.basename(path).split(".")[1..-2] || []).join("/")
next if type.empty?
@parts << Part.new(:content_type => type,
:disposition => "inline", :charset => "charset",
:body => render_message(File.basename(path).split(".")[0..-2].join('.'), @body))
end
end
# Then, if there were such templates, we check to see if we ought to
# also render a "normal" template (without the content type). If a
# normal template exists (or if there were no implicit parts) we render
# it.
template_exists = @parts.empty?
template_exists ||= Dir.glob("#{template_path}/#{method_name}.*").any? { |i| i.split(".").length == 2 }
@body = render_message(method_name, @body) if template_exists
# Finally, if there are other message parts and a textual body exists,
# we shift it onto the front of the parts and set the body to nil (so
# that create_mail doesn't try to render it in addition to the parts).
if !@parts.empty? && String === @body
@parts.unshift Part.new(:charset => charset, :body => @body)
@body = nil
end
end
# build the mail object itself
@mail = create_mail
end
# Delivers the cached TMail::Mail object. If no TMail::Mail object has been
# created (via the #create! method, for instance) this will fail.
def deliver!
raise "no mail object available for delivery!" unless @mail
logger.info "Sent mail:\n #{mail.encoded}" unless logger.nil?
begin
send("perform_delivery_#{delivery_method}", @mail) if perform_deliveries
rescue Object => e
raise e if raise_delivery_errors
end
return @mail
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
part = 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 = { :disposition => "attachment",
:transfer_encoding => "base64" }.merge(params)
part(params, &block)
end
private
def render_message(method_name, body)
ActionView::Base.new(template_path, body).render_file(method_name)
end
def template_path
template_root + "/" + Inflector.underscore(self.class.name)
end
def create_mail
m = TMail::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.date = sent_on.to_time rescue sent_on if sent_on
headers.each { |k, v| m[k] = v }
if @parts.empty?
m.set_content_type "text", "plain", { "charset" => charset }
m.body = body
else
if String === body
part = TMail::Mail.new
part.body = body
part.set_content_type "text", "plain", { "charset" => charset }
part.set_content_disposition "inline"
m.parts << part
end
@parts.each do |p|
part = (TMail::Mail === p ? p : p.to_mail(self))
m.parts << part
end
end
@mail = m
end
def perform_delivery_smtp(mail)
Net::SMTP.start(server_settings[:address], server_settings[:port], server_settings[:domain],
server_settings[:user_name], server_settings[:password], server_settings[:authentication]) do |smtp|
smtp.sendmail(mail.encoded, mail.from, mail.destinations)
end
end
def perform_delivery_sendmail(mail)
IO.popen("/usr/sbin/sendmail -i -t","w+") do |sm|
sm.print(mail.encoded)
sm.flush
end
end
def perform_delivery_test(mail)
deliveries << mail
end
class << self
def method_missing(method_symbol, *parameters)#:nodoc:
case method_symbol.id2name
when /^create_([_a-z]\w*)/ then create_from_action($1, *parameters)
when /^deliver_([_a-z]\w*)/ then deliver(send("create_" + $1, *parameters))
when /^create_([_a-z]\w*)/ then new($1, *parameters).mail
when /^deliver_([_a-z]\w*)/ then new($1, *parameters).deliver!
end
end
def mail(to, subject, body, from, timestamp = nil, headers = {}, charset = @@default_charset) #:nodoc:
deliver(create(to, subject, body, from, timestamp, headers, charset))
end
def create(to, subject, body, from, timestamp = nil, headers = {}, charset = @@default_charset) #:nodoc:
m = TMail::Mail.new
m.body = body
m.subject, = quote_any_if_necessary(charset, subject)
m.to, m.from = quote_any_address_if_necessary(charset, to, from)
m.date = timestamp.respond_to?("to_time") ? timestamp.to_time : (timestamp || Time.now)
m.set_content_type "text", "plain", { "charset" => charset }
headers.each do |k, v|
m[k] = v
end
return m
end
def deliver(mail) #:nodoc:
logger.info "Sent mail:\n #{mail.encoded}" unless logger.nil?
begin
send("perform_delivery_#{delivery_method}", mail) if perform_deliveries
rescue Object => e
raise e if raise_delivery_errors
end
return mail
end
def quoted_printable(text, charset)#:nodoc:
text = text.gsub( /[^a-z ]/i ) { "=%02x" % $&[0] }.gsub( / /, "_" )
"=?#{charset}?Q?#{text}?="
end
CHARS_NEEDING_QUOTING = /[\000-\011\013\014\016-\037\177-\377]/
# Quote the given text if it contains any "illegal" characters
def quote_if_necessary(text, charset)
(text =~ CHARS_NEEDING_QUOTING) ?
quoted_printable(text, charset) :
text
end
# Quote any of the given strings if they contain any "illegal" characters
def quote_any_if_necessary(charset, *args)
args.map { |v| quote_if_necessary(v, charset) }
end
# Quote the given address if it needs to be. The address may be a
# regular email address, or it can be a phrase followed by an address in
# brackets. The phrase is the only part that will be quoted, and only if
# it needs to be. This allows extended characters to be used in the
# "to", "from", "cc", and "bcc" headers.
def quote_address_if_necessary(address, charset)
if Array === address
address.map { |a| quote_address_if_necessary(a, charset) }
elsif address =~ /^(\S.*)\s+(<.*>)$/
address = $2
phrase = quote_if_necessary($1.gsub(/^['"](.*)['"]$/, '\1'), charset)
"\"#{phrase}\" #{address}"
else
address
end
end
# Quote any of the given addresses, if they need to be.
def quote_any_address_if_necessary(charset, *args)
args.map { |v| quote_address_if_necessary(v, charset) }
end
def receive(raw_email)
logger.info "Received mail:\n #{raw_email}" unless logger.nil?
mail = TMail::Mail.parse(raw_email)
@@ -186,51 +308,6 @@ module ActionMailer #:nodoc:
new.receive(mail)
end
private
def perform_delivery_smtp(mail)
Net::SMTP.start(server_settings[:address], server_settings[:port], server_settings[:domain],
server_settings[:user_name], server_settings[:password], server_settings[:authentication]) do |smtp|
smtp.sendmail(mail.encoded, mail.from, mail.destinations)
end
end
def perform_delivery_sendmail(mail)
IO.popen("/usr/sbin/sendmail -i -t","w+") do |sm|
sm.print(mail.encoded)
sm.flush
end
end
def perform_delivery_test(mail)
deliveries << mail
end
def create_from_action(method_name, *parameters)
mailer = new
mailer.body = {}
mailer.send(method_name, *parameters)
unless String === mailer.body then
mailer.body = render_body mailer, method_name
end
mail = create(mailer.recipients, mailer.subject, mailer.body,
mailer.from, mailer.sent_on, mailer.headers,
mailer.charset)
mail.bcc = quote_address_if_necessary(mailer.bcc, mailer.charset) unless mailer.bcc.nil?
mail.cc = quote_address_if_necessary(mailer.cc, mailer.charset) unless mailer.cc.nil?
return mail
end
def render_body(mailer, method_name)
ActionView::Base.new(template_path, mailer.body).render_file(method_name)
end
def template_path
template_root + "/" + Inflector.underscore(self.to_s)
end
end
end
end

View File

@@ -0,0 +1,84 @@
require 'action_mailer/adv_attr_accessor'
module ActionMailer
class Part #:nodoc:
include ActionMailer::AdvAttrAccessor
adv_attr_accessor :content_type, :content_disposition, :charset, :body
adv_attr_accessor :filename, :transfer_encoding, :headers
def initialize(params)
@content_type = params[:content_type] || "text/plain"
@content_disposition = params[:disposition] || "inline"
@charset = params[:charset]
@body = params[:body]
@filename = params[:filename]
@transfer_encoding = params[:transfer_encoding] || "quoted-printable"
@headers = params[:headers] || {}
end
def to_mail(defaults)
part = TMail::Mail.new
part.set_content_type(content_type, nil,
"charset" => charset || defaults.charset, "name" => filename)
part.set_content_disposition(content_disposition,
"filename" => filename)
part.content_transfer_encoding = transfer_encoding || "quoted-printable"
case (transfer_encoding || "").downcase
when "base64" then
part.body = TMail::Base64.encode(body)
when "quoted-printable"
part.body = [body].pack("M*")
else
part.body = body
end
part
end
end
end
require 'action_mailer/adv_attr_accessor'
module ActionMailer
class Part #:nodoc:
include ActionMailer::AdvAttrAccessor
adv_attr_accessor :content_type, :content_disposition, :charset, :body
adv_attr_accessor :filename, :transfer_encoding, :headers
def initialize(params)
@content_type = params[:content_type] || "text/plain"
@content_disposition = params[:disposition] || "inline"
@charset = params[:charset]
@body = params[:body]
@filename = params[:filename]
@transfer_encoding = params[:transfer_encoding] || "quoted-printable"
@headers = params[:headers] || {}
end
def to_mail(defaults)
part = TMail::Mail.new
part.set_content_type(content_type, nil,
"charset" => charset || defaults.charset, "name" => filename)
part.set_content_disposition(content_disposition,
"filename" => filename)
part.content_transfer_encoding = transfer_encoding || "quoted-printable"
case (transfer_encoding || "").downcase
when "base64" then
part.body = TMail::Base64.encode(body)
when "quoted-printable"
part.body = [body].pack("M*")
else
part.body = body
end
part
end
end
end

View File

@@ -0,0 +1,102 @@
module ActionMailer
module Quoting
# Convert the given text into quoted printable format, with an instruction
# that the text be eventually interpreted in the given charset.
def quoted_printable(text, charset)
text = text.gsub( /[^a-z ]/i ) { "=%02x" % $&[0] }.gsub( / /, "_" )
"=?#{charset}?Q?#{text}?="
end
# A quick-and-dirty regexp for determining whether a string contains any
# characters that need escaping.
CHARS_NEEDING_QUOTING = /[\000-\011\013\014\016-\037\177-\377]/
# Quote the given text if it contains any "illegal" characters
def quote_if_necessary(text, charset)
(text =~ CHARS_NEEDING_QUOTING) ?
quoted_printable(text, charset) :
text
end
# Quote any of the given strings if they contain any "illegal" characters
def quote_any_if_necessary(charset, *args)
args.map { |v| quote_if_necessary(v, charset) }
end
# Quote the given address if it needs to be. The address may be a
# regular email address, or it can be a phrase followed by an address in
# brackets. The phrase is the only part that will be quoted, and only if
# it needs to be. This allows extended characters to be used in the
# "to", "from", "cc", and "bcc" headers.
def quote_address_if_necessary(address, charset)
if Array === address
address.map { |a| quote_address_if_necessary(a, charset) }
elsif address =~ /^(\S.*)\s+(<.*>)$/
address = $2
phrase = quote_if_necessary($1.gsub(/^['"](.*)['"]$/, '\1'), charset)
"\"#{phrase}\" #{address}"
else
address
end
end
# Quote any of the given addresses, if they need to be.
def quote_any_address_if_necessary(charset, *args)
args.map { |v| quote_address_if_necessary(v, charset) }
end
end
end
module ActionMailer
module Quoting
# Convert the given text into quoted printable format, with an instruction
# that the text be eventually interpreted in the given charset.
def quoted_printable(text, charset)
text = text.gsub( /[^a-z ]/i ) { "=%02x" % $&[0] }.gsub( / /, "_" )
"=?#{charset}?Q?#{text}?="
end
# A quick-and-dirty regexp for determining whether a string contains any
# characters that need escaping.
if !defined?(CHARS_NEEDING_QUOTING)
CHARS_NEEDING_QUOTING = /[\000-\011\013\014\016-\037\177-\377]/
end
# Quote the given text if it contains any "illegal" characters
def quote_if_necessary(text, charset)
(text =~ CHARS_NEEDING_QUOTING) ?
quoted_printable(text, charset) :
text
end
# Quote any of the given strings if they contain any "illegal" characters
def quote_any_if_necessary(charset, *args)
args.map { |v| quote_if_necessary(v, charset) }
end
# Quote the given address if it needs to be. The address may be a
# regular email address, or it can be a phrase followed by an address in
# brackets. The phrase is the only part that will be quoted, and only if
# it needs to be. This allows extended characters to be used in the
# "to", "from", "cc", and "bcc" headers.
def quote_address_if_necessary(address, charset)
if Array === address
address.map { |a| quote_address_if_necessary(a, charset) }
elsif address =~ /^(\S.*)\s+(<.*>)$/
address = $2
phrase = quote_if_necessary($1.gsub(/^['"](.*)['"]$/, '\1'), charset)
"\"#{phrase}\" #{address}"
else
address
end
end
# Quote any of the given addresses, if they need to be.
def quote_any_address_if_necessary(charset, *args)
args.map { |v| quote_address_if_necessary(v, charset) }
end
end
end

View File

@@ -253,6 +253,7 @@ module TMail
# FIXME: implement line folding
#
def kv_pair( k, v )
return if v.nil?
v = normalize_encoding(v)
if token_safe?(v)
add_text k + '=' + v

View File

@@ -442,7 +442,8 @@ module TMail
h.disposition = str
h.params.clear
else
h = store('Content-Disposition', str)
store('Content-Disposition', str)
h = @header['content-disposition']
end
h.params.replace params if params
end

View File

@@ -27,7 +27,7 @@ module TMail
t = Time.now
sprintf('%x%x_%x%x%d%x',
t.to_i, t.tv_usec,
$$, Thread.current.id, @uniq, rand(255))
$$, Thread.current.object_id, @uniq, rand(255))
end
private_class_method :random_tag

View File

@@ -0,0 +1,10 @@
<html>
<body>
HTML formatted message to <strong><%= @recipient %></strong>.
</body>
</html>
<html>
<body>
HTML formatted message to <strong><%= @recipient %></strong>.
</body>
</html>

View File

@@ -0,0 +1,2 @@
Plain text to <%= @recipient %>.
Plain text to <%= @recipient %>.

View File

@@ -14,21 +14,21 @@ class TestMailer < ActionMailer::Base
end
def cancelled_account(recipient)
@recipients = recipient
@subject = "[Cancelled] Goodbye #{recipient}"
@from = "system@loudthinking.com"
@sent_on = Time.local(2004, 12, 12)
@body = "Goodbye, Mr. #{recipient}"
self.recipients = recipient
self.subject = "[Cancelled] Goodbye #{recipient}"
self.from = "system@loudthinking.com"
self.sent_on = Time.local(2004, 12, 12)
self.body = "Goodbye, Mr. #{recipient}"
end
def cc_bcc(recipient)
@recipients = recipient
@subject = "testing bcc/cc"
@from = "system@loudthinking.com"
@sent_on = Time.local 2004, 12, 12
@cc = "nobody@loudthinking.com"
@bcc = "root@loudthinking.com"
@body = "Nothing to see here."
recipients recipient
subject "testing bcc/cc"
from "system@loudthinking.com"
sent_on Time.local(2004, 12, 12)
cc "nobody@loudthinking.com"
bcc "root@loudthinking.com"
body "Nothing to see here."
end
def iso_charset(recipient)
@@ -74,6 +74,30 @@ class TestMailer < ActionMailer::Base
@charset = "utf-8"
end
def explicitly_multipart_example(recipient)
@recipients = recipient
@subject = "multipart example"
@from = "test@example.com"
@sent_on = Time.local 2004, 12, 12
@body = "plain text default"
part "text/html" do |p|
p.charset = "iso-8859-1"
p.body = "blah"
end
attachment :content_type => "image/jpeg", :filename => "foo.jpg",
:body => "123456789"
end
def implicitly_multipart_example(recipient)
@recipients = recipient
@subject = "multipart example"
@from = "test@example.com"
@sent_on = Time.local 2004, 12, 12
@body = { "recipient" => recipient }
end
class <<self
attr_accessor :received_body
end
@@ -86,9 +110,10 @@ end
TestMailer.template_root = File.dirname(__FILE__) + "/fixtures"
class ActionMailerTest < Test::Unit::TestCase
include ActionMailer::Quoting
def encode( text, charset="utf-8" )
ActionMailer::Base.quoted_printable( text, charset )
quoted_printable( text, charset )
end
def new_mail( charset="utf-8" )
@@ -312,12 +337,12 @@ EOF
@recipient = "Grytøyr <test@localhost>"
expected = new_mail "iso-8859-1"
expected.to = TestMailer.quote_address_if_necessary @recipient, "iso-8859-1"
expected.to = quote_address_if_necessary @recipient, "iso-8859-1"
expected.subject = "testing extended headers"
expected.body = "Nothing to see here."
expected.from = TestMailer.quote_address_if_necessary "Grytøyr <stian1@example.net>", "iso-8859-1"
expected.cc = TestMailer.quote_address_if_necessary "Grytøyr <stian2@example.net>", "iso-8859-1"
expected.bcc = TestMailer.quote_address_if_necessary "Grytøyr <stian3@example.net>", "iso-8859-1"
expected.from = quote_address_if_necessary "Grytøyr <stian1@example.net>", "iso-8859-1"
expected.cc = quote_address_if_necessary "Grytøyr <stian2@example.net>", "iso-8859-1"
expected.bcc = quote_address_if_necessary "Grytøyr <stian3@example.net>", "iso-8859-1"
expected.date = Time.local 2004, 12, 12
created = nil
@@ -339,12 +364,12 @@ EOF
def test_utf8_body_is_not_quoted
@recipient = "Foo áëô îü <extended@example.net>"
expected = new_mail "utf-8"
expected.to = TestMailer.quote_address_if_necessary @recipient, "utf-8"
expected.to = quote_address_if_necessary @recipient, "utf-8"
expected.subject = "testing utf-8 body"
expected.body = "åœö blah"
expected.from = TestMailer.quote_address_if_necessary @recipient, "utf-8"
expected.cc = TestMailer.quote_address_if_necessary @recipient, "utf-8"
expected.bcc = TestMailer.quote_address_if_necessary @recipient, "utf-8"
expected.from = quote_address_if_necessary @recipient, "utf-8"
expected.cc = quote_address_if_necessary @recipient, "utf-8"
expected.bcc = quote_address_if_necessary @recipient, "utf-8"
expected.date = Time.local 2004, 12, 12
created = TestMailer.create_utf8_body @recipient
@@ -354,12 +379,12 @@ EOF
def test_multiple_utf8_recipients
@recipient = ["\"Foo áëô îü\" <extended@example.net>", "\"Example Recipient\" <me@example.com>"]
expected = new_mail "utf-8"
expected.to = TestMailer.quote_address_if_necessary @recipient, "utf-8"
expected.to = quote_address_if_necessary @recipient, "utf-8"
expected.subject = "testing utf-8 body"
expected.body = "åœö blah"
expected.from = TestMailer.quote_address_if_necessary @recipient.first, "utf-8"
expected.cc = TestMailer.quote_address_if_necessary @recipient, "utf-8"
expected.bcc = TestMailer.quote_address_if_necessary @recipient, "utf-8"
expected.from = quote_address_if_necessary @recipient.first, "utf-8"
expected.cc = quote_address_if_necessary @recipient, "utf-8"
expected.bcc = quote_address_if_necessary @recipient, "utf-8"
expected.date = Time.local 2004, 12, 12
created = TestMailer.create_utf8_body @recipient
@@ -400,5 +425,26 @@ EOF
assert_nothing_raised { mail.body }
end
def test_explicitly_multipart_messages
mail = TestMailer.create_explicitly_multipart_example(@recipient)
assert_equal 3, mail.parts.length
assert_equal "text/plain", mail.parts[0].content_type
assert_equal "text/html", mail.parts[1].content_type
assert_equal "inline", mail.parts[1].content_disposition
assert_equal "image/jpeg", mail.parts[2].content_type
assert_equal "attachment", mail.parts[2].content_disposition
assert_equal "foo.jpg", mail.parts[2].sub_header("content-disposition", "filename")
assert_equal "foo.jpg", mail.parts[2].sub_header("content-type", "name")
end
def test_implicitly_multipart_messages
mail = TestMailer.create_implicitly_multipart_example(@recipient)
assert_equal 2, mail.parts.length
assert_equal "text/html", mail.parts[0].content_type
assert_equal "text/plain", mail.parts[1].content_type
end
end