概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: Rails 6 - Action Mailbox tryout – Saeloun Blog
- 原文公開日: 2019/11/11
- 著者: Romil Mehta
- サイト: Saeloun -- Ruby on Railsのコンサルティング会社で、Rails + React開発のほかに、React Nativeによるモバイルアプリ開発も手がけています。
Rails 6のAction Mailboxを使ってみよう(翻訳)
アプリケーションで大量のメールを受信するはめになることがままあります。そういうメールを扱わなければならなくなった場合、操作のためにメールをひとつひとつ処理しなければなりません。
人事部門のユースケースで考えてみましょう。ある組織から、オープニングの参加候補者たちに「レジュメを送って欲しい」というメールを1件送信したとします。参加候補者たち全員がレジュメを添付してメールに返信したら、届いたレジュメをクラウドにアップロードして、レジュメ1件ごとにデータベースのエントリーを作成する必要があります。
以下はそのためのステップです。
- メールを1件ずつチェックする
- レジュメをダウンロードする
- レジュメをクラウドにアップロードして、レジュメのテーブルにエントリを作成する
ダルそうな作業ですね。
Action Mailboxを導入する
Rails 6では、受信メールを処理するためのAction Mailboxが導入されました。Action Mailboxは、受信メールをコントローラによく似たメールボックスにルーティングしてRailsで処理できるようにします。Mailgun、Mandrill、Postmark、SendGridといった主要なプラットフォームはひととおりサポートされています。
インストールと実装
以下のコマンドを実行して、feedback_collector
という新しいアプリケーションを作ります。
> rails new feedback_collector
rails action_mailbox:install
を実行してAction Mailboxをインストールします。このとき、受信メール保存用にActive Storageも同時にインストールされます。メールはここに保存され、処理済みかどうかがトラッキングされます。
続いてActive Jobが読み込まれてメールを処理し、処理が終わったメールは削除されます。削除されたメールについてもidやチェックサムをトラッキングするので、同じメールがまたやってきたときに処理を回避できます。
> cd feedback_collector
> rails action_mailbox:install
#=> Copying application_mailbox.rb to app/mailboxes
#=> create app/mailboxes/application_mailbox.rb
#=> Copied migration 20191021075823_create_active_storage_tables.active_storage.rb from active_storage
#=> Copied migration 20191021075824_create_action_mailbox_tables.action_mailbox.rb from action_mailbox
上のコマンドで生成されるマイグレーションは、action_mailbox
用とactive_storgage
用です。application_mailbox
も同時に作成されます。
それではUser
とProduct
とFeedback
のscaffoldを生成しましょう。
> rails g scaffold User name email
> rails g scaffold Product title
> rails g scaffold Feedback user:references product:references content:text
以下のマイグレーションを実行します。
> rails db:migrate
action_mailbox
テーブルのスキーマにはstatus
やmessage_id
やmessage_checksum
などのカラムが含まれます。status
はpending
、processing
、finished
、bounced
のいずれかになります。message_id
とmessage_checksum
は重複防止用です。
ApplicationMailbox
クラスは以下のような感じになります。
class ApplicationMailbox < ActionMailbox::Base
# routing /something/i => :somewhere
end
メールのルーティングは以下のように定義します。
class ApplicationMailbox < ActionMailbox::Base
# routing /something/i => :somewhere
routing :all => :feedbacks
end
すべてのメールをFeedbacksMailbox
にリダイレクトするルーティングを1つ指定しました。ルーティングは以下のように正規表現でも指定できます。
class ApplicationMailbox < ActionMailbox::Base
# routing /something/i => :somewhere
routing /feedback\-.+@example.com/i => :feedbacks
end
上の正規表現はfeedback-Ahdhc12@example.com
やfeedback-5264yYxjg@example.com
のようなメールアドレスにマッチします。
次はFeedbacksMailbox
を作成します。
> rails g mailbox Feedbacks
#=> Running via Spring preloader in process 623
#=> create app/mailboxes/feedbacks_mailbox.rb
#=> invoke test_unit
#=> create test/mailboxes/feedbacks_mailbox_test.rb
FeedbacksMailbox
クラスには、メールを処理するprocess
メソッドがあります。このクラスでmail
オブジェクトにアクセスできます。
メールを処理する前に何か操作を加えたい場合は、次のようにbefore_processing
コールバックででやれます。
class FeedbacksMailbox < ApplicationMailbox
before_processing :user
def process
end
def user
@user ||= User.find_by(email: mail.from)
end
end
mail.from
でuser
を取れるようになりました。しかし製品へのフィードバックを保存するためにproduct_id
が欲しくなります。
product_id
を取得するには、返信メールの正規表現を次のようにproduct_id
を含む形で指定できます。
RECIPIENT_FORMAT = /feedback\-(.+)@example.com/i
返信メールがfeedback-1234@example.com
の場合、上の正規表現によって1234
をproduct_id
として取得できます。
このメールを処理してユーザーからのフィードバックを保存しましょう。
class FeedbacksMailbox < ApplicationMailbox
RECIPIENT_FORMAT = /feedback\-(.+)@example.com/i
before_processing :user
def process
# フィードバックを作成する
# mail.decodedはメールがマルチパートでない場合はbodyを返す
# マルチパートの場合はmail.parts[0].body.decodedを使う
# ここでは後者がフィードバックを含む
if mail.parts.present?
Feedback.create user_id: @user.id, product_id: product_id, content: mail.parts[0].body.decoded
else
Feedback.create user_id: @user.id, product_id: product_id, content: mail.decoded
end
end
def user
@user ||= User.find_by(email: mail.from)
end
def product_id
# recipientsは複数の可能性があるので
# RECEIPIENT_FORMATにマッチするものを検索
recipient = mail.recipients.find { |r| RECIPIENT_FORMAT.match?(r) }
# first_match(つまりproduct_id)を返す
# 例: recipient = "feedback-1234@example.com"
# これで1234を返す
recipient[RECIPIENT_FORMAT, 1]
end
end
mail
オブジェクトにアクセスできるので、マルチパートのメールや添付ファイルありのメールも読み取れます。
development環境でテストする
development環境でテストするなら、http://localhost:3000/rails/conductor/action_mailbox/inbound_emails/new
をブラウザで開いて受信メールを配信するだけでやれます。to
のメールアドレスに基づいてメールボックスにルーティングされ、メールが処理されます。
production向けの設定
Action Mailboxをproduction環境向けに設定するために、私たちの場合はconfig/environment/production.rbでingress
を指定する必要があります。
ingress
をpostmark
にした場合で考えます。
config.action_mailbox.ingress = :postmark
さらに、Action Mailboxでpostmark
のingressへの認証リクエストに使う強力なパスワードの生成も必要です。私たちの場合、強力なパスワードはingress_password
に暗号化済みcredentialとして保存する必要があります。
action_mailbox:
ingress_password: PASSWORD
credentialに保存する代わりに、RAILS_INBOUND_EMAIL_PASSWORD環境変数に保存したパスワードを提供する手もあります。
今度は受信フックを設定して、受信したメールをactionmailbox
というユーザー名と先ほど生成したパスワードを用いて/rails/action_mailbox/postmark/inbound_emails
に転送する必要があります。以下は私たちの場合のWebhook URLです。
https://actionmailbox:PASSWORD@example.com/rails/action_mailbox/postmark/inbound_emails
動かせるサンプル
たとえば以下のサービスが使えます。
いずれも無料です。
セットアップ
SendGrid、Freenom、ngrokでアカウントを作ります。ngrok
のインストールについてはngrokのガイドに記載の手順をご覧ください。
次はfreenomのドメイン登録リンクで無料ドメインを登録します。検索ボックスにactionmailbox
などのドメイン名を入力して「Check Availability」ボタンを登録します。オプション機能は無料なので好きな機能をオンにします。ナビゲーションバーの「Services > My Domain」セクションにドメインが表示されます。
それが終わったら、このドメインをSendGrid
で認証する必要があります。認証手順は以下のとおりです。
- SendGridの sender_authフォームをクリックする。
- 「DNS host」の「Other Host (Not Listed)」を選択し、「DNS Host」に
freenom
と入力する。 - 「Next」をクリック。
- 「From Domain」ボックスにドメイン名を入力する。
- 「Next」をクリック。
- 「CNAME」レコードのフィールドが3つ表示される(「Freenom」ドメイン管理セクションでこれらを追加する必要あり)。
- 「My Domains」セクションの新しいタブで「Freenom」を開く。
- 「Manage Freenom DNS」タブをクリック。
- ここにCNAMEレコードを3つ入力する必要がある。
MX
レコードも作成する(「name」は空欄でもよい)。「type」をMX
、「target」をmx.sendgrid.net
、「priority」を10
にそれぞれ設定。- 「SendGrid」タブに戻ってチェックボックスを確認し、「Verify」をクリック。
- 失敗した場合は15〜20分ほど待ってから「Verify」を再度クリック。
- sender_auth/domainsリンクを開くと、設定したドメインのステータスが
Verified
になっている。
次は、development.rbに以下のSMTP設定を追加します。
config.action_mailer.smtp_settings = {
:user_name => SENDGRID_USERANME,
:password => SENDGRID_PASSWORD,
:domain => OUR DOMAIN,
:address => 'smtp.sendgrid.net',
:port => 587,
:authentication => :plain,
:enable_starttls_auto => true
}
config.action_mailer.delivery_method = :smtp
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = false
rails s
を実行してサーバーを起動します。
./ngrok http 3000
を実行してブラウザの新しいタブで表示します。
> ./ngrok http 3000
ngrok by @inconshreveable
Session Status online
Account ROMIL MEHTA (Plan: Free)
Version 2.3.35
Region United States (us)
Web Interface http://127.0.0.1:4040
Forwarding http://386e42cd.ngrok.io -> http://localhost:3000
Forwarding https://386e42cd.ngrok.io -> http://localhost:3000
Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00
上の「Forwarding」の値を見ると、サーバーのpublic URLが386e42cd.ngrok.io
になっています。この後の例で使いますので、URLをメモしておきます。
今度は、前述の「production向けの設定」で予告した手順を勧めます。
- 「ingress」に
sendgrid
を追加
config.action_mailbox.ingress = :sendgrid
- ingressのパスワードを作成してcredentialに追加します。
action_mailbox:
ingress_password: PASSWORD
- SendGridの「Inbound Parse」セクションを開いて、「Add Host & URL」をクリックします。
ドメインを選択し、destination URLにhttps://actionmailbox:INGRESS_PASSWORD@SERVER_PUBLIC_URL/rails/action_mailbox/postmark/inbound_emails
を設定して、「POST the raw, full MIME message」チェックボックスをオンにします。
実例
以下を実行して、ユーザーに製品へのフィードバックを促すメイラーを作成します。
> rails g mailer Feedback
Running via Spring preloader in process 48769
create app/mailers/feedback_mailer.rb
invoke erb
create app/views/feedback_mailer
invoke test_unit
create test/mailers/feedback_mailer_test.rb
create test/mailers/previews/feedback_mailer_preview.rb
app/mailers/feedback_mailer.rbに以下のメール送信コードを追加します。
class FeedbackMailer < ApplicationMailer
default from: FROM_MAIL_ADDRESS
def send_email
mail(to: ANY_USERS_EMAIL, reply_to: REPLY_TO_MAIL_ADDRESS, subject: 'Mailbox Test', body: 'Provide feedback for the product by replying to this mail')
end
end
上のコード例のREPLY_TO_MAIL_ADDRESSにはfeedback-#{PRODUCT_ID}@#{SERVER_PUBLIC_URL}
が、PRODUCT_IDには、フィードバックをお願いする製品がそれぞれ入ります。
以下のコマンドを実行すると、フィードバック依頼メールがトリガーされます。
> FeedbackMailer.send_email.deliver_now
上のコマンドを実行後、メールがユーザーに届きます。
feedback mailbox
のルーティングは設定済みなので、ユーザーがメールに返信するとFeedbackMailbox
のprocess
メソッドが呼び出されてメールが処理されます。
まとめ
本記事ではAction Mailboxの基礎、インストール方法、実装、設定方法について学びました。また、production環境に近いセットアップ例についても解説いたしました。