ActionMailerの個別のクラスで、interceptorみたいなことをやりたい

Tsukasa OISHI

ActionMailerにはinterceptorという機能があって、メールを送信するときにいろいろと処理を挟むことができます。

class TsukaInterceptor
  def self.delivering_email(message)
    message.title = "[development] #{message.title}"
  end
end

みたいなクラスを作っておいて、initializerなどで

ActionMailer::Base.register_interceptor(TsukaInterceptor) if Rails.env.development?

とやっておけば、development環境で送信するメールのサブジェクトには、すべて [development] がつくようになります。
interceptorクラスのクラスメソッド delivering_email に渡される引数 message は、Mail::Messageクラスのオブジェクトです。

というようにinterceptorはとても便利なのですが、ActionMailer全体に影響してしまうので、個別のActionMailerクラスで別のことをやりたいときに困ります。
たとえば、あるActionMailerクラスを使ってdevelopment環境からメール送信をするときだけ、メールのあて先を変更したいとき。
interceptorを使っちゃうと全部のMailerに影響してしまうし、inteceptorクラスのdelivering_emailメソッド内で切り分けようとしても、Mail::MessageオブジェクトからはActionMailer::Baseクラスはわからないのです。

どうしようかなとちょっと悩んでtwitterでつぶやいたら、1分後くらいに元同僚のK和さんが「AbstractController::Callbacksを使ったらいいですよ」と教えてくれました。

さっそくやってみます。

class TsukaMailer < ActionMailer::Base
  if Rails.env.development?
    include AbstractController::Callbacks
    after_filter :check_to

    def check_to
      message.to = "tsuka@example.com"
    end
    private :check_to
  end

  def hoge
   ...
  end
end

としたら、development環境のときだけ、メールのあて先がすべて check_to メソッドの中で指定したものに変わりました。
message というのは、Mail::Messageオブジェクトで、以下のように attr_internal 宣言されているのでこういうふうにアクセスできます。
https://github.com/rails/rails/blob/4-1-stable/actionmailer/lib/action_mailer/base.rb#L558