Action Mailer Basics - Ruby On Rails Guides
Action Mailer Basics - Ruby On Rails Guides
Chapters
1. Introduction
2. Sending Emails
Walkthrough to Generating a Mailer
Mailer Views
Previewing Emails
3. Receiving Emails
7. Mailer Testing
8. Intercepting Emails
1 Introduction
Action Mailer allows you to send emails from your application using mailer classes and views. Mailers work
very similarly to controllers. They inherit fromActionMailer::Base and live in app/mailers, and they have
associated views that appear inapp/views.
2 Sending Emails
This section will provide a step-by-step guide to creating a mailer and its views.
# app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
default from: "[email protected]"
layout 'mailer'
end
# app/mailers/user_mailer.rb
class UserMailer < ApplicationMailer
end
As you can see, you can generate mailers just like you use other generators with Rails. Mailers are
conceptually similar to controllers, and so we get a mailer
, a directory for views, and a test.
If you didn't want to use a generator, you could create your own file inside of app/mailers, just make sure that
it inherits from ActionMailer::Base:
class MyMailer < ActionMailer::Base
end
Let's add a method calledwelcome_email, that will send an email to the user's registered email address:
def welcome_email(user)
@user = user
@url = 'http://example.com/login'
mail(to: @user.email, subject: 'Welcome to My Awesome Site')
end
end
Here is a quick explanation of the items presented in the preceding method. For a full list of all available
options, please have a look further down at the Complete List of Action Mailer user-settable attributes
section.
default Hash - This is a hash of default values for any email you send from this mailer
. In this case
we are setting the :from header to a value for all messages in this class. This can be overridden on
a per-email basis.
mail - The actual email message, we are passing the:to and :subject headers in.
Just like controllers, any instance variables we define in the method become available for use in the views.
<!DOCTYPE html>
<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type'
/>
</head>
<body>
<h1>Welcome to example.com, <%= @user.name %></h1>
<p>
You have successfully signed up to example.com,
your username is: <%= @user.login %>.<br>
</p>
<p>
To login to the site, just follow this link:<%= @url %>.
</p>
<p>Thanks for joining and have a great day!</p>
</body>
</html>
Let's also make a text part for this email. Not all clients prefer HTML emails, and so sending both is best
practice. To do this, create a file calledwelcome_email.text.erb in app/views/user_mailer/:
When you call the mail method now, Action Mailer will detect the twotemplates (text and HTML) and
automatically generate amultipart/alternative email.
Now that we have a user model to play with, we will just edit theapp/controllers/users_controller.rb
make it instruct the UserMailer to deliver an email to the newly created user by editing the create action and
inserting a call to UserMailer.welcome_email right after the user is successfully saved.
Action Mailer is nicely integrated with Active Job so you can send emails outside of the request-response
cycle, so the user doesn't have to wait on it:
respond_to do |format|
if @user.save
# Tell the UserMailer to send a welcome email after save
UserMailer.welcome_email(@user).deliver_later
Active Job's default behavior is to execute jobs via the:async adapter. So, you can use deliver_later now
to send emails asynchronously. Active Job's default adapter runs jobs with an in-processhread
t pool. It's well-
suited for the development/test environments, since it doesn't require any external infrastructure, but it's a
poor fit for production since it drops pending jobs on restart. If you need a persistent backend, you will need
to use an Active Job adapter that has a persistent backend (Sidekiq, Resque, etc).
If you want to send emails right away (from a cronjob for example) just call
deliver_now:
class SendWeeklySummary
def run
User.find_each do |user|
UserMailer.weekly_summary(user).deliver_now
end
end
end
The method welcome_email returns an ActionMailer::MessageDelivery object which can then just be
told deliver_now or deliver_later to send itself out. TheActionMailer::MessageDelivery object is
just a wrapper around aMail::Message. If you want to inspect, alter or do anything else with the
Mail::Message object you can access it with themessage method on the
ActionMailer::MessageDelivery object.
For more complex examples such as defining alternate character sets or self-encoding text first, please refer
to the Mail library.
2.3 Complete List of Action Mailer Methods
There are just three methods that you need to send pretty much any email message:
Pass the file name and content and Action Mailer and theMail gem will automatically guess the
mime_type, set the encoding and create the attachment.
attachments['filename.jpg'] = File.read('/path/to/filename.jpg')
When the mail method will be triggered, it will send a multipart email withan attachment, properly nested
with the top level beingmultipart/mixed and the first part being a multipart/alternative containing the
plain text and HTML email messages.
Mail will automatically Base64 encode an attachment. If you want something dif
ferent, encode your content
and pass in the encoded content and encoding in aHash to the attachments method.
Pass the file name and specify headers and content and Action Mailer and Mail will use the settings
you pass in.
encoded_content =
SpecialEncode(File.read('/path/to/filename.jpg'))
attachments['filename.jpg'] = {
mime_type: 'application/gzip',
encoding: 'SpecialEncoding',
content: encoded_content
}
If you specify an encoding, Mail will assume that your content is already encoded and not try to Base64
encode it.
First, to tell Mail to turn an attachment into an inline attachment, you just call
#inline on the
attachments method within your Mailer:
def welcome
attachments.inline['image.jpg'] =
File.read('/path/to/image.jpg')
end
Then in your view, you can just referenceattachments as a hash and specify which attachment
you want to show, calling url on it and then passing the result into theimage_tag method:
As this is a standard call toimage_tag you can pass in an options hash after the attachment URL
as you could for any other image:
def new_registration(user)
@user = user
mail(subject: "New User Signup: #{@user.email}")
end
end
The same format can be used to set carbon copy (Cc:) and blind carbon copy (Bcc:) recipients, by using the
:cc and :bcc keys respectively.
def welcome_email(user)
@user = user
email_with_name = %("#{@user.name}" <#{@user.email}>)
mail(to: email_with_name, subject: 'Welcome to My Awesome Site')
end
To change the default mailer view for your action you do something like:
def welcome_email(user)
@user = user
@url = 'http://example.com/login'
mail(to: @user.email,
subject: 'Welcome to My Awesome Site',
template_path: 'notifications',
template_name: 'another')
end
end
In this case it will look for templates atapp/views/notifications with name another. You can also specify
an array of paths for template_path, and they will be searched in order.
If you want more flexibility you can also pass a block and render specific templates or even render inline or
text without using a template file:
def welcome_email(user)
@user = user
@url = 'http://example.com/login'
mail(to: @user.email,
subject: 'Welcome to My Awesome Site') do |format|
format.html { render 'another_template' }
format.text { render text: 'Render text' }
end
end
end
This will render the template 'another_template.html.erb' for the HTML part and use the rendered text for the
text part. The render command is the same one used inside of Action Controller, so you can use all the same
options, such as :text, :inline etc.
And in order to use this feature, you need to configure your application with this:
config.action_mailer.perform_caching = true
Just like with controller views, useyield to render the view inside the layout.
You can also pass in a layout: 'layout_name' option to the render call inside the format block to specify
different layouts for different formats:
Will render the HTML part using themy_layout.html.erb file and the text part with the usual
user_mailer.text.erb file if it exists.
By default, these preview classes live intest/mailers/previews. This can be configured using the
preview_path option. For example, if you want to change it tolib/mailer_previews, you can configure it
in config/application.rb:
config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews"
As the :host usually is consistent across the application you can configure it globally in
config/application.rb:
config.action_mailer.default_url_options = { host:'example.com' }
Because of this behavior you cannot use any of the*_path helpers inside of an email. Instead you will need
to use the associated *_url helper. For example instead of using
By using the full URL, your links will now work in your emails.
If you did not configure the:host option globally make sure to pass it to the url helper
.
non-GET links require jQuery UJS and won't work in mailer templates. They will result in normalGET
requests.
As the :asset_host usually is consistent across the application you can configure it globally in
config/application.rb:
config.action_mailer.asset_host = 'http://example.com'
The order of the parts getting inserted is determined by the:parts_order inside of the
ActionMailer::Base.default method.
3 Receiving Emails
Receiving and parsing emails with Action Mailer can be a rather complex endeavor . Before your email
reaches your Rails app, you would have had to configure your system to somehow forward emails to your
app, which needs to be listening for that. So, to receive emails in your Rails app you'll need to:
Configure your email server to forward emails from the address(es) you would like your app to
receive to /path/to/app/bin/rails runner
'UserMailer.receive(STDIN.read)'.
Once a method calledreceive is defined in any mailer, Action Mailer will parse the raw incoming email into
an email object, decode it, instantiate a new mailer
, and pass the email object to the mailerreceive instance
method. Here's an example:
if email.has_attachments?
email.attachments.each do |attachment|
page.attachments.create({
file: attachment,
description: email.subject
})
end
end
end
end
Filters can be specified with a block or a symbol to a method in the mailer class similar to
controllers.
You could use a before_action to populate the mail object with defaults, delivery_method_options
or insert default headers and attachments.
You could use an after_action to do similar setup as abefore_action but using instance
variables set in your mailer action.
private
def set_delivery_options
# You have access to the mail instance,
# @business and @user instance variables here
if @business && @business.has_smtp_settings?
mail.delivery_method.settings.merge!(@business.smtp_settings)
end
end
def prevent_delivery_to_guests
if @user && @user.guest?
mail.perform_deliveries = false
end
end
def set_business_headers
if @business
headers["X-SMTPAPI-CATEGORY"] = @business.code
end
end
end
Configuration Description
Generates information on the mailing run if available. Can be set tonil for
logger
no logging. Compatible with both Ruby's ownLogger and Log4r loggers.
Configuration Description
Keeps an array of all the emails sent out through the Action Mailer with
deliveries
delivery_method :test. Most useful for unit and functional testing.
Allows you to set default values for themail method options (:from,
default_options
:reply_to, etc.).
For a complete writeup of possible configurations see theConfiguring Action Mailerin our Configuring Rails
Applications guide.
config.action_mailer.delivery_method =:sendmail
# Defaults to:
# config.action_mailer.sendmail_settings = {
# location: '/usr/sbin/sendmail',
# arguments: '-i'
# }
config.action_mailer.perform_deliveries =true
config.action_mailer.raise_delivery_errors =true
config.action_mailer.default_options = {from:'[email protected]'}
config.action_mailer.delivery_method =:smtp
config.action_mailer.smtp_settings = {
address: 'smtp.gmail.com',
port: 587,
domain: 'example.com',
user_name: '<username>',
password: '<password>',
authentication: 'plain',
enable_starttls_auto: true }
Note: As of July 15, 2014, Google increasedits security measuresand now blocks attempts from apps it
deems less secure. You can change your gmail settingshere to allow the attempts or use another ESP to
send email by replacing 'smtp.gmail.com' above with the address of your provider
.
7 Mailer Testing
You can find detailed instructions on how to est
t your mailers in thetesting guide.
8 Intercepting Emails
There are situations where you need to edit an email before it's delivered. Fortunately Action Mailer provides
hooks to intercept every email. You can register an interceptor to make modifications to mail messages right
before they are handed to the delivery agents.
class SandboxEmailInterceptor
def self.delivering_email(message)
message.to = ['[email protected]']
end
end
Before the interceptor can do its job you need to register it with the Action Mailer framework.ou
Y can do this
in an initializer file config/initializers/sandbox_email_interceptor.rb
if Rails.env.staging?
ActionMailer::Base.register_interceptor(SandboxEmailInterceptor)
end
The example above uses a custom environment called "staging" for a production like server but for testing
purposes. You can read Creating Rails environmentsfor more information about custom Rails
environments.
Feedback
You're encouraged to help improve the quality of this guide.
Please contribute if you see any typos or factual errors.To get started, you can read ourdocumentation
contributions section.
You may also find incomplete content, or stuff that is not up to date.Please do add any missing
documentation for master. Make sure to check Edge Guides first to verify if the issues are already fixed or
not on the master branch.Check the Ruby on Rails Guides Guidelinesfor style and conventions.
If for whatever reason you spot something to fix but cannot patch it yourself, please
open an issue.
And last but not least, any kind of discussion regarding Ruby on Railsdocumentation is very welcome in the
rubyonrails-docs mailing list.
"Rails", "Ruby on Rails", and the Rails logo are trademarks of David Heinemeier Hansson. All rights reserved.