Merge remote branch 'mikel/master'

This commit is contained in:
José Valim 2010-01-26 01:56:52 +01:00
commit abad097016
76 changed files with 2080 additions and 1296 deletions

View File

@ -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'}

View File

@ -5,51 +5,72 @@ 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.
def signed_up(recipient)
recipients recipient
subject "[Signed up] Welcome #{recipient}"
from "system@loudthinking.com"
body :recipient => recipient
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
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 <tt>create_method_name</tt> and
<tt>deliver_method_name</tt>. 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 +124,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

View File

@ -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

View File

@ -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.1.1')
s.files = Dir['CHANGELOG', 'README', 'MIT-LICENSE', 'lib/**/*']
s.has_rdoc = true

View File

@ -31,10 +31,12 @@ module ActionMailer
extend ::ActiveSupport::Autoload
autoload :AdvAttrAccessor
autoload :Collector
autoload :Base
autoload :DeliveryMethod
autoload :DeprecatedBody
autoload :DeliveryMethods
autoload :DeprecatedApi
autoload :MailHelper
autoload :OldApi
autoload :Quoting
autoload :TestCase
autoload :TestHelper

View File

@ -1,6 +1,11 @@
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'
module ActionMailer #:nodoc:
# Action Mailer allows you to send email from your application using a mailer model and views.
@ -11,46 +16,71 @@ 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 <watcher@example.com>"]
# 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 <watcher@example.com>"])
# end
# end
# end
#
# Within the mailer method, you have access to the following methods:
#
# * <tt>attachments[]=</tt> - Allows you to add attachments to your email in an intuitive
# manner; <tt>attachments['filename.png'] = File.read('path/to/filename.png')</tt>
#
# Mailer methods have the following configuration methods available.
# * <tt>headers[]=</tt> - Allows you to specify non standard headers in your email such
# as <tt>headers['X-No-Spam'] = 'True'</tt>
#
# * <tt>recipients</tt> - Takes one or more email addresses. These addresses are where your email will be delivered to. Sets the <tt>To:</tt> header.
# * <tt>subject</tt> - The subject of your email. Sets the <tt>Subject:</tt> header.
# * <tt>from</tt> - Who the email you are sending is from. Sets the <tt>From:</tt> header.
# * <tt>cc</tt> - Takes one or more email addresses. These addresses will receive a carbon copy of your email. Sets the <tt>Cc:</tt> header.
# * <tt>bcc</tt> - Takes one or more email addresses. These addresses will receive a blind carbon copy of your email. Sets the <tt>Bcc:</tt> header.
# * <tt>reply_to</tt> - Takes one or more email addresses. These addresses will be listed as the default recipients when replying to your email. Sets the <tt>Reply-To:</tt> header.
# * <tt>sent_on</tt> - The date on which the message was sent. If not set, the header will be set by the delivery agent.
# * <tt>content_type</tt> - Specify the content type of the message. Defaults to <tt>text/plain</tt>.
# * <tt>headers</tt> - Specify additional headers to be set for the message, e.g. <tt>headers 'X-Mail-Count' => 107370</tt>.
# * <tt>mail</tt> - 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 <tt>Subject</tt>, <tt>To</tt>, <tt>From</tt>, <tt>Cc</tt>, <tt>Bcc</tt>,
# <tt>Reply-To</tt> and <tt>Date</tt>. See the <tt>ActionMailer#mail</tt> method for more details.
#
# If you need other headers not listed above, use the <tt>headers['name'] = value</tt> method.
#
# When a <tt>headers 'return-path'</tt> 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 <tt>from</tt>.
# 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/alternative+ email.
#
# If you want to explicitly render only certain templates, pass a block:
#
# mail(:to => user.emai) do |format|
# format.text
# 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 method of the class looks for a template with its name.
# To define a template to be used with a mailing, create an <tt>.erb</tt> file with the same name as the method
# in your mailer model. For example, in the mailer defined above, the template at
# <tt>app/views/notifier/signup_notification.erb</tt> 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 <tt>.erb</tt> file with the same
# name as the method in your mailer model. For example, in the mailer defined above, the template at
# <tt>app/views/notifier/signup_notification.text.erb</tt> would be used to generate the email.
#
# Variables defined in the model are accessible as instance variables in the view.
#
@ -64,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) %>
#
#
@ -106,54 +136,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
# <tt>deliver_</tt> followed by the name of the mailer method that you would
# like to deliver. The <tt>signup_notification</tt> method defined above is
# delivered by invoking <tt>Notifier.deliver_signup_notification</tt>.
# 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 <tt>.erb</tt> 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
@ -163,13 +152,12 @@ 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 <tt>multipart/alternative</tt>, 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 <tt>multipart/alternative</tt>,
# 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
@ -177,32 +165,30 @@ 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 <tt>.text.erb</tt> and <tt>.html.erb</tt> tempalte in the view
# directory), send a complete <tt>multipart/mixed</tt> email with two parts, the first part being
# a <tt>multipart/alternative</tt> with the text and HTML email parts inside, and the second being
# a <tt>application/pdf</tt> 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 <tt>ActionMailer::Base.template_root = "/my/templates"</tt>
#
# * <tt>template_root</tt> - Determines the base from which template references will be made.
# * <tt>delivers_from</tt> - 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 <tt>:from => 'another@address'</tt> in
# the +mail+ method.
#
# * <tt>logger</tt> - 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.
@ -251,8 +237,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
include Quoting
extend AdvAttrAccessor
include DeliveryMethods, Quoting
abstract!
include AbstractController::Logger
include AbstractController::Rendering
@ -262,27 +248,23 @@ module ActionMailer #:nodoc:
include AbstractController::UrlFor
helper ActionMailer::MailHelper
include ActionMailer::DeprecatedBody
include ActionMailer::OldApi
include ActionMailer::DeprecatedApi
private_class_method :new #:nodoc:
@@raise_delivery_errors = true
cattr_accessor :raise_delivery_errors
extlib_inheritable_accessor :default_from
self.default_from = nil
@@perform_deliveries = true
cattr_accessor :perform_deliveries
extlib_inheritable_accessor :default_charset
self.default_charset = "utf-8"
@@deliveries = []
cattr_accessor :deliveries
extlib_inheritable_accessor :default_content_type
self.default_content_type = "text/plain"
@@default_charset = "utf-8"
cattr_accessor :default_charset
@@default_content_type = "text/plain"
cattr_accessor :default_content_type
@@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
@ -290,101 +272,20 @@ 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
@@protected_instance_variables = %w(@parts @mail)
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 <tt>text/plain</tt>
# 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
attr_reader :mail
# Alias controller_path to mailer_name so render :partial in views work.
alias :controller_path :mailer_name
extlib_inheritable_accessor :default_implicit_parts_order
self.default_implicit_parts_order = [ "text/plain", "text/enriched", "text/html" ]
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
attr_writer :mailer_name
alias :controller_path :mailer_name
def delivery_method=(method_name)
@delivery_method = ActionMailer::DeliveryMethod.lookup_method(method_name)
end
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).mail
when 'deliver' then new(match[2], *parameters).deliver!
when 'new' then nil
else super
end
else
super
end
# 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,
@ -406,26 +307,24 @@ 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)
new.deliver!(mail)
# Delivers a mail object. This is actually called by the <tt>Mail::Message</tt> object
# itself through a call back when you call <tt>:deliver</tt> 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)
yield # Let Mail do the delivery actions
end
end
def template_root
self.view_paths && self.view_paths.first
def respond_to?(method, *args) #:nodoc:
super || action_methods.include?(method.to_s)
end
# Should template root overwrite the whole view_paths?
def template_root=(root)
self.view_paths = ActionView::Base.process_view_paths(root)
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
@ -436,229 +335,264 @@ module ActionMailer #:nodoc:
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
# 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
# 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) }
def method_missing(method, *args) #:nodoc:
if action_methods.include?(method.to_s)
new(method, *args).message
else
super
end
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
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
# 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
# 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]
@headers = args
else
@_message
end
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
# 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
private
# 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:
#
# * <tt>:subject</tt> - The subject of the message, if this is omitted, ActionMailer will
# ask the Rails I18n class for a translated <tt>:subject</tt> in the scope of
# <tt>[:actionmailer, mailer_scope, action_name]</tt> or if this is missing, will translate the
# humanized version of the <tt>action_name</tt>
# * <tt>:to</tt> - Who the message is destined for, can be a string of addresses, or an array
# of addresses.
# * <tt>:from</tt> - Who the message is from, if missing, will use the <tt>:delivers_from</tt>
# value in the class (if it exists)
# * <tt>:cc</tt> - Who you would like to Carbon-Copy on this email, can be a string of addresses,
# or an array of addresses.
# * <tt>:bcc</tt> - Who you would like to Blind-Carbon-Copy on this email, can be a string of
# addresses, or an array of addresses.
# * <tt>:reply_to</tt> - Who to set the Reply-To header of the email to.
# * <tt>:date</tt> - The date to say the email was sent on.
#
# If you need other headers not listed above, use the <tt>headers['name'] = value</tt> method.
#
# When a <tt>:return_path</tt> 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 <tt>:from</tt>. Mail will actually use the
# <tt>:return_path</tt> in preference to the <tt>:sender</tt> in preference to the <tt>:from</tt>
# 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 <tt>:deliver</tt> 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 => "<h1>Hello Mikel!</h1>" }
# end
#
# Which will render a <tt>multipart/alternative</tt> email with <tt>text/plain</tt> and
# <tt>text/html</tt> 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
@mail_was_called = true
m = @_message
# 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
# 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 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
# Set fields quotings
headers[:subject] ||= default_subject
headers[:from] ||= self.class.default_from.dup
quote_fields!(headers, charset)
@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
# Render the templates and blocks
responses, sort_order = collect_responses_and_sort_order(headers, &block)
def split_content_type(ct) #:nodoc:
ct.to_s.split("/")
create_parts_from_responses(m, responses, charset)
# Tidy up content type, charset, mime version and sort order
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
if m.multipart?
m.body.set_sort_order(sort_order)
m.body.sort_parts!
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)]
# Finaly set delivery behavior configured in class
wrap_delivery_behavior!(headers[:delivery_method])
m
end
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)
end
# TODO: Move this into Mail
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
each_template do |template|
responses << {
:body => render_to_body(:_template => template),
:content_type => template.mime_type.to_s
}
end
end
[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)
templates = templates.uniq_by { |t| t.details[:formats] }
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?
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
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
end
def insert_part(container, response, charset) #:nodoc:
response[:charset] ||= charset
part = Mail::Part.new(response)
container.add_part(part)
end
end
end

View File

@ -0,0 +1,36 @@
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_reader :responses
def initialize(context, &block)
@context = context
@responses = []
@default_render = block
@default_formats = context.formats
end
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.dup, &block) }
end
alias :all :any
def custom(mime, options={}, &block)
options.reverse_merge!(:content_type => mime.to_s)
@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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,87 @@
require 'tmpdir'
module 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
included do
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
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
module ClassMethods
# 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:
#
# 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
send(:"#{symbol}_settings=", default_options)
self.delivery_methods[symbol.to_sym] = klass
end
def wrap_delivery_behavior(mail, method=nil) #:nodoc:
method ||= self.delivery_method
mail.delivery_handler = 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 wrap_delivery_behavior!(*args) #:nodoc:
self.class.wrap_delivery_behavior(message, *args)
end
end
end

View File

@ -0,0 +1,112 @@
module ActionMailer
# 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
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, show_warning=true)
if show_warning
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
wrap_delivery_behavior(mail)
mail.deliver
mail
end
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)
if match = matches_dynamic_method?(method_symbol)
case match[1]
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).message.deliver
else super
end
else
super
end
end
private
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
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)
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!
def render(*args)
options = args.last.is_a?(Hash) ? args.last : {}
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
# 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(*args)
ActiveSupport::Deprecation.warn "render_message is deprecated, use render instead", caller[0,2]
render(*args)
end
private
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
end
end

View File

@ -1,46 +0,0 @@
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.
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[: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)
end
end
def create_parts
if String === @body && !defined?(@assigns_set)
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
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

View File

@ -17,8 +17,13 @@ module ActionMailer
end
# Access the mailer instance.
def mailer #:nodoc:
def mailer
@_controller
end
# Access the message instance.
def message
@_message
end
end
end

View File

@ -0,0 +1,250 @@
module ActionMailer
module OldApi #: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 <tt>text/plain</tt>
# 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
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)
@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
if String === @body
self.response_body = @body
end
if String === response_body
@parts.unshift create_inline_part(response_body)
else
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
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)
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)
ct.to_s.split("/")
end
def parse_content_type(defaults=nil)
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

View File

@ -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

View File

@ -58,7 +58,6 @@ module ActionMailer
end
end
# TODO: Deprecate this
module Test
module Unit
class TestCase

View File

@ -2,19 +2,27 @@ 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,2])
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,2])
content_transfer_encoding(value)
else
old_transfer_encoding
end
end
def original_filename
ActiveSupport::Deprecation.warn('Message#original_filename is deprecated, ' <<
'please call Message#filename', caller[0,2])
filename
end
end
end

View File

@ -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,14 +17,10 @@ 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.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
@ -49,19 +44,11 @@ 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(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
ActionMailer::Base.delivery_method = @old_delivery_method
end
end

View File

@ -0,0 +1,420 @@
# encoding: utf-8
require 'abstract_unit'
class BaseTest < ActiveSupport::TestCase
DEFAULT_HEADERS = {
:to => 'mikel@test.lindsaar.net',
: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 = {})
headers['X-SPAM'] = "Not SPAM"
mail(DEFAULT_HEADERS.merge(hash))
end
def attachment_with_content(hash = {})
attachments['invoice.pdf'] = 'This is test File content'
mail(DEFAULT_HEADERS.merge(hash))
end
def attachment_with_hash
attachments['invoice.jpg'] = { :data => "you smiling", :mime_type => "image/x-jpg",
:transfer_encoding => "base64" }
mail(DEFAULT_HEADERS)
end
def implicit_multipart(hash = {})
attachments['invoice.pdf'] = 'This is test File content' if hash.delete(:attachments)
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|
format.text { render :text => "TEXT Explicit Multipart" }
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
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
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.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!')
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',
: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')
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
test "mail() renders the template using the method being processed" do
email = BaseMailer.welcome.deliver
assert_equal("Welcome", email.body.encoded)
end
test "can pass in :body to the mail method hash" do
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.welcome.deliver
assert_equal("Not SPAM", email['X-SPAM'].decoded)
end
# Attachments
test "attachment with content" do
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.attachment_with_content.deliver
assert_equal('invoice.pdf', email.attachments[0].filename)
end
test "attachment with hash" do
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.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.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)
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.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)
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
email = BaseMailer.welcome.deliver
assert_equal("US-ASCII", email.charset)
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.welcome.deliver
assert_equal("text/html", email.mime_type)
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.welcome.deliver
assert_equal("2.0", email.mime_version)
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.welcome(:subject => nil).deliver
assert_equal "Welcome", email.subject
I18n.backend.store_translations('en', :actionmailer => {:base_mailer => {:welcome => {:subject => "New Subject!"}}})
email = BaseMailer.welcome(:subject => nil).deliver
assert_equal "New Subject!", email.subject
end
# Implicit multipart
test "implicit multipart" do
email = BaseMailer.implicit_multipart.deliver
assert_equal(2, email.parts.size)
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)
assert_equal("HTML Implicit Multipart", email.parts[1].body.encoded)
end
test "implicit multipart with sort order" do
order = ["text/html", "text/plain"]
swap BaseMailer, :default_implicit_parts_order => order do
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.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.implicit_multipart(:attachments => true).deliver
assert_equal("application/pdf", email.parts[0].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)
assert_equal("HTML Implicit Multipart", email.parts[1].parts[1].body.encoded)
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.implicit_multipart(:attachments => true).deliver
assert_equal("application/pdf", email.parts[0].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
end
test "implicit multipart with default locale" do
email = BaseMailer.implicit_with_locale.deliver
assert_equal(2, email.parts.size)
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)
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/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)
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
assert_equal(2, email.parts.size)
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)
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.explicit_multipart.deliver
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).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.explicit_multipart(:attachments => true).deliver
assert_equal("application/pdf", email.parts[0].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)
assert_equal("HTML Explicit Multipart", email.parts[1].parts[1].body.encoded)
end
test "explicit multipart with templates" do
email = BaseMailer.explicit_multipart_templates.deliver
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)
end
test "explicit multipart with any" do
email = BaseMailer.explicit_multipart_with_any.deliver
assert_equal(2, email.parts.size)
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)
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/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)
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)
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
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(: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
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
test "explicit multipart should be multipart" do
mail = BaseMailer.explicit_multipart
assert_not_nil(mail.content_type_parameters[:boundary])
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

View File

@ -1,101 +0,0 @@
require 'abstract_unit'
class DefaultDeliveryMethodMailer < ActionMailer::Base
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
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
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_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
end
def test_should_be_the_set_delivery_method
assert_instance_of ActionMailer::DeliveryMethod::Sendmail, NonDefaultDeliveryMethodMailer.delivery_method
end
end
class FileDeliveryMethodMailer_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_instance_of ActionMailer::DeliveryMethod::File, FileDeliveryMethodMailer.delivery_method
end
def test_should_default_location_to_the_tmpdir
assert_equal "#{Dir.tmpdir}/mails", ActionMailer::Base.file_settings[:location]
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_instance_of CustomDeliveryMethod, CustomerDeliveryMailer.delivery_method
end
end

View File

@ -0,0 +1,170 @@
require 'abstract_unit'
require 'mail'
class MyCustomDelivery
end
class BogusDelivery
def initialize(*)
end
def deliver!(mail)
raise "failed"
end
end
class DefaultsDeliveryMethodsTest < ActiveSupport::TestCase
test "default smtp settings" do
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
test "default file delivery settings" do
settings = {:location => "#{Dir.tmpdir}/mails"}
assert_equal settings, ActionMailer::Base.file_settings
end
test "default sendmail settings" do
settings = {:location => '/usr/sbin/sendmail',
:arguments => '-i -t'}
assert_equal settings, ActionMailer::Base.sendmail_settings
end
end
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
test "allow to add custom delivery method" do
ActionMailer::Base.delivery_method = :custom
assert_equal :custom, ActionMailer::Base.delivery_method
end
test "allow to customize custom settings" do
ActionMailer::Base.custom_settings = { :foo => :bar }
assert_equal Hash[:foo => :bar], ActionMailer::Base.custom_settings
end
test "respond to custom settings" do
assert_respond_to ActionMailer::Base, :custom_settings
assert_respond_to ActionMailer::Base, :custom_settings=
end
test "does not respond to unknown settings" do
assert_raise NoMethodError do
ActionMailer::Base.another_settings
end
end
end
class MailDeliveryTest < ActiveSupport::TestCase
class DeliveryMailer < 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
DeliveryMailer.delivery_method = :smtp
DeliveryMailer.perform_deliveries = true
DeliveryMailer.raise_delivery_errors = true
end
test "ActionMailer should be told when Mail gets delivered" do
DeliveryMailer.deliveries.clear
DeliveryMailer.expects(:deliver_mail).once
DeliveryMailer.welcome.deliver
end
test "delivery method can be customized per instance" do
email = DeliveryMailer.welcome.deliver
assert_instance_of Mail::SMTP, email.delivery_method
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
DeliveryMailer.delivery_method = :test
assert_equal :smtp, ActionMailer::Base.delivery_method
$BREAK = true
email = DeliveryMailer.welcome.deliver
assert_instance_of Mail::TestMailer, email.delivery_method
end
test "non registered delivery methods raises errors" do
DeliveryMailer.delivery_method = :unknown
assert_raise RuntimeError do
DeliveryMailer.welcome.deliver
end
end
test "does not perform deliveries if requested" do
DeliveryMailer.perform_deliveries = false
DeliveryMailer.deliveries.clear
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
test "raise errors on bogus deliveries" do
DeliveryMailer.delivery_method = BogusDelivery
DeliveryMailer.deliveries.clear
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
assert_equal(0, DeliveryMailer.deliveries.length)
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 "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(0, DeliveryMailer.deliveries.length)
end
end

View File

@ -0,0 +1 @@
Welcome from another path

View File

@ -0,0 +1 @@
Attachment with content

View File

@ -0,0 +1 @@
HTML Explicit Multipart Templates

View File

@ -0,0 +1 @@
TEXT Explicit Multipart Templates

View File

@ -0,0 +1 @@
HTML Implicit Multipart

View File

@ -0,0 +1 @@
TEXT Implicit Multipart

View File

@ -0,0 +1 @@
Implicit with locale EN HTML

View File

@ -0,0 +1 @@
Implicit with locale HTML

View File

@ -0,0 +1 @@
Implicit with locale PL TEXT

View File

@ -0,0 +1 @@
Implicit with locale TEXT

View File

@ -0,0 +1 @@
Welcome

View File

@ -1 +0,0 @@
So, <%= example_format(@text) %>

View File

@ -1 +0,0 @@
Hello, <%= person_name %>. Thanks for registering!

View File

@ -1 +0,0 @@
This message brought to you by <%= name_of_the_mailer_class %>.

View File

@ -1,5 +0,0 @@
From "Romeo and Juliet":
<%= block_format @text %>
Good ol' Shakespeare.

View File

@ -1,5 +0,0 @@
module ExampleHelper
def example_format(text)
"<em><strong><small>#{h(text)}</small></strong></em>".html_safe!
end
end

View File

@ -1,2 +0,0 @@
body: <%= @body %>
bar: <%= @bar %>

View File

@ -1,96 +1,54 @@
require 'abstract_unit'
module MailerHelper
def person_name
"Mr. Joe Person"
end
end
class HelperMailer < ActionMailer::Base
helper MailerHelper
helper :example
def use_helper(recipient)
recipients recipient
subject "using helpers"
from "tester@example.com"
end
def use_example_helper(recipient)
recipients recipient
subject "using helpers"
from "tester@example.com"
@text = "emphasize me!"
end
def use_mail_helper(recipient)
recipients recipient
subject "using mailing helpers"
from "tester@example.com"
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!"
end
def use_helper_method(recipient)
recipients recipient
subject "using helpers"
from "tester@example.com"
@text = "emphasize me!"
end
private
def name_of_the_mailer_class
self.class.name
mail_with_defaults do |format|
format.html { render(:inline => "<%= block_format @text %>") }
end
helper_method :name_of_the_mailer_class
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 < Test::Unit::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 = []
@recipient = 'test@localhost'
end
def teardown
restore_delivery_method
end
def test_use_helper
mail = HelperMailer.create_use_helper(@recipient)
assert_match %r{Mr. Joe Person}, mail.encoded
end
def test_use_example_helper
mail = HelperMailer.create_use_example_helper(@recipient)
assert_match %r{<em><strong><small>emphasize me!}, mail.encoded
end
def test_use_helper_method
mail = HelperMailer.create_use_helper_method(@recipient)
assert_match %r{HelperMailer}, mail.encoded
end
class MailerHelperTest < ActionMailer::TestCase
def test_use_mail_helper
mail = HelperMailer.create_use_mail_helper(@recipient)
assert_match %r{ But soft!}, mail.encoded
assert_match %r{east, and\r\n Juliet}, mail.encoded
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

View File

@ -1,24 +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
# CHANGED: body returns object, not string, Changed m.body to m.body.to_s
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

View File

@ -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
@ -12,9 +12,7 @@ class AssetHostTest < Test::Unit::TestCase
def setup
set_delivery_method :test
ActionMailer::Base.perform_deliveries = true
ActionMailer::Base.deliveries = []
@recipient = 'test@localhost'
ActionMailer::Base.deliveries.clear
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 "<img alt=\"Somelogo\" src=\"http://www.example.com/images/somelogo.png\" />", 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 "<img alt=\"Somelogo\" src=\"http://images.example.com/images/somelogo.png\" />", 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 "<img alt=\"Somelogo\" src=\"http://www.example.com/images/somelogo.png\" />", mail.body.to_s.strip
end
end

View File

@ -1,14 +1,15 @@
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"
@ -16,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"
@ -25,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"
@ -37,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
@ -54,9 +55,7 @@ class LayoutMailerTest < Test::Unit::TestCase
def setup
set_delivery_method :test
ActionMailer::Base.perform_deliveries = true
ActionMailer::Base.deliveries = []
@recipient = 'test@localhost'
ActionMailer::Base.deliveries.clear
end
def teardown
@ -64,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
@ -93,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
@ -115,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
@ -128,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

View File

@ -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
@ -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
@ -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 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<test/>", 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
@ -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
@ -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

View File

@ -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
@ -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,17 +297,10 @@ 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
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"
@ -344,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'
@ -357,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
@ -373,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
@ -389,11 +382,13 @@ 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 == "<test@test.com>" }
end
def test_signed_up
TestMailer.delivery_method = :test
Time.stubs(:now => Time.now)
expected = new_mail
@ -404,7 +399,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>'
@ -412,7 +407,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
@ -423,15 +418,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
@ -441,7 +427,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>'
@ -464,7 +450,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
@ -480,13 +466,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>'
@ -507,7 +493,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>'
@ -515,7 +501,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
@ -527,8 +513,8 @@ class ActionMailerTest < Test::Unit::TestCase
end
def test_from_without_name_for_smtp
ActionMailer::Base.delivery_method = :smtp
TestMailer.deliver_from_without_name
TestMailer.delivery_method = :smtp
TestMailer.from_without_name.deliver
mail = MockSMTP.deliveries.first
assert_not_nil mail
@ -538,17 +524,19 @@ class ActionMailerTest < Test::Unit::TestCase
end
def test_from_with_name_for_smtp
ActionMailer::Base.delivery_method = :smtp
TestMailer.deliver_from_with_name
TestMailer.delivery_method = :smtp
TestMailer.from_with_name.deliver
mail = MockSMTP.deliveries.first
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
TestMailer.delivery_method = :test
expected = new_mail
expected.to = @recipient
@ -560,7 +548,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
@ -570,7 +558,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
@ -583,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"
@ -594,7 +584,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
@ -604,7 +594,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
@ -617,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"
@ -628,7 +619,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
@ -638,7 +629,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
@ -650,41 +641,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
TestMailer.delivery_method.expects(:perform_delivery).raises(Exception)
assert_nothing_raised { TestMailer.deliver_signed_up(@recipient) }
Mail::TestMailer.any_instance.expects(:deliver!).raises(Exception)
assert_nothing_raised { TestMailer.signed_up(@recipient).deliver }
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
TestMailer.deliver_signed_up(@recipient)
IO.expects(:popen).once.with('/usr/sbin/sendmail -i -t -f "system@loudthinking.com" test@localhost', 'w+')
TestMailer.delivery_method = :sendmail
TestMailer.signed_up(@recipient).deliver
end
def test_unquote_quoted_printable_subject
@ -769,7 +752,7 @@ EOF
created = nil
assert_nothing_raised do
created = TestMailer.create_extended_headers @recipient
created = TestMailer.extended_headers @recipient
end
assert_not_nil created
@ -779,7 +762,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
@ -802,7 +785,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
@ -817,82 +800,82 @@ 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_.*?\?= <extended@example.net>\r/, created.encoded)
assert_match(/\nTo: =\?utf-8\?Q\?Foo_.*?\?= <extended@example.net>, \r\n\tExample Recipient <me/, created.encoded)
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.original_filename
assert_equal "smime.p7s", attachment.filename
assert_equal "application/pkcs7-signature", mail.parts.last.mime_type
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.original_filename
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
assert_equal "hello.rb", mail.attachments.first.original_filename
assert_equal "hello.rb", mail.attachments.first.filename
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
def test_multipart_with_mime_version
mail = TestMailer.create_multipart_with_mime_version(@recipient)
mail = TestMailer.multipart_with_mime_version(@recipient)
assert_equal "1.1", mail.mime_version
end
def test_multipart_with_utf8_subject
mail = TestMailer.create_multipart_with_utf8_subject(@recipient)
mail = TestMailer.multipart_with_utf8_subject(@recipient)
assert_match(/\nSubject: =\?utf-8\?Q\?Foo_.*?\?=/, mail.encoded)
end
def test_implicitly_multipart_with_utf8
mail = TestMailer.create_implicitly_multipart_with_utf8
mail = TestMailer.implicitly_multipart_with_utf8
assert_match(/\nSubject: =\?utf-8\?Q\?Foo_.*?\?=/, mail.encoded)
end
def test_explicitly_multipart_messages
mail = TestMailer.create_explicitly_multipart_example(@recipient)
mail = TestMailer.explicitly_multipart_example(@recipient)
assert_equal 3, mail.parts.length
assert_equal 'multipart/mixed', mail.mime_type
assert_equal "text/plain", mail.parts[0].mime_type
@ -901,6 +884,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
@ -908,13 +892,13 @@ EOF
end
def test_explicitly_multipart_with_content_type
mail = TestMailer.create_explicitly_multipart_example(@recipient, "multipart/alternative")
mail = TestMailer.explicitly_multipart_example(@recipient, "multipart/alternative")
assert_equal 3, mail.parts.length
assert_equal "multipart/alternative", mail.mime_type
end
def test_explicitly_multipart_with_invalid_content_type
mail = TestMailer.create_explicitly_multipart_example(@recipient, "text/xml")
mail = TestMailer.explicitly_multipart_example(@recipient, "text/xml")
assert_equal 3, mail.parts.length
assert_equal 'multipart/mixed', mail.mime_type
end
@ -922,7 +906,7 @@ EOF
def test_implicitly_multipart_messages
assert ActionView::Template.template_handler_extensions.include?("bak"), "bak extension was not registered"
mail = TestMailer.create_implicitly_multipart_example(@recipient)
mail = TestMailer.implicitly_multipart_example(@recipient)
assert_equal 3, mail.parts.length
assert_equal "1.0", mail.mime_version.to_s
assert_equal "multipart/alternative", mail.mime_type
@ -937,7 +921,7 @@ EOF
def test_implicitly_multipart_messages_with_custom_order
assert ActionView::Template.template_handler_extensions.include?("bak"), "bak extension was not registered"
mail = TestMailer.create_implicitly_multipart_example(@recipient, nil, ["application/x-yaml", "text/plain"])
mail = TestMailer.implicitly_multipart_example(@recipient, nil, ["application/x-yaml", "text/plain"])
assert_equal 3, mail.parts.length
assert_equal "application/x-yaml", mail.parts[0].mime_type
assert_equal "text/plain", mail.parts[1].mime_type
@ -945,7 +929,7 @@ EOF
end
def test_implicitly_multipart_messages_with_charset
mail = TestMailer.create_implicitly_multipart_example(@recipient, 'iso-8859-1')
mail = TestMailer.implicitly_multipart_example(@recipient, 'iso-8859-1')
assert_equal "multipart/alternative", mail.header['content-type'].content_type
@ -955,23 +939,23 @@ EOF
end
def test_html_mail
mail = TestMailer.create_html_mail(@recipient)
mail = TestMailer.html_mail(@recipient)
assert_equal "text/html", mail.mime_type
end
def test_html_mail_with_underscores
mail = TestMailer.create_html_mail_with_underscores(@recipient)
mail = TestMailer.html_mail_with_underscores(@recipient)
assert_equal %{<a href="http://google.com" target="_blank">_Google</a>}, 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 "<p>line #1</p>\n<p>line #2</p>\n<p>line #3</p>\n<p>line #4</p>\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
@ -979,8 +963,8 @@ EOF
end
def test_headers_removed_on_smtp_delivery
ActionMailer::Base.delivery_method = :smtp
TestMailer.deliver_cc_bcc(@recipient)
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")
assert MockSMTP.deliveries[0][2].include?(@recipient)
@ -990,10 +974,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.deliver_cc_bcc(@recipient)
result = TestMailer.cc_bcc(@recipient).deliver
assert File.exists?(tmp_location)
assert File.directory?(tmp_location)
assert File.exists?(File.join(tmp_location, @recipient))
@ -1002,7 +986,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)
@ -1013,36 +997,36 @@ 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
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
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
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
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?
@ -1051,88 +1035,80 @@ 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
TestMailer.delivery_method = :test
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
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
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.delivery_method = :smtp
TestMailer.return_path.deliver
assert_match %r{^Return-Path: <another@somewhere.test>}, MockSMTP.deliveries[0][0]
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.smtp_settings[: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.deliver_signed_up(@recipient)
TestMailer.delivery_method = :smtp
TestMailer.signed_up(@recipient).deliver
end
def test_starttls_is_disabled_if_not_supported
ActionMailer::Base.smtp_settings[: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.deliver_signed_up(@recipient)
TestMailer.delivery_method = :smtp
TestMailer.signed_up(@recipient).deliver
end
def test_starttls_is_not_enabled
ActionMailer::Base.smtp_settings[:enable_starttls_auto] = false
TestMailer.smtp_settings.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)
TestMailer.delivery_method = :smtp
TestMailer.signed_up(@recipient).deliver
ensure
ActionMailer::Base.smtp_settings[:enable_starttls_auto] = true
TestMailer.smtp_settings.merge!(:enable_starttls_auto => true)
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")
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
end
end
class MethodNamingTest < Test::Unit::TestCase
class MethodNamingTest < ActiveSupport::TestCase
class TestMailer < ActionMailer::Base
def send
render :text => 'foo'
@ -1142,7 +1118,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
@ -1152,12 +1128,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
@ -1220,4 +1197,4 @@ class RespondToTest < Test::Unit::TestCase
assert_match(/undefined method.*not_a_method/, error.message)
end
end
end

View File

@ -1,21 +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
STDERR.expects(:puts) # Deprecation warning
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
STDERR.expects(:puts) # Deprecation warning
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

View File

@ -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
@ -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"
@ -67,14 +69,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

View File

@ -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

View File

@ -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
@ -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

View File

@ -10,6 +10,7 @@ module AbstractController
autoload :Base
autoload :Callbacks
autoload :Collector
autoload :Helpers
autoload :Layouts
autoload :LocalizedCache

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -52,8 +52,6 @@ class ArrayExtToParamTests < Test::Unit::TestCase
end
class ArrayExtToSentenceTests < Test::Unit::TestCase
include ActiveSupport::Testing::Deprecation
def test_plain_array_to_sentence
assert_equal "", [].to_sentence
assert_equal "one", ['one'].to_sentence
@ -62,28 +60,12 @@ class ArrayExtToSentenceTests < Test::Unit::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 => ' ')
@ -320,6 +302,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

View File

@ -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

View File

@ -1,3 +0,0 @@
<%= class_name %>#<%= @action %>
Find me in app/views/<%= @path %>

View File

@ -0,0 +1,3 @@
<%= class_name %>#<%= @action %>
<%%= @greeting %>, find me in app/views/<%= @path %>

View File

@ -1,14 +1,15 @@
class <%= class_name %> < ActionMailer::Base
delivers_from "from@example.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 => "to@example.com")
end
<% end -%>
end
end

View File

@ -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 => [],

View File

@ -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

View File

@ -1,3 +1,3 @@
<%= class_name %>#<%= @action %>
Find me in app/views/<%= @path %>
Hi, find me in app/views/<%= @path %>

View File

@ -3,11 +3,13 @@ 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 %>')
@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_equal @expected.encoded, <%= class_name %>.create_<%= action %>(@expected.date).encoded
assert_equal @expected, <%= class_name %>.<%= action %>
end
<% end -%>

View File

@ -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

View File

@ -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

View File

@ -7,7 +7,18 @@ class MailerGeneratorTest < Rails::Generators::TestCase
def test_mailer_skeleton_is_created
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 "from@example.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 +35,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.text.erb" do |view|
assert_match /app\/views\/notifier\/foo/, view
assert_match /<%= @greeting %>/, view
end
assert_file "app/views/notifier/bar.text.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 +58,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 => "to@example.com"\)/, foo
assert_match /@greeting = "Hi"/, foo
end
assert_instance_method :bar, mailer do |bar|
assert_match /mail\(:to => "to@example.com"\)/, bar
assert_match /@greeting = "Hi"/, bar
end
end
end
end

View File

@ -16,28 +16,68 @@ 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
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)
assert_equal value, generator.send(method)
end
end