Neu: Das englische Ruby on Rails 4.0 Buch.

Kapitel 9. Action Mailer

Auch wenn wir mit Rails hauptsächlich Webseiten generieren, so ist es doch hin und wieder ganz praktisch, auch mal eine E-Mail verschicken zu können.
Bauen wir uns mal ein Beispiel mit einer minimalen Userverwaltung für einen Webshop, die beim Anlegen eines neuen Users diesem automatisch eine E-Mail schickt:
MacBook:~ xyz$ rails new webshop
[...]
MacBook:~ xyz$ cd webshop 
MacBook:webshop xyz$ rails generate scaffold User name email
[...]
MacBook:webshop xyz$ rake db:migrate
[...]
MacBook:webshop xyz$
Für das User-Model erstellen wir in der app/models/user.rb noch eine minimale Validierung, damit wir sichergehen können, dass jeder User einen Namen und eine syntaktisch korrekte E-Mail-Adresse hat.
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
Es gibt einen Generator namens mailer, der für uns die fürs Mailen notwendigen Dateien anlegt. Als Erstes schauen wir uns die Ausgabe des rails generate mailer an, ohne dass wir weitere Argumente übergeben:
MacBook:webshop xyz$ 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]    # Supress 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 [...]

MacBook:webshop xyz$
Das ist alles so, wie wir es erwarten. Legen wir also den Mailer notification an:
MacBook:webshop xyz$ rails generate mailer notification
      create  app/mailers/notification.rb
      invoke  erb
      create    app/views/notification
      invoke  test_unit
      create    test/functional/notification_test.rb
MacBook:webshop xyz$ 
In der Datei app/mailers/notification.rb finden Sie den Controller:
class Notification < ActionMailer::Base
  default from: "from@example.com"
end
Wir legen darin eine Methode new_account(user) an, mit der wir die Bestätigungs-E-Mail für einen neuen Account verschicken:
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
Danach legen wir den View zu dieser Methode an. Der Dateiname app/views/notification/new_account.text.erb setzt sich dabei aus dem Methodennamen und der Endung text.erb zusammen.
Hello <%= @user.name %>,

your new account is active.

Have a great day!
  A Robot
Da wir diese E-Mail nach dem create eines Users verschicken wollen, müssen wir noch die app/models/user.rb-Datei anpassen:
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
Nachfolgend legen wir in der Console einen neuen User an:
MacBook:webshop xyz$ rails console
Loading development environment (Rails 3.2.3)
1.9.3p194 :001 > User.create(name: 'Wintermeyer', email: 'stefan.wintermeyer@amooma.de')
   (0.1ms)  begin transaction
  SQL (7.7ms)  INSERT INTO "users" ("created_at", "email", "name", "updated_at") VALUES (?, ?, ?, ?)  [["created_at", Wed, 30 May 2012 15:30:30 UTC +00:00], ["email", "stefan.wintermeyer@amooma.de"], ["name", "Wintermeyer"], ["updated_at", Wed, 30 May 2012 15:30:30 UTC +00:00]]
   (1.4ms)  commit transaction
 => #<User id: 1, name: "Wintermeyer", email: "stefan.wintermeyer@amooma.de", created_at: "2012-05-30 15:30:30", updated_at: "2012-05-30 15:30:30"> 
1.9.3p194 :002 > exit
MacBook:webshop xyz$ 
Das war unspektakulär. Werfen wir nun einen Blick in die Log-Datei log/development.log:
  Rendered notification/new_account.text.erb (2.5ms)

Sent mail to stefan.wintermeyer@amooma.de (58ms)
Date: Wed, 30 May 2012 17:30:31 +0200
From: from@example.com
To: stefan.wintermeyer@amooma.de
Message-ID: <4fc63d175ea71_a9f03fceda028cd8377c5@MacBook.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
Da wir im „development“-Modus arbeiten, hat Rails die E-Mail nicht verschickt, sondern nur in die log/development.log den Inhalt hineingeschrieben.

Autor

Stefan Wintermeyer