Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

【Rails】Action Mailboxで受信メールを統合し、トラブルシューティングをマスターしよう

メールの送受信に関するRFC(Request for Comments)を一度でも目にしたことがあれば、受信箱の「送信」ボタンの裏側にどれほど複雑な技術が隠れているかに気づくはずです。

幸いなことに、開発者向けにSMTP(Simple Mail Transfer Protocol)サービスを提供してくれるツールは数多くあります。自前で管理するPostfixサーバーから、SendGrid、Amazon SES、Postmarkといった拡張性の高い送信サービスまで、選択肢は豊富です。ただし、配信性や料金の理由でプロバイダーを乗り換えるたびに、各サービス固有の仕様に合わせてアプリを書き直したりリファクタリングしたりする必要があるのが悩みの種でした。

そんなときに頼れるのが、Railsが提供するAction Mailboxです。本記事では、Action Mailboxを使って受信メールを統合し、トラブルシューティングする方法を詳しく解説します。

まずは、Action Mailboxとは何かを簡単に確認しておきましょう。

RailsのAction Mailboxとは?

Action Mailboxは、Ruby on Railsでメールを受信するための「概念的圧縮」を実現します。概念的圧縮とは、各SMTPサービス間の細かな違いをすべてカプセル化し、受信処理のコードを一度書くだけで済ませられる仕組みのことです。さらに、新しいサービス向けのプロバイダーを独自に作成することも可能です。

Action Mailboxの鍵となる概念は、メールの受信者(宛先)に基づいたルーティングです。受信メールプロバイダーを設定すれば、あるドメイン宛てのメールがアプリへルーティングされるようになります。そして受信者アドレスを確認することで、各メッセージをどのように処理すべきかを判定できます。

たとえば、Rails conductorのアクションを使ってテストメールを送信すると(筆者も実際に試しました)、次のような画面になります。

【Rails】Action Mailboxで受信メールを統合し、トラブルシューティングをマスターしよう

受信メールには、ToCCBCCX-Original-Toの各フィールドからの受信者が含まれます。


各アドレスはどこへルーティングされるかテストされますが、メール本体がルーティングされるのは一度だけです。

開発において重要なのは、自分のシステムが受け取るメールを実際にテストすることです。Railsには/rails/conductor/配下に開発用ページが用意されており、ローカルの開発環境へ直接メールを投入できます。

先ほどの例のように手動でメールを入力してもよいですし、ヘッダー一式を含んだメールファイルをアップロードすることも可能です。

完全なメール(ヘッダー、本文、添付ファイル付き)を入手する便利な方法のひとつが、Thunderbirdなどのメールクライアントの活用です。対象のメールを.eml形式で保存し、そのファイルをテキストエディタで開いて、内容全体をconductorページに貼り付けましょう。

これで、より複雑なメール処理もテストできるようになります。

Railsアプリでの投稿・コメントデモ

ここで、これらの仕組みが実際にどう動くのかを示す小さなデモを作ってみましょう。筆者は37Signalsの大ファンで、特にHey Worldでのブログ執筆スタイルが気に入っています。しかしコメント機能がないため、各ブログ投稿にコメントを付けられるクローンを作成します。

この記事のコードに沿って進めていきましょう。

まず新しいアプリを作成します(筆者はTailwind CSSを使用していますが、自分に合ったものを選んでください)。また、PostモデルとCommentモデルのためにAction Textも追加します。


スキャフォールドによって、PostsとCommentsを素早く確認できます。post.rbにアソシエーションを追加すると、投稿に関連するコメントが表示されるようになります。


posts/_post.html.erbには、コメント用のパーシャルを追加しましょう。


これでシンプルな投稿・コメント表示が完成しました。次に、ブログへの投稿用として受信メールをセットアップします。


これによりApplicationMailboxが生成されます。blog@宛てのメールがすべてPost Mailboxに振り分けられ、投稿を作成するようにルートを設定しましょう。


https://localhost:3000/rails/conductor/action_mailbox/inbound_emailsにアクセスして、いくつかメールを送ればすぐにテストできます。blog@whatever.com宛てに送信すれば、メールがアプリ内のinboxに届きます。それ以外のアドレスに送ると、メッセージはバウンスします。

Post Mailboxでメールを受信する

次に、メールを受け取ってブログに投稿するPost Mailboxをセットアップします。各Mailboxは、元のinbound_emailオブジェクトとmailオブジェクトにアクセスできます。InboundEmailは、Rails全体で利用されているmailクラスのラッパーです。

今回の目的では、差出人・件名・本文がわかれば十分です。これらを抽出してPostレコードを作成すれば、ブログのトップページに表示されます。


もう一度ブログのアドレスにメールを送り、インデックスページを更新してみてください。投稿が表示されているはずです!

続いて、投稿へのコメント機能を追加します。まず、コメントしたい人はメール送信時に正しい投稿を参照できる必要があります。シンプルな方法のひとつは、受信用メールアドレスに投稿IDを組み込むことです(例:comment+123@whatever.com。アドレス中の「123」がPost要素を指します)。

CommentMailboxを生成しましょう。


comment+123形式のメールがCommentMailboxへ届くよう、Action Mailboxにルートを追加します。


_post.html.erbには、メールアドレスを生成するリンクを追加し、読者が自分のメールアプリから直接コメントを送れるようにしましょう。


受信したメールはCommentMailboxにルーティングされ、正しいブログ投稿に紐づくコメントとして解析されます。


processメソッドは、メール本文と差出人アドレスからコメントを作成します。postメソッドで取得したPostを参照しており、このメソッドは最初の受信者のメールアドレスを取り出し、正規表現で投稿IDを抜き出します。

Postが存在しなかったり、トークンを解析できなかったりした場合は、メールがバウンスされ、処理はそこで停止します。

あとはRails conductorのフォームから、各投稿のアドレス宛てにコメントを送ってみましょう。インデックスページの投稿の下にコメントが表示されるはずです!

Action Mailboxを使ったより高度な例

メールというものは、実は非常に複雑です。たとえばアプリケーションモニタリングツールを導入していて、上記のようなコードをデプロイしたところ、APMダッシュボードにエラーが出始めたと想像してください。

パースエラーが発生していたり、投稿やコメントに奇妙なフォーマット崩れが多発していたりするかもしれません。

原因は、HTMLメールを受け取り、生のbodyソースをそのままWebサイトに投稿していることにあります。mail gemを使えば、受信メールがHTMLボディを持つかどうかを判定でき、メッセージから必要なパーツだけを取り出せます。

そこで、CommentMailboxPostMailboxを修正し、マルチパートメールをチェックしてHTMLパートを取り出し、HTMLがなければテキストパートへフォールバックするようにしてみましょう。

各メールは、パートをまったく持たないか、複数のパートを持つかのどちらかです。優先順位としては、まずHTMLパートがあればそれを使い、なければテキストパートを取得して使います。パース済みのHTMLもテキストも存在しない場合は、従来どおりメール本文を使用します。

修正後のPostMailboxは少し複雑になります。


CommentMailboxのprocessメソッドも変わります。


これで、スマートフォンから送られてくるメールにも問題なく対応できるようになりました。

RailsアプリへのAction Mailbox導入

Action Mailboxのおかげで、メールをRailsアプリのもうひとつの入出力経路として捉えられるようになりました。概念的圧縮によって、メールサービスプロバイダーに依存しないコードを書けるのです。筆者自身、基盤インフラを意識せずに済んだおかげで、最小限の作業でメールプロバイダーを乗り換えた経験もあります。

AppSignalのようなAPMツールは、すべてのActionMailer送信を監視し、配信状況を把握できる便利なダッシュボードも提供しています。

以下は、大量のメールを送受信している筆者のアプリの一例です。

【Rails】Action Mailboxで受信メールを統合し、トラブルシューティングをマスターしよう

これにより、アプリ内部で何が起きているのかをより可視化できるようになります。

まとめ

本記事では、まずRailsのAction Mailboxの機能について定義しました。続いてデモプロジェクトを構築し、受信メールを統合・解析して、ブログの投稿を作成するところまでを実装しました。

皆さんの開発の参考になれば幸いです。ハッピーコーディング!

P.S. Ruby Magicの最新記事をいち早く読みたい方は、Ruby Magicニュースレターを購読してください。記事を見逃すことはありません!

【Rails】Action Mailboxで受信メールを統合し、トラブルシューティングをマスターしよう

John Beatty

ゲスト著者のJohnは2010年、iPhoneアプリ向けAPIを構築するためにRailsを学び、以来その道を歩み続けています。現在は高校生にプログラミングを教える傍ら、学校向けのカスタムソフトウェアを開発し、ブログも執筆しています。

John Beattyの記事一覧


  1. RubyでAWS Lambda関数を書いてみよう!初心者向け実践ガイド

    AWS Lambdaを使えば、オーバーヘッドを最小限に抑えながらスケーラブルな関数を構築できます。Railsアプリ全体を作成・ホスト・保守する代わりに、Lambda関数を活用すれば、個々のイベントに対して独立して応答する仕組みを簡単に実現できます。本記事では、AWS初心者のあなたを、自分自身のRuby製Lambda関数を書けるレベルまで導きます。 Lambdaは、サーバーを管理することなく、イベントに反応してコードを実行できるサービスです。このイベント駆動型アーキテクチャにより、コードが実際に稼働している時間にのみ課金され、アイドル状態の間は費用が発生しません。LambdaはS3バケットから

  2. Railsアプリのパフォーマンス向上:条件付きGETリクエストでクライアント側キャッシュを使いこなす

    Russian dollキャッシング以外にも、Railsアプリのパフォーマンスを高速化するテクニックは数多くあります。今回は、レンダリング済みのページをユーザーのブラウザキャッシュに保存できる、Rails組み込みの「条件付きGET(Conditional GET)」サポートについて詳しく見ていきましょう。 👋 なお、キャッシュ以外のパフォーマンスに関する記事も多数執筆しています。Ruby(on Rails)のパフォーマンス監視チェックリストもあわせてぜひご覧ください。 EtagヘッダーとLast-Modifiedヘッダー ブラウザがRailsアプリのページに対してHTTP GETリクエストを