Islands in the byte stream

Technical notes by a software engineer

Herokuのreview appsでRailsのLetter Opener WebをつかうHack

github.com

これのREADMEにもありますが、Letter Opener (-Webも含む)をHerokuで使うにはちょっと注意が必要です。つまり、

  • Letter Openerはデータを #{Rails.root}/tmp/letter_opener に保存する
  • Herokuはdyno (node) 間でデータのやりとりは出来ない設計
  • ActionMailerの deliver_later! はActiveJobで実行するので、web dynoではなくworker dyno のファイルシステムにLetter Openerのデータを保存する

という状況なので、 #deliver_now! で送信したものはLetter Openerで見えますが、 #deliver_later! で送信したものは見えない、ということになります。

じゃあHeroku review appのときだけ #deliver_later!#deliver_now! に置き換えればいいんですねということでApplicationMailerにこんな感じのhackを入れました。

ActionMailerの内部実装に強く依存しているのでRailsのアップデートなどで動かなくなる可能性はありますが、 Rails 4.2.x, 5.1.x あたりはこれで大丈夫そうです。

# rubocop:disable Style/MethodMissing

class ApplicationMailer < ActionMailer::Base
  # Heroku web apps do not access worker's filesystem
  # https://github.com/fgrehm/letter_opener_web#usage-on-heroku

  # override
  def self.method_missing(method_name, *args)
    if action_methods.include?(method_name.to_s)
      MyMessageDelivery.new(self, method_name, *args)
    else
      super
    end
  end

  class MyMessageDelivery < ActionMailer::MessageDelivery
    # override
    def deliver_later!
      if Rails.env.heroku_review_app?
        deliver_now!
      else
        super
      end
    end

    # override
    def deliver_later
      if Rails.env.heroku_review_app?
        deliver_now
      else
        super
      end
    end
  end
end