はじめに
業務でrescue_from
メソッドの挙動について調べる機会があったので、記事にまとめておこうと思います。
もし内容に間違い等ありましたらご指摘いただけると幸いです。
rescue_fromはスタック形式で積まれる
rescue_from
を複数記述した場合、下から上の順に動作します。下記の場合、rescue_from FugaError
->rescue_from HogeError
の順に動作していきます。
class ApplicationController < ActionController::Base
rescue_from HogeError, with: :hoge_error_handler # 2番目に動作
rescue_from FugaError, with: :fuga_error_handler # 1番目に動作
rescue_fromは発生した例外クラスの継承元まで評価する
例えば下記のような場合、
class ApplicationController < ActionController::Base
rescue_from HogeError, with: :hoge_error_handler
rescue_from FugaError, with: :fuga_error_handler
# 以下省略
class HogeError < FugaError # HogeErrorクラス は FugaErrorクラス を継承している
# 以下省略
class Hoge
def raise_hoge_error
raise HogeError
end
end
raise_hoge_error
メソッドでraiseしたHogeError
は、FugaError
クラスを継承しているためrescue_from FugaError
で引っかかり、fuga_error_handler
が実行されることになります。
このため、例えば**rescue_from StandardError
などを下の方に書いてしまうと、StandardError
クラスを継承した例外クラスは全てrescue_from StandardError
で引っかかることになります**。
下記の場合、raiseしたFugaError
はrescue_from FugaError
ではなくrescue_from StandardError
で引っかかることになります。
class ApplicationController < ActionController::Base
rescue_from HogeError, with: :hoge_error_handler
rescue_from FugaError, with: :fuga_error_handler
rescue_from StandardError, with: :standard_error_handler # raise_fuga_errorメソッドでraiseしたFugaErrorはここで引っかかる
# 以下省略
class FugaError < StandardError # FugaErrorクラス は StandardErrorクラス を継承している
# 以下省略
class Fuga
def raise_fuga_error
raise FugaError
end
end
このようなケースを危惧してか、Railsガイドには以下のように書かれています。
rescue_fromにExceptionやStandardErrorを指定すると、Railsでの正しい例外ハンドリングが阻害されて深刻な副作用が生じる可能性があります。よほどの理由がない限り、この指定はおすすめできません。
例外クラスをラップしている場合、外側の例外から順にrescue_fromのスタックを一巡していく
class ApplicationController < ActionController::Base
rescue_from FooError, with: :foo_error_handler
rescue_from HogeError, with: :hoge_error_handler
rescue_from FugaError, with: :fuga_error_handler
# 以下省略
class Foo
def error_wrap
raise FooError # FooErrorクラス は StandardErrorクラス を継承しているものとする
rescue
raise BarError # FooErrorクラス を BarErrorクラス でラップ
end
end
error_wrap
メソッドが実行されたとき、まずはBarError
クラスがrescue_from
のスタックを一巡します。上記の場合はどのrescue_from
にも引っかからないため、続けてラップの内側のFooError
クラスがrescue_from
のスタックを一巡し、最終的にrescue_from FooError
で引っかかることになります。