LaunchSchool - An Online School for Developers /

Blog

Sending Emails in Rails Applications

Introduction

In this article we will walk through a simple app to demonstrate how to send emails through a Rails application with ActionMailer, ActionMailer Preview, and through a third party email service provider such as Gmail or Mailgun. We will also demostrate how to use Active Job to send the email with a background processor.

You can find the code for this tutorial here

Sending Emails Using ActionMailer and Gmail

Now we will build a rails application which will send an email to the user when a new user is created. Let’s create a new rails application.

1
2
3
$ rails new new_app_name
$ rails g scaffold user name:string email:string
$ rake db:migrate

We now have a basic application, let’s make use of ActionMailer. The mailer generator is similar to any other generator in rails.

1
2
3
4
5
6
7
$ rails g mailer example_mailer
create  app/mailers/example_mailer.rb
invoke  erb
create    app/views/example_mailer
invoke  test_unit
create    test/mailers/example_mailer_test.rb
create    test/mailers/previews/example_mailer_preview.rb

Our application is currently using Rails 4.2.0.beta4 so the rails generator has created preview files for our application by default as test/mailers/previews/example_mailer_preview.rb which we will be using later.

app/mailers/example_mailer.rb

1
2
3
class ExampleMailer < ActionMailer::Base
  default from: "from@example.com"
end

Now let’s write methods and customized email. First you should change the default email address from from@example.com to the email address you want use as the sender’s address.

1
2
3
4
5
6
7
8
class ExampleMailer < ActionMailer::Base
  default from: "from@example.com"

  def sample_email(user)
    @user = user
    mail(to: @user.email, subject: 'Sample Email')
  end
end

sample_email takes user parameters and sends email using method mail to email address of user. In case you want to know about more features like attachment and multiple receivers, you can check out rails guide in the reference section. Now let’s write the mail we want to send to our users, and this can be done in app/views/example_mailer. Create a file sample_email.html.erb which is an email formatted in HTML.

app/views/example_mailer/sample_email.html.erb

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
  <head>
    <meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
  </head>
  <body>
    <h1>Hi <%= @user.name %></h1>
    <p>
      Sample mail sent using smtp.
    </p>
  </body>
</html>

We also need to create the text part for this email as not all clients prefer HTML emails. Create sample_email.text.erb in the app/views/example_mailer directory.

app/views/example_mailer/sample_email.text.erb

1
2
Hi <%= @user.name %>
Sample mail sent using smtp.

In the development environment we can use ActionMailer Preview to test our application. We are going to use the test/mailers/previews/example_mailer_preview.rb file created while generating mailers. We will just call any user (first user in this case) to preview the email.

test/mailers/previews/example_mailer_preview.rb

1
2
3
4
5
6
# Preview all emails at http://localhost:3000/rails/mailers/example_mailer
class ExampleMailerPreview < ActionMailer::Preview
  def sample_mail_preview
    ExampleMailer.sample_email(User.first)
  end
end

When you visit http://localhost:3000/rails/mailers/example_mailer/sample_mail_preview you will see preview of the email. By default email previews are placed in test/mailers/previews. You can change this by setting up different a path in /config/environments/development.rb. Just set config.action_mailer.preview_path to the desired path and add preview file to the corresponding location.

Sending emails using ActionMailer and Gmail

By default rails tries to send emails via SMTP. We will provide SMTP configuration in environment settings /config/environments/production.rb. Let’s first look at the configuration you need to send emails with Gmail. Before we proceed we need to save sensitive information such as username and password as environment variables. We will do so by using the gem figaro. For detailed information on how to manage environment variables in rails refer to Manage Environment Configuration Variables in Rails.

/config/application.yml

1
2
gmail_username: 'username@gmail.com'
gmail_password: 'Gmail password'

/config/environments/production.rb

1
2
3
4
5
6
7
8
9
10
config.action_mailer.delivery_method = :smtp
# SMTP settings for gmail
config.action_mailer.smtp_settings = {
 :address              => "smtp.gmail.com",
 :port                 => 587,
 :user_name            => ENV['gmail_username'],
 :password             => ENV['gmail_password'],
 :authentication       => "plain",
:enable_starttls_auto => true
}

Note here we are setting the app to send out emails with Gmail from the production environment. This is typically what you want because you don’t want to accidentally send out emails while working locally. If you run into errors like Net::SMTPAuthenticationError while using gmail for sending out emails, visit your gmail settings and enable less secure apps to get the application working.

Now let’s edit the UsersController to trigger the event that will send an email to a user. We just need to add ExampleMailer.sample_email(@user).deliver to the create method in app/controllers/users_controller.rb. The create method in users_controller.rb should look something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def create
  @user = User.new(user_params)

  respond_to do |format|
    if @user.save

      # Sends email to user when user is created.
      ExampleMailer.sample_email(@user).deliver

      format.html { redirect_to @user, notice: 'User was successfully created.' }
      format.json { render :show, status: :created, location: @user }
    else
      format.html { render :new }
      format.json { render json: @user.errors, status: :unprocessable_entity }
    end
  end
end

When a new user is created we are sending out an email via the sample_email method in mailer ExampleMailer.

Sending emails using ActionMailer and Mailgun through SMTP

Let’s see how to use mailgun to send out emails. First create a free account on Mailgun. Once done you will be redirected to the dashboard. We will be using mailgun subdomains. Click on the sandbox, you should see something like this:

We are going to need information listed to get mailgun working for our application. Let’s store the credentials in /config/application.yml.

/config/application.yml

1
2
3
4
5
6
api_key: 'API Key'
domain: 'Domain'
username: 'Default SMTP Login'
password: 'Default Password'
gmail_username: 'username@gmail.com'
gmail_password: 'gmail password'

Replace the corresponding credentials received from domain information of sandbox server. We must also change SMTP settings as we are now using Mailgun instead of Gmail.

/config/environments/production.rb

1
2
3
4
5
6
7
8
9
10
config.action_mailer.delivery_method = :smtp
# SMTP settings for mailgun
ActionMailer::Base.smtp_settings = {
  :port           => 587,
  :address        => "smtp.mailgun.org",
  :domain         => ENV['domain'],
  :user_name      => ENV['username'],
  :password       => ENV['password'],
  :authentication => :plain,
}}

Sending emails using ActionMailer and Mailgun through Mailgun’s APIs

The official ruby library of Mailgun mailgun-ruby empowers users and developers to take advantage of the Mailgun APIs. To use it first add gem 'mailgun-ruby', '~>1.0.2', require: 'mailgun' to your Gemfile and run bundle install. Finally make changes to app/mailers/example_mailer.rb.

1
2
3
4
5
6
7
8
9
10
11
12
class ExampleMailer < ActionMailer::Base

  def sample_email(user)
    @user = user
    mg_client = Mailgun::Client.new ENV['api_key']
    message_params = {:from    => ENV['gmail_username'],
                      :to      => @user.email,
                      :subject => 'Sample Mail using Mailgun API',
                      :text    => 'This mail is sent using Mailgun API via mailgun-ruby'}
    mg_client.send_message ENV['domain'], message_params
  end
end

Mailgun::Client.new initiates mailgun client using the API keys. In message_params we are providing custom email information and .send_message takes care of sending emails via Mailgun API. You should change from@example.com to desired sender’s email address.

Sending Emails with a Background Processor through Active Job

While testing the application you might have noticed that it takes takes more time than usual to create a new user. This happens because we have to hit an external API to send out emails. This can be an issue if you are sending multiple emails or sending emails to multiple users. This problem can be easily resolved by moving the email sending part to background jobs. In our application we will make use of Active Jobs and Delayed Job to send emails in the background.

Active Job is an adapter that provides a universal interface for background processors like Resque, Delayed Job, Sidekiq, etc. Note that for using Active Job you will need Rails 4.2 or above.

1
2
3
4
5
$ rails g job send_email
  invoke  test_unit
  create    test/jobs/send_email_job_test.rb
  create  app/jobs/send_email_job.rb
$

Now let’s the write the job to be performed by workers. Active Job is integrated with ActionMailer so you can easily send emails asynchronously. For sending emails through Active Job we use deliver_later method.

/app/jobs/send_email_job.rb

1
2
3
4
5
6
7
8
class SendEmailJob < ActiveJob::Base
  queue_as :default

  def perform(user)
    @user = user
    ExampleMailer.sample_email(@user).deliver_later
  end
end

Now let’s make changes to our user creation process. Instead of sending email while creating the user we enqueue the email sending job to be performed later.

app/controllers/users_controller.rb

1
2
3
4
5
  def create
    ...
    SendEmailJob.set(wait: 20.seconds).perform_later(@user)
    ....
  end

Now we need to configure the backend for our background process. We have selected delayed_jobs as our backend but you can choose your own backend depedning on your needs. Active Job has built-in adapters for multiple queueing backends.

Gemfile

1
gem 'delayed_job_active_record'
1
2
3
$ bundle
$ rails generate delayed_job:active_record
$ rake db:migrate

Set up queueing backend for the production environment.

/config/environments/production.rb

1
config.active_job.queue_adapter = :delayed_job

Everything is configured now, for testing the application just start the rails server and create a new user. A new job will be added to the queue and you will notice the time required for creating a new user is drastically decreased. You can start running the jobs in queue by:

1
$ bundle exec rake jobs:work

Conclusion

In the article we went over basic configuration and tools used for sending emails through a rails application. We covered the basics of ActionMailer, Gmail & Mailgun (as email sending services), ActionMailer Previews(for previewing emails) and mailgun-ruby gem for the Mailgun APIs. In the end, we showed how to send out emails with a background processor through Active Job.

References/Resources