たまたま Rails の ActionMailer について調べてみる機会がありましたので、そこに見られた Observer について書きたいと思います。
ご存知のように、メールの送信というものは失敗する可能性があります。ですので、できうるかぎりメールはユーザに届けたい!という場合は、再送の手間をかけなければいけないこともあるわけですが、そうした際に必要になってくるのは、そう、送信ログです。ActionMailer では簡単に送信ログがとれますので、今回はそれについて書いてみます。
たとえば次のようなメールアクションがあったとします。
class FooMailer < ActionMailer::Base
def foo(recipient_email_address)
mail(to: recipient_email_address, body: 'foo')
end
end
このメールを送信するには、ご存知のように
FooMailer.foo('to@example.com').deliver_now
というふうにするわけですが、今回はログを取得したいので、これを実行するまえにあらかじめ ActiveSupport::Notification でサブスクライブしておきます。
ActiveSupport::Notification.subscribe("deliver.action_mailer") do |name, start, finish, id, payload|
puts "name: #{name}"
puts "payload: #{payload}"
end
こうしておくと、送信処理の直前に次のようなアウトプットが得られるようになります。
name: deliver.action_mailer
payload: {:mailer=>"FooMailer", :message_id=>nil, :subject=>"Foo", :to=>["to@example.com"], ...}
なお、 "deliver.action_mailer" は「送信時」(実際に送信する直前)のイベントですが、これを "process.action_mailer" にすれば「送信処理の前」(アクション、つまり今回の場合であれば FooMailer.foo 実行の直前)のイベントを購読できます。
interceptors と observers
ActiveSupport::Notification は ActionMailer の deliver_mail(送信処理)や process(アクションの実行)で発火するようになっていますが、Mail 自体にも Observer を加えるしくみが用意されており、ActionMailer からは register_interceptors や register_observers からそうした Observer を登録しておくことができます。
たとえば
class MyInterceptor
def self.delivering_email(mail)
puts mail.inspect
end
end
class MyObserver
def self.delivered_email(mail)
puts mail.inspect
end
end
ActionMailer::Base.register_interceptors(:my_interceptor)
ActionMailer::Base.register_observers(:my_observer)
としておけば、送信処理の直前で MyInterceptor.delivering_email を割り込ませることができ、送信後に MyObserver.delivered_email を実行させることができます。
さいごに
もちろん、 ActionMailer::Base#deliver_mail メソッドを直接上書きしても、送信処理の前に割り込みをすることはできるだろうと思います(この場合、super を呼ばなければ送信そのものをキャンセルすることもできそうです)。
しかし、こうした Observer を使えるしくみが用意されているので、もし用途に適しているようであれば、活用してみたほうがキレイな実装になるかな?と思います。