ActionMailerの_デリバリコールバックでonly/exceptオプションをサポート
ActionMailer の _deliver 系コールバックに only: と except: フィルタを正式に扱えるようになり、ActionController と同様のオプション指定が可能になりました。
背景
only: と except: が無視されていたことは ActionMailer のコールバック設計上の不整合でした。 *_delivery 系コールバックは before_action 系と同様に条件付きで実行できるはずですが、実際にはオプションが単に捨てられていました。 このため、特定のメール送信メソッドだけでコールバックを走らせたいケースで回避策として if: を使う必要があり、ドキュメント上も利用可能なオプションが曖昧でした。
開発者はコントローラと同様のインターフェースを期待していた点が問題の根源です。 ActionMailer を使用する多くのアプリケーションで、メール送信前後のロジックをメールテンプレートやジョブとは別に管理したい場面が増えており、フィルタが欠如していると意図しないコールバック実行やバグの温床になっていました。 この PR はその矛盾を解消し、一貫した API を提供することを目的としています。
技術的な変更
_insert_callbacks ラッパーを利用して AbstractController と同等の正規化ロジックを導入しました。 actionmailer/lib/action_mailer/callbacks.rb の before_deliver・after_deliver・around_deliver がそれぞれ
def before_deliver(*filters, &blk)
_insert_callbacks(filters, blk) do |name, options|
set_callback(:deliver, :before, name, options)
end
end
のように変更され、only: と except: が options に含まれる形で set_callback に渡されます。
ActionMailer::Base の内部で Callbacks のインクルード順序を調整しました。 actionmailer/lib/action_mailer/base.rb では元々 include Callbacks が先にありましたが、
include AbstractController::Callbacks
include AbstractController::Caching
include Callbacks
のように AbstractController::Callbacks の後に Callbacks を追加し、コールバックの定義が正しく上書きされるようにしました。
設定ドキュメントと CHANGELOG にエントリを追加しました。 actionmailer/CHANGELOG.md に
* Add support for `config.action_mailer.raise_on_missing_callback_actions` when using `_deliver` callbacks with `only:` and `except:` options.
が追記され、ガイド guides/source/configuring.md でも config.action_mailer.raise_on_missing_callback_actions が説明されています。
テストスイートに新しい期待を組み込み、動作を検証しました。 actionmailer/test/callbacks_test.rb では only: と except: の組み合わせでコールバックが正しく呼び出されるかを確認するテストが追加され、CallbackMailer.deliver_callback_log に期待通りのシンボルが記録されることを assert しています。
設計判断
AbstractController の正規化ロジックを再利用することで一貫性を確保しました。 既存の _normalize_callback_options をそのまま呼び出す形にしたため、コールバックオプションの解釈がコントローラとメール送信で完全に同一になります。 これにより、フレームワーク全体でのオプション処理が集中管理され、将来的な拡張やバグ修正が容易になります。
Callbacks のインクルード順序を調整し、名前空間の衝突を回避しました。 include Callbacks を AbstractController::Callbacks の後に置くことで、ActionMailer::Base が正しいコールバックメソッドを取得でき、既存の動作を壊さない安全な拡張となっています。
設定項目 config.action_mailer.raise_on_missing_callback_actions をコントローラと同等に導入しました。 これにより、コールバックが存在しない場合に例外を発生させるかどうかをメール側でも制御でき、開発者は期待通りの失敗をテストしやすくなります。 設計上は「Controller と Mailer の API を揃える」方針が貫かれています。
まとめ
今回の変更は、ActionMailer の _deliver コールバックに only: と except: フィルタを正式に導入し、AbstractController と同等の正規化ロジックを活用したことで API の一貫性が向上しました。 既存コードへの互換性を保ちつつ、設定オプションも整備されたため、開発者はメール送信フローをより細かく制御できるようになります。