A Production-Ready Rails 5 Email Workflow with Mailer Previews, Premailer and ActiveJob

A Production-Ready Rails 5 Email Workflow with Mailer Previews, Premailer and ActiveJob

Since the introduction of Mailer Previews and ActiveJob in Rails 4.1, the framework has truly set itself apart from others in the comprehensiveness and comfort of its workflow around emails. The framework’s creator, DHH, has espoused a perspective that mailers are views, and accordingly Rails now gives us an email workflow that is nearly identical to that around ordinary controllers and views.

I will walk through my complete email stack and workflow, which I have used very effectively across several production apps.

As of Rails 5, the project generator will drop the following ApplicationMailer implementation into your project tree:


# in app/mailers/application_mailer.rb

class ApplicationMailer < ActionMailer::Base
  default from: 'from@example.com'
  layout 'mailer'
end

Perhaps the only notable omission from Rails’ stock email workflow is CSS inlining, but that is easily addressed with the premailer-rails gem, which we will take care of before we go any further:


# in your Gemfile, add premailer-rails with...

gem 'premailer-rails'

We should then run bundle install and restart our server with bin/rails server. We can now add a mailer.sass stylesheet to the base of our app/assets/stylesheets directory:

// in app/assets/stylesheets/mailer.sass

body
  padding: 0
  background: #f7f7f7
  text-align: center

.email-table
  width: 538px
  padding: 20px
  background: #fff
  margin: 40px auto
  text-align: left
  font-family: 'Helvetica Neue'

h1
  font-size: 20px

p
  margin-top: 0
  font-size: 15px

In order for premailer to pull and inline these styles in production, we will need to add this file to our precompile list:

# add in config/initializers/assets.rb...

Rails.application.config.assets.precompile += %w( mailer.css )

Rails’ project generator will drop a mailer layout into app/views/mailers/mailer.html.erb. Now all we have to do to add inline styles to our mailers is to reference this stylesheet from the layout with an ordinary stylesheet_link_tag. With this, premailer will implicitly handle inlining when the message is rendered and delivered.

<!-- in app/views/layouts/mailer.html.erb -->
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

    <!-- * simply referencing the stylesheet with this stylesheet_link_tag * -->
    <!-- * is enough for premailer to pull-in and inline your styles * -->
    <%= stylesheet_link_tag "mailer" %>

  </head>

  <body>
    <%= yield %>
  </body>
</html>

Let’s go ahead and subclass ApplicationMailer now with something concrete. For our purposes here, let’s create a UserMailer with a welcome action to deliver a welcome email to new registrants.

# in app/mailers/user_mailer.rb

class UserMailer < ApplicationMailer
  def welcome(user)
    @user = user
    mail(to: @user.email, subject: "Welcome to our site!")
  end
end

…and a corresponding template:

<!-- in app/views/user_mailer/welcome.html.erb -->

<table class="email-table">
  <tbody>
    <tr class="header-row">
      <td>
        <h1>Welcome, <%= @user.name %>!</h1>
      </td>
    </tr>
    <tr class="body-row">
      <td>
        <p>Thanks for your registering! We appreciate your interest.</p>
      </td>
    </tr>
  </tbody>
</table>

Now in order to work with this mailer as we would an ordinary view all we have to do is create a mailer preview for it. We can do this by dropping a file into test/mailers/previews:

# in test/mailers/previews/user_mailer_preview.rb

class UserMailerPreview < ActionMailer::Preview
  def welcome
    user = OpenStruct.new(email: "demo@example.com", name: "John Doe")
    UserMailer.welcome(user)
  end
end

Note that we are passing an OpenStruct with a few carefully defined attributes to the mailer here. In reality you will probably want to pull an actual record from your database via ActiveRecord or use a factory or fixture.

You can now navigate to http://localhost:3000/rails/mailers, where you will find an index of defined mailer previews, which at the moment will just show the above-defined UserMailer#welcome preview.

screen-shot-2016-09-23-at-10-21-46-pm

…and if you click through the link to “welcome” you will find a preview of the mailer, complete with inlined styles:

screen-shot-2016-09-23-at-10-27-20-pm

You can now work with your mailer and template in-browser as if they were an ordinary controller action and view.

The final piece to Rails’ email workflow worth noting is ActiveJob. As of Rails 4.2 ActionMailer has included an elegant ActiveJob integration in deliver_later. When you wish to deliver an email in your application and would have previously invoked deliver, instead invoke deliver_later and the delivery will be queued up in ActiveJob for asynchronous execution. For example…

UserMailer.welcome(@user).deliver_later

That is all there is to it.

Nicholas

Hi! I'm Nicholas and I like building stuff. I'm software development consultant in New York, where I work with startups big and small. I am also a sometime theatre and film nerd. You can reach me by email at nicholas@zaillian.com (public key here if you want to encrypt your message). I keep a personal homepage at nicholas.zaillian.com and accept consulting engagements through my company Written Software.

2 Comments

Paul C

about 3 months ago

Thank you for taking the time to write this straight forward and very helpful article. Just what I needed.

Reply

heriberto perez

about 1 week ago

Thank you very much for writing this article I was having some issues when using sass because I was using the gem `actionmailer_inline_css` but I just removed it and installed the `premailer-rails` instead and now it's working like a charm.

Reply

Leave a Comment

Please be polite. We appreciate that.
Your email address will not be published and required fields are marked