Action Mailer の基礎

本ガイドは、Railsアプリケーションからメールを送信する方法について解説します。

このガイドの内容:

  • Action Mailerクラスとメーラービューの生成および編集方法
  • Railsアプリケーションで添付ファイルやマルチパートメールを送信する方法
  • Action Mailerコールバックの利用方法
  • 環境に合わせてAction Mailerを設定する方法
  • メールのプレビュー方法と、Action Mailerクラスのテスト方法

1 Action Mailerについて

Action Mailerを使うと、アプリケーションからメールを送信できるようになります。Action Mailerは、Railsフレームワークにおけるメール関連コンポーネントの1つであり、メール受信を処理するAction Mailboxと対になります。

Action Mailerでは、「メーラー(mailer)」と呼ばれるクラスと、メールの作成や送信設定用のビューが使われます。メーラーは、ActionMailer::Baseクラスを継承します。

メーラーの振る舞いは、以下の点がコントローラときわめて似通っています。

  • ビューでアクセス可能なインスタンス変数がある
  • レイアウトやパーシャルを利用可能にする機能
  • paramsハッシュにアクセス可能にする機能
  • アクションと、関連するビュー(app/viewsディレクトリ以下に配置される)がある

2 メーラーとビューを作成する

このセクションでは、Action Mailerによるメール送信方法を手順を追って説明します。詳しい手順は以下の通りです。

2.1 メーラーを生成する

最初に、以下の「メーラー」ジェネレータコマンドを実行して、メーラー関連のクラスを作成します。

$ bin/rails generate mailer User
create  app/mailers/user_mailer.rb
invoke  erb
create    app/views/user_mailer
invoke  test_unit
create    test/mailers/user_mailer_test.rb
create    test/mailers/previews/user_mailer_preview.rb

生成されるすべてのメーラークラスは、以下のUserMailerと同様にApplicationMailerを継承します。

# app/mailers/user_mailer.rb
class UserMailer < ApplicationMailer
end

上のApplicationMailerクラスはActionMailer::Baseを継承しており、すべてのメーラーに共通する属性を以下のように定義できます。

# app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
  default from: "from@example.com"
  layout 'mailer'
end

ジェネレータを使いたくない場合は、app/mailersディレクトリ以下にファイルを手動で作成します。このクラスは、必ずActionMailer::Baseを継承するようにしてください。

# app/mailers/custom_mailer.rb
class CustomMailer < ApplicationMailer
end

2.2 メーラーを編集する

app/mailers/user_mailer.rbに最初に作成されるUserMailerクラスには、メソッドがありません。そこで、次に特定のメールを送信するメソッド(アクション)をメーラーに追加します。

メーラーには「アクション」と呼ばれるメソッドがあり、コントローラーのアクションと同様に、ビューを用いてコンテンツを構造化します。コントローラーはクライアントに送り返すHTMLコンテンツを生成しますが、メーラーはメールで配信されるメッセージを作成します。

UserMailerクラスに以下のようにwelcome_emailというメソッドを追加して、ユーザーの登録済みメールアドレスにメールを送信しましょう。

class UserMailer < ApplicationMailer
  default from: "notifications@example.com"

  def welcome_email
    @user = params[:user]
    @url  = "http://example.com/login"
    mail(to: @user.email, subject: "私の素敵なサイトへようこそ")
  end
end

メーラー内のメソッド名は、末尾を_emailにする必要はありません。

上のメソッドで使われているメーラー関連のメソッドについて簡単に説明します。

  • default: メーラーから送信するあらゆるメールで使われるデフォルト値のハッシュです。上の例の場合、:fromヘッダーにこのクラスのすべてのメッセージで使う値を1つ設定しています。この値はメールごとに上書きすることもできます。
  • mail: 実際のメールメッセージです。ここでは:toヘッダーと:subjectヘッダーを渡しています。

また、headersメソッドは、メールのヘッダーをハッシュで渡すか、headers[:field_name] = 'value'を呼び出すことで指定できます(上のサンプルで使っていません)。

以下のように、メーラーをジェネレータで生成するときに直接アクションを指定することも可能です。

$ bin/rails generate mailer User welcome_email

上のコマンドは、空のwelcome_emailメソッドを持つUserMailerクラスを生成します。

1個のメーラークラスから複数のメールを送信することも可能です。これは、関連するメールをグループ化するのに便利です。たとえば、、UserMailerクラスにはwelcome_emailメソッドの他に、password_reset_emailメソッドも追加できます。

2.2.1 メーラービューを作成する

次に、welcome_emailアクションに対応するwelcome_email.html.erbというファイル名のビューをapp/views/user_mailer/ディレクトリの下に作成する必要があります。以下は、ウェルカムメールに使えるHTMLテンプレートのサンプルです。

<h1><%= @user.name %>様、example.comへようこそ。</h1>
<p>
  example.comへのサインアップが成功しました。
  ユーザー名は「<%= @user.login %>」です。<br>
</p>
<p>
  このサイトにログインするには、次のリンクをクリックしてください: <%= link_to 'login`, login_url %>
</p>
<p>本サイトにユーザー登録いただきありがとうございます。</p>

上のサンプルは<body>タグの内容です。これは、<html>タグを含むデフォルトのメーラーレイアウトに埋め込まれます。詳しくは、メーラーのレイアウト を参照してください。

また、上のメールのテキストバージョンをapp/views/user_mailer/ディレクトリのwelcome_email.text.erbファイルに保存することも可能です(拡張子がhtml.erbではなく.text.erbである点にご注意ください)。テキストバージョンも用意しておくと、HTMLレンダリングで問題が発生した場合に信頼できるフォールバックとして機能するため、HTML形式とテキスト形式の両方を送信することがベストプラクティスと見なされます。テキストメールの例を次に示します。

<%= @user.name %>様、example.comへようこそ。
===============================================

example.comへのサインアップが成功しました。ユーザー名は「<%= @user.login %>」です。

このサイトにログインするには、次のリンクをクリックしてください: <%= @url %>

本サイトにユーザー登録いただきありがとうございます。

なお、インスタンス変数(@user@url)は、HTMLテンプレートとテキストテンプレートの両方で利用可能になります。

これで、mailメソッドを呼び出せば、Action Mailerは2種類のテンプレート (テキストおよびHTML) を探索して、multipart/alternative形式のメールを自動生成します。

2.3 メーラーを呼び出す

メーラーのクラスとビューのセットアップが終わったら、次は、メーラーメソッドを実際に呼び出してメールのビューをレンダリングします。メーラーは、コントローラと別の方法でビューをレンダリングするものであるとみなせます。コントローラのアクションは、HTTPプロトコルで送信するためのビューをレンダリングしますが、メーラーのアクションは、メールプロトコルを経由して送信するためのビューをレンダリングします。

それでは、ユーザーの作成に成功したらUserMailerでウェルカムメールを送信するサンプルコードを見てみましょう。

最初にscaffoldでUserを作成します。

$ bin/rails generate scaffold user name email login
$ bin/rails db:migrate

次に、UserControllercreateアクションを編集して、新しいユーザーが作成されたときにウェルカムメールを送信するようにします。これは、ユーザーが正常に保存された直後にUserMailer.with(user: @user).welcome_emailへの呼び出しを挿入する形で行います。

ここではdeliver_laterを使って、Active Jobによるメールキューにメールを登録して後で送信するようにしています。これにより、コントローラのアクションはメールの送信完了を待たずに処理を続行できます。deliver_laterメソッドは、Active Jobに支えられています。

class UsersController < ApplicationController
  # ...

  def create
    @user = User.new(user_params)

    respond_to do |format|
      if @user.save
        # 保存後にUserMailerを使ってwelcomeメールを送信
        UserMailer.with(user: @user).welcome_email.deliver_later

        format.html { redirect_to user_url(@user), notice: "ユーザーが正常に作成されました" }
        format.json { render :show, status: :created, location: @user }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  # ...
end

withに任意のキーバリューペアを渡すことで、Mailerアクションのparamsにできます。たとえば、with(user: @user, account: @user.account)を渡すことで、Mailerアクションでparams[:user]params[:account]が利用可能になります。

上記のメーラー、ビュー、コントローラーをセットアップできたので、後は新しいUserを作成すれば、ウェルカムメールが送信されているかどうかをログをチェックすることで確認できます。ログファイルには、以下のように、テキストバージョンのメールとHTMLバージョンのメールが送信される様子が表示されます。

[ActiveJob] [ActionMailer::MailDeliveryJob] [ec4b3786-b9fc-4b5e-8153-9153095e1cbf] Delivered mail 6661f55087e34_1380c7eb86934d@Bhumis-MacBook-Pro.local.mail (19.9ms)
[ActiveJob] [ActionMailer::MailDeliveryJob] [ec4b3786-b9fc-4b5e-8153-9153095e1cbf] Date: Thu, 06 Jun 2024 12:43:44 -0500
From: notifications@example.com
To: test@gmail.com
Message-ID: <6661f55087e34_1380c7eb86934d@Bhumis-MacBook-Pro.local.mail>
Subject: Welcome to My Awesome Site
Mime-Version: 1.0
Content-Type: multipart/alternative;
 boundary="--==_mimepart_6661f55086194_1380c7eb869259";
 charset=UTF-8
Content-Transfer-Encoding: 7bit

----==_mimepart_6661f55086194_1380c7eb869259
Content-Type: text/plain;

...

----==_mimepart_6661f55086194_1380c7eb869259
Content-Type: text/html;

...

Railsコンソールでメーラーを呼び出してメールを送信することも可能です。この方法は、コントローラーアクションを設定する前のテストとして有用でしょう。以下は、上記と同じwelcome_emailを送信します。

irb> user = User.first
irb> UserMailer.with(user: user).welcome_email.deliver_later

メールをcronjobなどから今すぐ送信したい場合は、以下のようにdeliver_nowを呼び出すだけで済みます。

class SendWeeklySummary
  def run
    User.find_each do |user|
      UserMailer.with(user: user).weekly_summary.deliver_now
    end
  end
end

UserMailerなどのweekly_summaryメソッドは、ActionMailer::MessageDeliveryオブジェクトを1つ返します。このオブジェクトは、そのメール自身が送信対象であることをdeliver_nowdeliver_laterに伝えます。ActionMailer::MessageDeliveryオブジェクトは、Mail::Messageのラッパーです。内部のMail::Messageオブジェクトの表示や変更などを行いたい場合は、ActionMailer::MessageDeliveryオブジェクトのmessageメソッドにアクセスできます。

上のRailsコンソールにおけるMessageDeliveryの例を以下に示します。

irb> UserMailer.with(user: user).weekly_summary
#<ActionMailer::MailDeliveryJob:0x00007f84cb0367c0
 @_halted_callback_hook_called=nil,
 @_scheduled_at_time=nil,
 @arguments=
  ["UserMailer",
   "welcome_email",
   "deliver_now",
   {:params=>
     {:user=>
       #<User:0x00007f84c9327198
        id: 1,
        name: "Bhumi",
        email: "hi@gmail.com",
        login: "Bhumi",
        created_at: Thu, 06 Jun 2024 17:43:44.424064000 UTC +00:00,
        updated_at: Thu, 06 Jun 2024 17:43:44.424064000 UTC +00:00>},
    :args=>[]}],
 @exception_executions={},
 @executions=0,
 @job_id="07747748-59cc-4e88-812a-0d677040cd5a",
 @priority=nil,

3 マルチパートメールと添付ファイル

MIMEタイプmultipartは、ドキュメントを複数のコンポーネントパーツで構成して表現します。個別のコンポーネントパーツには、それぞれ独自のMIMEタイプ(text/htmltext/plainなど)を利用できます。multipartタイプは、複数のファイルを1つのトランザクションで送信するために使います(例: メールに複数のファイルを添付する)。

3.1 ファイルを添付する

Action Mailerで添付ファイルを追加するには、以下のようにattachmentsメソッドにファイル名とコンテンツを渡します。Action Mailerは自動的にmime_typeを推測し、encodingを設定して添付ファイルを作成します。

attachments['filename.jpg'] = File.read('/path/to/filename.jpg')

mailメソッドをトリガーすると、マルチパート形式のメールが1件送信されます。送信されるメールは、トップレベルがmultipart/mixedで最初のパートがmultipart/alternativeという正しい形式でネストしている、プレーンテキストメールまたはHTMLメールです。

添付ファイルを送信するもう1つの方法は、以下のようにファイル名、MIMEタイプとエンコードヘッダー、コンテンツを指定することです。Action Mailerは、渡された設定を利用します。

encoded_content = SpecialEncode(File.read('/path/to/filename.jpg'))
attachments['filename.jpg'] = {
  mime_type: 'application/gzip',
  encoding: 'SpecialEncoding',
  content: encoded_content
}

Action Mailerは添付ファイルを自動的にBase64でエンコードします。別のエンコードが必要な場合は、コンテンツをエンコードしてから、エンコードされたコンテンツとエンコードをHashとしてattachmentsメソッドに渡せます。エンコードを指定すると、Action Mailerは添付ファイルをBase64でエンコードしません。

3.2 インライン添付ファイルを作成する

場合によっては、添付ファイル(画像など)をメール本文内にインライン表示したいことがあります。

これを行うには、まず以下のように#inlineを呼び出して添付ファイルをインライン添付ファイルに変換します。

def welcome
  attachments.inline['image.jpg'] = File.read('/path/to/image.jpg')
end

次に、ビューでattachmentsをハッシュとして参照して、インライン表示したい添付ファイルを指定します。以下のように、ハッシュでurlを呼び出した結果をimage_tagメソッドに渡せます。

<p>こんにちは、リクエストいただいた写真は以下です:</p>

<%= image_tag attachments['image.jpg'].url %>

これはimage_tagに対する標準的な呼び出しであるため、画像ファイルを扱う場合と同様に、添付URLの後にもオプションのハッシュを渡せます。

<p>こんにちは、以下の写真です。</p>

<%= image_tag attachments['image.jpg'].url, alt: 'My Photo', class: 'photos' %>

3.3 マルチパートメール

メーラービューを作成するで説明したように、同じアクションに異なるテンプレートがある場合、Action Mailerは自動的にマルチパートメールを送信します。たとえば、UserMailerapp/views/user_mailerディレクトリにwelcome_email.text.erbwelcome_email.html.erbを配置している場合、Action MailerはHTMLバージョンとテキストバージョンのメーラーを両方とも異なる部分として含めたマルチパートメールを自動的に送信します。

Mail gem には、text/plainおよびtext/htmlというMIMEタイプを対象とするmultipart/alternateメールを作成するためのヘルパーメソッドがあり、その他のMIMEタイプのメールについては手動で作成できます。

挿入されるパーツの順序は、ActionMailer::Base.defaultメソッド内の:parts_orderで決定されます。

マルチパートは、電子メールで添付ファイルを送信するときにも使われます。

4 メーラーのビューとレイアウト

Action Mailerは、メールで送信するコンテンツをビューファイルで指定します。デフォルトでは、メーラーのビューはapp/views/name_of_mailer_classディレクトリに配置されます。コントローラのビューの場合と同様に、ファイル名はMailerのメソッド名と一致します。

メーラーのビューは、コントローラのビューと同様に、レイアウトの内側でレンダリングされます。メーラーのレイアウトは、app/views/layoutsに配置されます。メーラーのデフォルトのレイアウトファイルは、mailer.html.erbmailer.text.erbです。このセクションでは、メーラーのビューとレイアウトに関するさまざまな機能について説明します。

4.1 ビューのパスをカスタマイズする

アクションに対応するデフォルトのメーラービューは、以下のようにさまざまな方法で変更できます。

mailメソッドでは、以下のようにtemplate_pathtemplate_nameオプションが利用できます。

class UserMailer < ApplicationMailer
  default from: 'notifications@example.com'

  def welcome_email
    @user = params[:user]
    @url  = 'http://example.com/login'
    mail(to: @user.email,
         subject: '私の素敵なサイトへようこそ',
         template_path: 'notifications',
         template_name: 'hello')
  end
end

上の設定によって、mailメソッドはapp/views/notificationsディレクトリ以下にあるhelloという名前のテンプレートを探索します。template_pathにはパスの配列も指定できます。この場合探索は配列順に沿って行われます。

より柔軟な方法を使いたい場合は、ブロックを渡して特定のテンプレートをレンダリングする方法や、テンプレートファイルを使わずにインラインでテキストをレンダリングする方法も利用できます。

class UserMailer < ApplicationMailer
  default from: 'notifications@example.com'

  def welcome_email
    @user = params[:user]
    @url  = 'http://example.com/login'
    mail(to: @user.email,
         subject: '私の素敵なサイトへようこそ') do |format|
      format.html { render 'another_template' }
      format.text { render plain: 'hello' }
    end
  end
end

上のコードは、HTMLパートをanother_template.html.erbテンプレートでレンダリングし、テキストパートを"hello"でレンダリングしています。renderメソッドはAction Controllerで使われているものと同じなので、:plain:inlineなどのオプションもすべて同様に利用できます。

最後に、デフォルトのapp/views/mailer_name/ディレクトリ以外の場所にあるテンプレートでレンダリングしたい場合は、以下のようにprepend_view_pathを適用します。

class UserMailer < ApplicationMailer
  prepend_view_path "custom/path/to/mailer/view"

  # "custom/path/to/mailer/view/welcome_email" テンプレートの読み出しを試みる
  def welcome_email
    # ...
  end
end

またはappend_view_pathメソッドも利用できます。

4.2 Action MailerのビューでURLを生成する

アプリケーションのホスト情報をメーラー内で使いたい場合は、最初に:hostパラメータでアプリケーションのドメイン名を明示的に指定する必要があります。理由は、メーラーのインスタンスは、コントローラと異なり、サーバーが受信するHTTPリクエストのコンテキストと無関係であるためです。

アプリケーション全体で共通のデフォルト:hostを設定するには、config/application.rbに以下を追加します。

config.action_mailer.default_url_options = { host: 'example.com' }

独自のhostを設定したら、メールビューでは、相対URLを生成する*_pathヘルパーではなく、完全なURLを生成する*_urlを使うことをオススメします。メールクライアントはWebリクエストのコンテキストを持たないため、*_pathヘルパーが完全なWebアドレスをビルドするのに必要なベースURLがありません。

<%= link_to 'ようこそ', welcome_path %>

たとえば、メールビューでは上の*_pathではなく、以下のように*_urlを使う必要があります。

<%= link_to 'ようこそ', welcome_url %>

フルパスのURLを使うことで、メール内のリンクが正常に機能するようになります。

4.2.1 url_forでURLを生成する

テンプレートでurl_forを用いて生成したURLは、デフォルトでフルパスになります。

:hostオプションをグローバルに設定していない場合は、url_for:hostオプションを明示的に渡す必要があります。

<%= url_for(host: 'example.com',
            controller: 'welcome',
            action: 'greeting') %>
4.2.2 名前付きルーティングでURLを生成する

他のURLと同様に、名前付きルーティングヘルパーについても*_pathではなく*_urlを使う必要があります。

:hostオプションをグローバルに設定するか、url_for:hostオプションを明示的に渡すようにしてください。

<%= user_url(@user, host: 'example.com') %>

4.3 Action Mailerのビューに画像を追加する

image_tagヘルパーをメールで使えるようにするには、:asset_hostパラメータを指定する必要があります。理由は、メーラーのインスタンスは、サーバーが受信するHTTPリクエストのコンテキストを持っていないためです。

:asset_hostはアプリケーション全体で同じものを使うのが普通なので、config/application.rbで以下のようにグローバルに設定できます。

config.action_mailer.asset_host = 'http://example.com'

このプロトコルはリクエストから推測できないため、:asset_hostコンフィグではhttp://https://などのプロトコルを指定する必要があります。

これで、以下のようにメール内で画像を表示できます。

<%= image_tag 'image.jpg' %>

4.4 メーラービューをキャッシュする

アプリケーションビューでcacheメソッドを用いるときと同じように、メーラービューでもフラグメントキャッシュを利用できます。

<% cache do %>
  <%= @company.name %>
<% end %>

この機能を使うには、アプリケーションのconfig/environments/*.rbファイルで以下の設定が必要です。

config.action_mailer.perform_caching = true

フラグメントキャッシュはマルチパートメールでもサポートされています。詳しくはRails のキャッシュ機構ガイドを参照してください。

4.5 Action Mailerのレイアウト

メーラーのレイアウトも、コントローラのビューと同様の方法で設定できます。メーラーのレイアウトはapp/views/layoutsディレクトリに配置されます。以下はデフォルトのレイアウトです。

# app/views/layouts/mailer.html.erb
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <style>
      /* Email styles need to be inline */
    </style>
  </head>

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

上のレイアウトは、mailer.html.erbファイルにあります。デフォルトのレイアウト名は、先ほどメーラーを生成するセクションのlayout "mailer"行で見たように、ApplicationMailerで指定されます。コントローラーのレイアウトと同様に、メーラーのビューをレイアウト内でレンダリングするにはyieldを使います。

別のレイアウトファイルを明示的に指定したい場合は、メーラーでlayoutを呼び出します。

class UserMailer < ApplicationMailer
  layout 'awesome' # awesome.(html|text).erbをレイアウトとして使う
end

特定のメールでレイアウトを指定するには、以下のようにformatブロック内のrenderメソッド呼び出しでlayout: 'layout_name'オプションを渡します。

class UserMailer < ApplicationMailer
  def welcome_email
    mail(to: params[:user].email) do |format|
      format.html { render layout: 'my_layout' }
      format.text
    end
  end
end

上のコードは、HTMLパートについてはmy_layout.html.erbレイアウトファイルでレンダリングし、テキストパートについては通常のuser_mailer.text.erbでレンダリングします。

5 メールを送信する

5.1 メールを複数の相手に送信する

:toキーにメールアドレスのリストを設定すると、1件のメールを複数の相手に送信できます。メールアドレスのリストの形式は、メールアドレスの配列でも、メールアドレスをカンマで区切った単一の文字列でも構いません。

たとえば、新規登録を管理者全員に通知するには以下のようにします。

class AdminMailer < ApplicationMailer
  default to: -> { Admin.pluck(:email) },
          from: 'notification@example.com'

  def new_registration(user)
    @user = user
    mail(subject: "New User Signup: #{@user.email}")
  end
end

CC (カーボンコピー) やBCC (ブラインドカーボンコピー) アドレスを指定する場合にも同じ形式を使えます。それぞれ:ccキーと:bccキーを使います(使い方は:toフィールドと同じ要領です)。

5.2 メールアドレスを名前で表示する

メールアドレスを表示する代わりに、メールの受信者や送信者の名前をメールに表示したいことがあります。

ユーザーがメールを受信したときにメールアドレスの位置に受信者名を表示するには、以下のようにto:フィールドでemail_address_with_nameメソッドを使います。

def welcome_email
  @user = params[:user]
  mail(
    to: email_address_with_name(@user.email, @user.name),
    subject: '私の素敵なサイトへようこそ'
  )
end

同じ要領で、送信者名もfrom:フィールドで指定できます。

class UserMailer < ApplicationMailer
  default from: email_address_with_name('notification@example.com', '会社からのお知らせの例')
end

名前が空白(nilまたは空文字)の場合は、メールアドレスのみを返します。

5.3 送信メールの件名を訳文に置き換える

メールメソッドに件名を渡さなかった場合、Action Mailerは件名を国際化(I18n)の訳文から探索します。詳しくは、国際化ガイドを参照してください。

5.4 テンプレートをレンダリングせずにメール送信する

メール送信時にテンプレートのレンダリングをスキップしてメール本文に単なる文字列を指定したい場合は、:bodyオプションを使えます。このオプションを使う場合は、:content_typeオプションも指定も忘れないでください。:content_typeオプションが指定されていないと、デフォルトのtext/plainが適用されます。

class UserMailer < ApplicationMailer
  def welcome_email
    mail(to: params[:user].email,
         body: params[:email_body],
         content_type: "text/html",
         subject: "レンダリング完了")
  end
end

5.5 メール送信時に配信オプションを動的に変更する

SMTP認証情報(credential)などのデフォルトの配信設定をメール配信時に上書きしたい場合、メーラーのアクションでdelivery_method_optionsを使って変更できます。

class UserMailer < ApplicationMailer
  def welcome_email
    @user = params[:user]
    @url  = user_url(@user)
    delivery_options = { user_name: params[:company].smtp_user,
                         password: params[:company].smtp_password,
                         address: params[:company].smtp_host }
    mail(to: @user.email,
         subject: "添付の利用規約を参照してください",
         delivery_method_options: delivery_options)
  end
end

6 Action Mailerのコールバック

Action Mailerには以下のコールバックがあります。

メッセージの設定: before_actionafter_actionaround_action

配信の制御: before_deliverafter_deliveraround_deliver

メーラーのコールバックには、コントローラやモデルのコールバックと同様、ブロックまたはシンボル(メーラークラス内のメソッド名を表す)を渡せます。コールバックをメーラーで利用する場合の例をいくつか示します。

6.1 before_action

before_actionコールバックは、インスタンス変数を設定したり、デフォルト値付きのメールオブジェクトを渡したり、デフォルトのヘッダや添付ファイルを挿入したりするのに利用できます。

class InvitationsMailer < ApplicationMailer
  before_action :set_inviter_and_invitee
  before_action { @account = params[:inviter].account }

  default to:       -> { @invitee.email_address },
          from:     -> { common_address(@inviter) },
          reply_to: -> { @inviter.email_address_with_name }

  def account_invitation
    mail subject: "#{@inviter.name} をBasecampに招待いたします (#{@account.name})"
  end

  def project_invitation
    @project    = params[:project]
    @summarizer = ProjectInvitationSummarizer.new(@project.bucket)

    mail subject: "#{@inviter.name.familiar} をBasecampのプロジェクトに追加しました (#{@account.name})"
  end

  private
    def set_inviter_and_invitee
      @inviter = params[:inviter]
      @invitee = params[:invitee]
    end
end

6.2 after_action

after_actionコールバックもbefore_actionと同様のセットアップで利用できますが、メーラーのアクション内で設定されたインスタンス変数も利用できます。after_actionコールバックは、mail.delivery_method.settings設定を更新して配信メソッドを上書きするときにも利用できます。

class UserMailer < ApplicationMailer
  before_action { @business, @user = params[:business], params[:user] }

  after_action :set_delivery_options,
               :prevent_delivery_to_guests,
               :set_business_headers

  def feedback_message
  end

  def campaign_message
  end

  private
    def set_delivery_options
      # ここではメールのインスタンスや
      # @businessや@userインスタンス変数にアクセスできる
      if @business && @business.has_smtp_settings?
        mail.delivery_method.settings.merge!(@business.smtp_settings)
      end
    end

    def prevent_delivery_to_guests
      if @user && @user.guest?
        mail.perform_deliveries = false
      end
    end

    def set_business_headers
      if @business
        headers["X-SMTPAPI-CATEGORY"] = @business.code
      end
    end
end

6.3 after_deliver

after_deliverはメッセージ配信を記録するのに利用できます。メーラーのコンテキスト全体にアクセスするオブザーバーやインターセプタのような振る舞いも可能です。

class UserMailer < ApplicationMailer
  after_deliver :mark_delivered
  before_deliver :sandbox_staging
  after_deliver :observe_delivery

  def feedback_message
    @feedback = params[:feedback]
  end

  private
    def mark_delivered
      params[:feedback].touch(:delivered_at)
    end

    # インターセプタの代替
    def sandbox_staging
      message.to = ['sandbox@example.com'] if Rails.env.staging?
    end

    # コールバックは、同様のオブザーバーの例よりも多くのコンテキストを含む
    def observe_delivery
      EmailDelivery.log(message, self.class, action_name, params)
    end
end

メールのbodynil以外の値が設定されている場合、メーラーのコールバックは以後の処理を中止します。 before_deliverthrow :abortで中止できます。

7 Action Mailerのビューヘルパー

Action Mailerでは、通常のビューと同様のヘルパーメソッドを利用できます。

Action Mailer固有のヘルパーメソッドは、ActionMailer::MailHelperで利用できます。 たとえば、mailerを用いてビューからメーラーインスタンスにアクセスすることも、messageでメッセージにアクセスすることも可能です。

<%= stylesheet_link_tag mailer.name.underscore %>
<h1><%= message.subject %></h1>

8 Action Mailerを設定する

本セクションでは、Action Mailerの設定例の一部を示すにとどめます。

さまざまな設定オプションの説明について詳しくは、「Rails アプリケーションを設定する」ガイドのAction Mailerを設定するを参照してください。これらのオプションは、production.rbなどの環境固有の設定ファイルで設定できます。

8.1 Action Mailerの設定例

適切なconfig/environments/$RAILS_ENV.rbファイルに追加する設定の例を以下に示します。

config.action_mailer.delivery_method = :sendmail
# デフォルトは以下:
# config.action_mailer.sendmail_settings = {
#   location: '/usr/sbin/sendmail',
#   arguments: %w[ -i ]
# }
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
config.action_mailer.default_options = { from: 'no-reply@example.com' }

8.2 Gmail用のAction Mailer設定

Gmail経由でメールを送信するには、config/environments/$環境名.rbファイルに以下の設定を追加します。

config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  address:         'smtp.gmail.com',
  port:            587,
  domain:          'example.com',
  user_name:       Rails.application.credentials.dig(:smtp, :user_name),
  password:        Rails.application.credentials.dig(:smtp, :password),
  authentication:  'plain',
  enable_starttls: true,
  open_timeout:    5,
  read_timeout:    5 }

Googleは、安全性が低いと判断したアプリからのサインインをブロックしています。

Gmailの設定を変更することで、サインイン試行を許可できます。Gmailアカウントで2要素認証が有効になっている場合は、アプリケーションのパスワードを設定し、通常のパスワードの代わりにそれを使う必要があります。

9 メーラーのテストとプレビュー

メーラーのテスト方法について詳しくは、テスティングガイドのメーラーをテストするを参照してください。

9.1 メールのプレビュー

Action Mailerのプレビュー機能は、レンダリング用のURLを開くことでメールの外観を確認する方法を提供します。

上述のUserMailerクラスでプレビューをセットアップするには、UserMailerPreviewという名前のクラスを作成してtest/mailers/previews/ディレクトリに配置します。UserMailerwelcome_emailのプレビューを表示するには、UserMailerPreviewで同じ名前のメソッドを実装してから、UserMailer.welcome_emailを呼び出します。

class UserMailerPreview < ActionMailer::Preview
  def welcome_email
    UserMailer.with(user: User.first).welcome_email
  end
end

これで、http://localhost:3000/rails/mailers/user_mailer/welcome_emailにアクセスしてプレビューを表示できます。

app/views/user_mailer/welcome_email.html.erbメーラービューやメーラー自身に何らかの変更を加えると、自動的に再読み込みしてレンダリングされるので、スタイル変更を画面ですぐ確認できます。利用可能なプレビューのリストはhttp://localhost:3000/rails/mailersで表示できます。

これらのプレビュー用クラスは、デフォルトでtest/mailers/previewsに配置されます。このパスはpreview_pathsオプションで設定できます。たとえばlib/mailer_previewsに変更したい場合はconfig/application.rbに以下の設定を追加します。

config.action_mailer.preview_paths << "#{Rails.root}/lib/mailer_previews"

9.2 エラーをrescueする

メーラーメソッド内のrescueブロックは、レンダリングの外で発生したエラーをキャッチできません。たとえば、バックグラウンドジョブ内でのレコードのデシリアライズエラーや、サードパーティのメール配信サービスからのエラーはキャッチできません。

メール送信処理中に発生するエラーをキャッチするには、以下のようにrescue_fromを使います。

class NotifierMailer < ApplicationMailer
  rescue_from ActiveJob::DeserializationError do
    # ...
  end

  rescue_from "SomeThirdPartyService::ApiError" do
    # ...
  end

  def notify(recipient)
    mail(to: recipient, subject: "Notification")
  end
end

10 メールのインターセプタとオブザーバー

Action Mailerは、メールのオブザーバーやインターセプターのメソッドへのフックを提供します。これを用いて、送信されるすべてのメールのメール配信のライフサイクル中に呼び出されるクラスを登録できます。

10.1 メールをインターセプトする

インターセプタを使うと、メールを配信エージェントに渡す前にメールを加工できます。インターセプタクラスは以下のように、メールが送信される前に呼び出される::delivering_email(message)メソッドを実装しなければなりません。

class SandboxEmailInterceptor
  def self.delivering_email(message)
    message.to = ['sandbox@example.com']
  end
end

interceptors設定オプションを用いてインターセプタを登録しておく必要があります。これを行うには、config/initializers/mail_interceptors.rbなどのイニシャライズファイルを以下のような内容で作成します。

Rails.application.configure do
  if Rails.env.staging?
    config.action_mailer.interceptors = %w[SandboxEmailInterceptor]
  end
end

上の例では"staging"というカスタマイズした環境を使っています。これはproduction環境に準じた状態でテストを行うための環境です。Railsのカスタム環境についてはRails環境を作成するを参照してください。

10.2 メールのオブザーバー

オブザーバーを使うと、メールが送信された後でメールのメッセージにアクセスできるようになります。オブザーバークラスは以下のように、メール送信後に呼び出される:delivered_email(message)メソッドを実装しなければなりません。

class EmailDeliveryObserver
  def self.delivered_email(message)
    EmailDelivery.log(message)
  end
end

インターセプタのときと同様、observers設定オプションを用いてオブザーバーを登録しておかなければなりません。これを行うには、config/initializers/mail_observers.rbなどのイニシャライズファイルを以下のような内容で作成します。

Rails.application.configure do
  config.action_mailer.observers = %w[EmailDeliveryObserver]
end

フィードバックについて

Railsガイドは GitHub の yasslab/railsguides.jp で管理・公開されております。本ガイドを読んで気になる文章や間違ったコードを見かけたら、気軽に Pull Request を出して頂けると嬉しいです。Pull Request の送り方については GitHub の README をご参照ください。

原著における間違いを見つけたら『Rails のドキュメントに貢献する』を参考にしながらぜひ Rails コミュニティに貢献してみてください 🛠💨✨

本ガイドの品質向上に向けて、皆さまのご協力が得られれば嬉しいです。

Railsガイド運営チーム (@RailsGuidesJP)

支援・協賛

Railsガイドは下記の協賛企業から継続的な支援を受けています。支援・協賛にご興味あれば協賛プランからお問い合わせいただけると嬉しいです。

  1. Star
  2. このエントリーをはてなブックマークに追加