How to use Rails to send email with ActionMailer

Before I was a budding programmer, I helped manage a sheet music eCommerce site. I worked at a small company run by passionate musicians who built their lives around their goal of helping composers reach larger audiences. Before we moved to a modern eCommerce platform in summer 2015, my bosses rarely had time to develop new projects for our clients: we spent most of the day maintaining our website and communicating with customers. In short, the artists I worked with rarely had time to be artists.

Any online business is likely to send a whole lot of email. We used email to create new accounts, reset passwords, confirm orders, send digital products, send marketing material, and provide customer support. If we had to perform each of these tasks manually, we’d only be able to process orders from a handful of accounts every day. For even the smallest business, most email needs to happen automatically. Can you imagine placing an order and having to wait hours (or days!) until a site administrator saw your order and confirmed it with you? Or having to (gulp) call someone if you needed something urgently, like a password reset?

Thankfully, we don’t have to live like manual-emailing monsters, because Rails has ActionMailer!

What is ActionMailer?

ActionMailer is a Rails class that — you guessed it! — sends email. Like most other Rails assets, Mailer models can be generated from the command line:
$ rails g mailer my_mailer
Running via Spring preloader in process 98534
create app/mailers/my_mailer.rb
identical app/mailers/application_mailer.rb
invoke erb
create app/views/my_mailer
identical app/views/layouts/mailer.text.erb
identical app/views/layouts/mailer.html.erb
invoke test_unit
create test/mailers/my_mailer_test.rb
create test/mailers/previews/my_mailer_preview.rb

As the Rails documentation explains, ActionMailer renders email templates in a similar way to how ActionController renders views. ActionMailer has the additional functionality of receiving email; for example, incoming email could be rendered into a blog post using a #receive method.

ApplicationMailer & Mailer classes

Like controllers, Mailer classes all inherit from a standard ApplicationMailer class, which inherits from ActionMailer::Base. This is what our mailers look like out of the box:

# app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
  default from: "from@example.com"
  layout 'mailer'
end
# app/mailers/my_mailer.rb
class MyMailer < ApplicationMailer
end

We’ll want to add a default hash to include the right from: email address. (Or we can just set the default in ApplicationMailer. In fact, we don’t need to use any generator at all if we want, so long as our classes inherit from ActionMailer::Base).

Now let’s add some methods to our mailer!

Basic methods

A sample instance method would look like this:


class MyMailer < ApplicationMailer
  default from: 'mymailer@example.com'
  def welcome(user)
    @user = user
    mail(to: @user.email, subject: 'Welcome to our site!')
  end
end

In case you haven’t noticed, mailers are very similar to controllers. ActionMailer does have its own methods:

  • Attachments: An easy method to add attachments: attachments['example.jpg'] = File.read('/path/to/example.jpg')
  • Attachments.inline: In Rails 3.0 or later, inline attachments can be specified as such. The syntax is the same as above (with “attachments.inline” replacing “attachments”).
  • Headers: Most headers (i.e. subject, sender, etc) are already defined, but custom headers can be created with a hash: headers['X-Special-Header'] = special_value
  • Mail: As seen above, a method that defines the mail object with a hash.

Views & templates

Mailer views live in the app/views directory in folders by mailer class name. The path for the views for this example mailer would be /app/views/my_mailer. Templates can be written in .html.erb or .text.erb files; I recommend creating both for each view as some email clients open emails as plain text by default.

One major difference between mailer views and controller views is the use of URL helpers. Host urls can be configured in config/application.rb for global use or can be passed in to a URL helper. A dynamic link to a user’s profile might look like this:
<%= user_url(@user, host: 'example.com') %>

Note the difference in syntax: instead of _path helpers, mailer views use _url. For example, the link to a welcome page might be welcome_path in a controller view, but for a mailer view, we’d use welcome_url.

Here’s an example view for the welcome email to my brand new website, SpookySkeltal.com:

Welcome to spookyskeltal.com, <%= @user.name %>
Your registration was successful. Your username is: <%= @user.username %>.

View your profile at <%= user_url(@user, host: 'spookyskeltal.com') %>

 

Sending email

Rails handles the actual sending of email with ActiveJob, which I’ll explore in a follow up. ActiveJob allows for tasks to be completed outside of a request/response cycle, which is particularly important for email, as you’ll likely need to send emails independent of users’ interaction with your Rails application. But for now, we can stick to the simple ActiveJob method .deliver_now to send an email immediately. It’s easy to associate an email with a controller method. For example, to send a welcome email immediately upon user creation:
class UsersController < ApplicationController
  def create
    @user = User.new(user_params)
    if @user.save
      MyMailer.welcome(@user).deliver_now
      redirect_to :index
    else
      render :new
    end
  end
end

Now you’re ready to stay in touch with your users without living at your keyboard all day!

Leave a Reply

Your email address will not be published. Required fields are marked *