Chapter 9. Action Mailer

Even if we mainly use Rails to generate web pages, it can sometimes be useful to be able to send an e-mail.
So let's go and build an example with minimal user management for a web shop that automatically sends an e-mail to the user when a new user is created:
$ rails new webshop
  [...]
$ cd webshop 
$ rails generate scaffold User name email
  [...]
$ rake db:migrate
  [...]
$
For the user model we create a minimal validation in the app/models/user.rb, so that we can be sure that each user has a name and a syntactically correct e-mail address.
class User < ActiveRecord::Base
  attr_accessible :email, :name

  validates :name,
            :presence => true

  validates :email,
            :presence => true,
            :format => { :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i }
end
There is a generator with the name mailer that creates the files required for mailing. First, we have a look at the output of the rails generate mailer, without passing any further arguments:
$ rails generate mailer
Usage:
  rails generate mailer NAME [method method] [options]

Options:
      [--skip-namespace]        # Skip namespace (affects only isolated applications)
      [--old-style-hash]        # Force using old style hash (:foo => 'bar') on Ruby >= 1.9
  -e, [--template-engine=NAME]  # Template engine to be invoked
                                # Default: erb
  -t, [--test-framework=NAME]   # Test framework to be invoked
                                # Default: test_unit

Runtime options:
  -f, [--force]    # Overwrite files that already exist
  -p, [--pretend]  # Run but do not make any changes
  -q, [--quiet]    # Suppress status output
  -s, [--skip]     # Skip files that already exist

Description:
============
    Stubs out a new mailer and its views. Pass the mailer name, either
    CamelCased or under_scored, and an optional list of emails as arguments.

    This generates a mailer class in app/mailers and invokes your template
    engine and test framework generators.

Example:
========
    rails generate mailer Notifications signup forgot_password invoice

    creates a Notifications mailer class, views, test, and fixtures:
        Mailer:     app/mailers/notifications.rb
        Views:      app/views/notifications/signup.erb [...]
        Test:       test/functional/notifications_test.rb
        Fixtures:   test/fixtures/notifications/signup [...]

$
That is just what we are expecting. So let's now create the mailer notification:
$ rails generate mailer notification
      create  app/mailers/notification.rb
      invoke  erb
      create    app/views/notification
      invoke  test_unit
      create    test/functional/notification_test.rb
$ 
In the file app/mailers/notification.rb you will find the controller:
class Notification < ActionMailer::Base
  default from: "from@example.com"
end
In it, we create a method new_account(user), with which we send the confirmation e-mail for a new account:
class Notification < ActionMailer::Base
  default from: "from@example.com"

  def new_account(user)
    @user = user
    mail(:to => user.email,
         :subject => "The new account #{user.name} is active.")
  end
end
Now we create the view for this method. The file name app/views/notification/new_account.text.erb is composed from the method name and the ending text.erb.
Hello <%= @user.name %>,

your new account is active.

Have a great day!
  A Robot
As we want to send this e-mail afer the create of a User, we still need to adapt the file app/models/user.rb:
class User < ActiveRecord::Base
  attr_accessible :email, :name

  validates :name,
            :presence => true

  validates :email,
            :presence => true,
            :format => { :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i }

  after_create :send_welcome_email
  
  private
  
  def send_welcome_email
    Notification.new_account(self).deliver
  end
end
Let's create a new User in the console:
$ rails console
Loading development environment (Rails 3.2.9)
>> User.create(name: 'Wintermeyer', email: 'stefan.wintermeyer@amooma.de')
   (0.1ms)  begin transaction
  SQL (5.2ms)  INSERT INTO "users" ("created_at", "email", "name", "updated_at") VALUES (?, ?, ?, ?)  [["created_at", Wed, 21 Nov 2012 18:53:34 UTC +00:00], ["email", "stefan.wintermeyer@amooma.de"], ["name", "Wintermeyer"], ["updated_at", Wed, 21 Nov 2012 18:53:34 UTC +00:00]]
   (0.6ms)  commit transaction
=> #<User id: 1, name: "Wintermeyer", email: "stefan.wintermeyer@amooma.de", created_at: "2012-11-21 18:53:34", updated_at: "2012-11-21 18:53:34">
>> exit
$ 
That was straightforward. Let's take a look at the log file log/development.log:
Connecting to database specified by database.yml
  Rendered notification/new_account.text.erb (26.2ms)

Sent mail to stefan.wintermeyer@amooma.de (54ms)
Date: Wed, 21 Nov 2012 19:53:36 +0100
From: from@example.com
To: stefan.wintermeyer@amooma.de
Message-ID: <50ad23301617a_d5233fea89c2a1d8458c8@SW.local.mail>
Subject: The new account Wintermeyer is active.
Mime-Version: 1.0
Content-Type: text/plain;
 charset=UTF-8
Content-Transfer-Encoding: 7bit

Hello Wintermeyer,

your new account is active.

Have a great day!
  A Robot
As we are working in "development" mode, Rails has not sent the e-mail but only written the content into the log/development.log.

Updates about this book will be published on my Twitter feed.