たまたま 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 を使えるしくみが用意されているので、もし用途に適しているようであれば、活用してみたほうがキレイな実装になるかな?と思います。