Help us understand the problem. What is going on with this article?

Railsでメール自動配信機能をつくるまでの道程

More than 3 years have passed since last update.

あなたがRails触る人なら見ておきたい「体系的な」豆知識」からの派生記事です。

「mailersってデフォルトで用意生成されるけどあんま使ったことない」そう思った8月某日、RailsでAction Mailerを利用して開発環境でメール自動配信機能をつくってみました。

手順

  1. development.rb にメール送信設定を記述
  2. メーラーを生成
  3. メーラーを編集
  4. メール本文をデザインする
  5. メーラーを呼び出すためのメソッドを記述する
  6. 実際に表示を確認する
  7. (発展) 添付ファイル付きのメールを送信する

1. development.rb にメール送信設定を記述

メールを送信する時には送信するサーバーが必要です。
今回はgmailのアカウントからメールを送れるようにしてみます。

development.rb
Rails.application.configure do
  #--- 中略 ---#
  config.action_mailer.raise_delivery_errors = true
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = {
    port:                 587,
    address:              'smtp.gmail.com',
    domain:               'smtp.gmail.com',
    user_name:            '<YOUR EMAIL ADDRESS>',
    password:             '<YOUR EMAIL PASSWORD>',
    authentication:       'login',
    enable_starttls_auto: true
  }
end

config.action_mailer というパラメーターに様々なオプションを指定する事ができます。
上から順番に説明していきます。

  • raise_delivery_errors

メールの送信に失敗した時にエラーを発火させるか (デフォルト値: false)

  • delivery_method

メールを送信する方法 (デフォルト値: :smtp)

  • smtp_settings

:smtpモードでの設定情報

  1. address => SMTPサーバーのホスト名
  2. port => SMTPサーバーのポート番号
  3. domain => HELOドメイン
  4. user_name => メール送信に使用するgmailのアカウント
  5. password => メール送信に使用するgmailのパスワード
  6. authentication => 認証方法
  7. enable_starttls_auto => メールの送信にTLS認証を使用するか

※ 後にGmail以外のクライアントを使用する際にhostname was not match with the server certificateというエラーが出続ける場合には`enablestarttls_autoの項目をfalse`としてください。_

2. メーラーを生成

メール送信におけるコントローラー的役割を果たすMailerrailsコマンドで生成します。

$ rails g mailer SampleMailer send_when_update

# rails g mailer <メーラー名> <メソッド名>

app/mailersフォルダの配下にapplication_mailersample_mailerが生成されます。
app/views/sample_mailerには同じくメールテンプレートファイルが生成されます。

以後は基本的にこれら2つをカスタムしていきます。

3. メーラーを編集

app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
  default from: "from@example.com"
  layout 'mailer'
end
app/mailers/sample_mailer.rb
class SampleMailer < ApplicationMailer
  def send_when_update
    @greeting = "Hi"
    mail to: "to@example.org"
  end
end

コメント等は省いていますが、生成直後の状態はこうなっているはずです。

application_mailerには全メーラー共通の設定を、
sample_mailerにはメーラー個別の設定を、それぞれ記述していきます。

application_mailerの編集

app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
  default from:     "メールテスト運営局",
          bcc:      "sample+sent@gmail.com",
          reply_to: "sample+reply@gmail.com"
  layout 'mailer'
end

共通の処理・設定を記述する場合にはdefaultメソッドを使用します。
メールに関して指定可能な主なプロパティは以下のとおりです。

名前 概要
to 通常送信先
cc 一斉送信先
bcc 非表示送信先
from メールの送信元名
subject メールタイトル
date メールの送信日時
reply_to 返信先アドレス
content_type メール本文の種類

今回の場合はメールの送信元名を設定しました。
アドレス以外に「メールテスト運営局」という名前が表示されるようになります。

sample_mailerの編集

app/mailers/sample_mailer.rb
class SampleMailer < ApplicationMailer
  def send_when_update(user)
    @user = user
    mail to:      user.email,
         subject: '会員情報が更新されました。'
  end
end

メールに関する個別の設定にはmailメソッドを使用します。
この時設定可能なプロパティは上掲の表のとおりです。

今回の場合はsend_when_updateメソッドを呼び出す際に渡されるユーザーの情報から、
emailアドレスだけを取り出してメールの送信先としています。
こうすることでメール送信先を動的に変更することが可能です。

またメールテンプレート内で動的な変数を使用したい場合にはここで定義します。
メーラーはコントローラー的役割を果たしていると言った意味が実感できると思います。

mailメソッドが呼び出された後は、自動的にメール本文のテンプレートを読み込みます。
次はメールの本文を編集していきましょう。

4. メール本文をデザインする

メール本文のテンプレートはデフォルトで二種類の形式が提供されています。

views/sample_mailer/send_when_update.html.erb
<h1>SampleMailer#send_when_update</h1>

<p>
  <%= @greeting %>, find me in app/views/sample_mailer/send_when_update.html.erb
</p>
views/sample_mailer/send_when_update.text.erb
SampleMailer#send_when_update

<%= @greeting %>, find me in app/views/sample_mailer/send_when_update.text.erb

HTMLメールが送信できない場合にはテキストベースのメールを送信する
といったRailsの動作を保証するために二種類とも記述してある事が望まれます。

先ほどsample_mailerで定義したインスタンス変数を利用して、
動的にテキストの内容を表示できるようにしましょう。

views/sample_mailer/send_when_update.html.erb
<!doctype html>
<html lang="ja">
<head>
  <meta content="text/html; charset=UTF-8" />
  <style type="text/css">
    h2 {
      color: #e7454a;
    }
    p hr {
      color: #2d2a24;
    }
  </style>
</head>
<body>
  <h2><%= @user.name %> 様</h2>
  <hr />
  <p>
    この度は「メールテスト運営局」を利用頂きましてありがとうございます。
  </p>
  <p>
    ユーザー名: <%= @user.name %><br />
  </p>
  <hr />
</body>
</html>
views/sample_mailer/send_when_update.text.erb
===============================
<%= @user.name %>様
===============================

この度は「メールテスト運営局」を利用頂きましてありがとうございます。
ユーザー名: <%= @user.name %>

5. メーラーを呼び出すためのメソッドを記述する

メールの設定を記述しただけではメールは送信できません。
メーラーは各コントローラーのアクションからの呼び出しによって起動します。

controllers/users_controller.rb
class UsersController < ApplicationController
  def update
    current_user.update(update_params)
    SampleMailer.send_when_update(current_user).deliver
  end
end

ユーザー情報が更新された直後に、ユーザー宛にメールが送信されるよう記述した例です。

メーラーは「メーラー名.メソッド名」として、クラスメソッドを呼び出すかのように実行できます。
今回はメーラー側でユーザーの情報を利用して、動的に宛先や本文を変更する実装を行っています。
そのためメソッドの引数としてユーザーのインスタンスを渡しています。

実際の送信を担うのはdeliverメソッドです。
メーラーを起動して返ってきたメールのデータを送信します。

6. 実際に表示を確認する

ユーザーの編集機能は適当にscaffoldで生成したものがあれば十分です。
実際に編集してみましょう。

\\ポチッ!//
メールタイトル

自分のユーザー情報として登録したGmailに届きました。
送信元は「メールテスト運営局<sample@gmail.com>」となっています。

メール本文を開くと…

メール本文

うん、きちんとHTML形式で本文が表示されていますね。

7. (発展) 添付ファイル付きのメールを送信する

ユーザーがアバター画像としてアップロードしたファイルを、メールに添付したいという欲求にかられトライした記録を残しておきます。

以下は多少見やすいように、改行を増やして表示しています。

app/mailers/sample_mailer.rb
class SampleMailer < ApplicationMailer
  def send_when_update(user)
    @user = user
    @avatar_file = avatar_file_name(user)

    # set attachment file
    attachments[@avatar_file] =
      File.read(Rails.root.join("public#{avatar_url(@user)}"))

    # set mail header
    mail to:      @user.email,
         subject: '会員情報が更新されました。'
  end

  private

  def avatar_url(user)
    user.avatar.url(:thumb).gsub(/\?\d*/, "")
  end

  def avatar_file_name(user)
    "avatar#{avatar_url(user).match(/(\..+)/)[0]}"
  end
end

メールにファイルを添付するにはattachmentsメソッドを使用します。
attachmentメソッドのキーにはファイル名を、バリューにはファイル本体を指定します。

以下、順を追って解説します。

キモはattachmentメソッド

まずアバター画像は「20150808235932.png」のようなファイル名で、
public/system/users/avatars/... のディレクトリに保存してあります。

@avatar_file = avatar_file_name(user)

メールテンプレートでも添付ファイル名を使用できるように、インスタンス変数を定義しています。

def avatar_file_name(user)
"avatar#{avatar_url(user).match(/(..+)/)[0]}"
end

avatar_file_nameメソッドを呼び出すことで、添付ファイル名を
「avatar.jpg」や「avatar.png」といった「 avatar.拡張子 」の形に成形しています。

attachments[@avatar_file] =
File.read(Rails.root.join("public#{avatar_url(@user)}"))

ファイルの添付にはattachmentsメソッドを使用するのでしたね。

先ほど成形したファイル名を添付ファイル名にしています。
ファイル本体を読み込むにはFile.readメソッドの引数にファイルの絶対パスを指定すると良いです。
今回の場合はアプリケーションのルートパスに、public以下のパスをjoinメソッドで結合しています。

def avatar_url(user)
user.avatar.url(:thumb).gsub(/\?\d*/, "")
end

avatar_urlメソッドはファイルを呼び出した際にファイル名に付与されてしまうハッシュ値を、
取り除くために定義・実行しています。

表示を確認してみる

\\ポチッ!//
avatar画像

添付できとるうううぅぅぅ

添付画像のインライン表示化

ちなみに、以下のように細工することで添付画像をインライン表示することも可能です。

app/mailers/sample_mailer.rb
attachments.inline[@avatar_file] =
  File.read(Rails.root.join("public#{avatar_url(@user)}"))
views/sample_mailer/send_when_update.html.erb
<p>
  ユーザー名: <%= @user.name %><br />
  アバター画像:<br />
  <%= image_tag attachments[@avatar_file].url %>
</p>

「attachments.inline」とメソッド内で定義していること、
「image_tag attachments[@avatar_file].url」とテンプレート内で呼び出していること、
この2つが必要条件です。早速動作を確認してみましょう。

avatar_inline画像

先ほどとは異なり、メール本文にそのまま画像が取り込まれているため
より自然で伝わりやすい文章となっていますね。

さいごに

今回はRailsでデフォルト搭載されているメーラーを、食べず嫌いになっている方向けに書いたつもりです。
ざっとメーラーを使っていくのに必要な事項はまとめられたかなと思います。

しかしまだ課題はあります。
今のコードのままだとメールを送信する際どうしても数秒のバッファが生じてしまうのです。
理想としては、アクションを実行した後さっさとテンプレートの描画に進み、
メールの送信はバックグランドで勝手にやっていて欲しいですね。
どうすればできるようになるか、皆さんも考えてみてください!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした