By Scott Hale & Javier Soto February 24, 2015

[Tutorial] How to Set up a Rails 4.2 Mailer with Sidekiq

Sidekiq is a simple, open source background processor for Ruby. This tutorial explains how to set up your app in Rails 4.2 with background jobs from Sidekiq using Redis.

In a nutshell, when we create background jobs, we pass an object, with the data to use, to the worker. In most cases, we want the background job to reference a Model object (e.g. ActiveRecord), so to make things simpler, we just pass the ID of the object to the worker. Then Rails and Sidekiq take care of everything for us (this includes finding the object in the database, then serializing and de-serializing it).

In this template we have taken a different route. Instead of passing an existing Model object, we’ll pass an ad-hoc one, created on the spot and stored in a hash that we’ll pass to the worker in JSON format. Then it is up to us to tell the worker how to de-serialize it, reconstituting the original hash from the JSON string, to be able to operate on the data passed.

All this will become more clear with an example.

Our app will have a simple contact page with a custom form that the user fills in with some requested information. Upon submission of the form, the app will create a background job to send an email, deferring the responsibility to Sidekiq.

1. Prep Work

Add Letter Opener and Launchy gems to your Gemfile:

gem "letter_opener"
gem "launchy"

In development, letter_opener allows us to simulate the process of sending the email by creating it as a temporary file. That way we will avoid sending the actual email over the network, which is messy and brittle to test.

Launchy automatically opens the created temp file in a browser window so that the sending process becomes automatic and we have real-time confirmation that the email was sent correctly.

Modify the config/environments/development.rb:

# to be appraised of mailing errors
config.action_mailer.raise_delivery_errors = true
# to deliver to the browser instead of email
config.action_mailer.delivery_method = :letter_opener

2. Mailer

Generate a Mailer

This project starts with a basic new Rails application without controllers, models, or views. Generate a mailer:

$ rails g mailer VisitorMailer

Create the Mailer Action

Next, receive the information needed to build the email (name, email address, and body of email) in the Mailer action. Instance variables defined here are available to the corresponding view (the email template).

Keep in mind that the contact_email method is the one that the worker (background process) will execute.

class VisitorMailer < ActionMailer::Base
  def contact_email(name, email, message)
    @name = name
    @email = email
    @message = message
    mail(from: @email,
         to: 'javier@badaboom.com',
         subject: 'New Visitor\'s Email')
  end
end

Create the E-Mail Template

The “view” associated with the Mailer method is the actual template for the email to be sent. Make two versions, in HTML and text format, with the information passed through instance variables.

 <html>
   <head>
     <meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
   </head>
   <body>
     <h1><%= @name %> (<%= @email %>)</h1>
     <p>
       <%= @message %>
     </p>
   </body>
 </html>

3. The Resource

Generate the Controller

Now that the Mailer is set and done, generate the VisitorsController.

$ rails g controller visitors

We add two actions. The index actions displays the basic contact form. Once submitted, it reaches the contact action where we extract the form parameters.

The form information is packaged into a hash and subsequently JSONified so we can pass it as an argument to the worker (a Sidekiq requirement).

class VisitorsController < ApplicationController
  def index
  end

  def contact
    h = JSON.generate({ 'name' => params[:name],
                        'email' => params[:email],
                        'message' => params[:message] })

    PostmanWorker.perform_async(h, 5)

    # if instead of sidekiq I was just sending email from rails
    # VisitorMailer.contact_email(@name, @email, @message).deliver

    redirect_to :root
  end
end

Make a Small Form for the View

Simplicity personified:

Simple contact form

Update the Routes

We haven’t done it yet and we cannot defer anymore. It’s time to establish our routes and root.

Rails.application.routes.draw do
  post 'visitors/contact', to: 'visitors#contact'
  root 'visitors#index'
end

The Model?

Nope, no model. The controller just passes the information received from the form directly to the worker.

4. The Background Worker

Install Sidekiq

Add to your Gemfile and don’t forget to bundle up.

gem 'sidekiq'

Create a Worker

In this step, follow the instructions from the Sidekiq’s README and docs, and create a worker responsible for delivering emails: a Postman worker.

The key here is that the worker needs a JSON object as simple as possible. Usually this would be the ID from a Model object, in which case Sidekiq would serialize and de-serialize the object referenced by the ID.

In our case, the information is not stored in the database, so we create a JSON hash that we passed to the worker for queuing in Redis. Now, the key is that we also need to de-serialize this JSON object upon arrival to re-create the hash. Once re-constituted, the hash gives us access to the data that we need in order to call the ActionMailer and deliver the email.

class PostmanWorker
  include Sidekiq::Worker

  def perform(h, count)
    h = JSON.load(h)
    VisitorMailer.contact_email(h['name'], h['email'], h['message']).deliver
  end
end

The results show up in the browser when the email is sent:

Email sent to us

Add Dashboard

The nifty Sidekiq Dashboard runs on Sinatra, which we have to add to the Gemfile.

gem 'sinatra', '>= 1.3.0', :require => nil

Add it to the routes:

require 'sidekiq/web'
mount Sidekiq::Web => '/sidekiq'

This makes it available (depending on your setup) in localhost:3000/sidekiq

dashboard

Be aware that anybody can access this dashboard once in production, so check for ways to secure its access.

5. Final Setup

Make sure that you have run bundle, and install Redis if it’s not already in place (I recommend using brew on Mac OS X whenever possible to avoid headaches).

Then all that is left to do is to start each service in its own tab, like this:

$ rails s

$ redis-server

$ bundle exec sidekiq

You can also use Foreman and save yourself opening tabs and running things separately.

We haven’t included tests in this guide. So it is up to you to get these components of the app covered with the necessary tests. Good luck, and have fun with your new background tasks!


Ready to become a professional software developer? Learn more about getting trained in advanced software development »