22
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[Rails] rescue_fromに関する留意点

Last updated at Posted at 2019-05-13

はじめに

業務でrescue_fromメソッドの挙動について調べる機会があったので、記事にまとめておこうと思います。
もし内容に間違い等ありましたらご指摘いただけると幸いです。

rescue_fromはスタック形式で積まれる

rescue_fromを複数記述した場合、下から上の順に動作します。下記の場合、rescue_from FugaError->rescue_from HogeErrorの順に動作していきます。

application_controller.rb
class ApplicationController < ActionController::Base
  rescue_from HogeError, with: :hoge_error_handler  # 2番目に動作
  rescue_from FugaError, with: :fuga_error_handler  # 1番目に動作

rescue_fromは発生した例外クラスの継承元まで評価する

例えば下記のような場合、

application_controller.rb
class ApplicationController < ActionController::Base
  rescue_from HogeError, with: :hoge_error_handler
  rescue_from FugaError, with: :fuga_error_handler
# 以下省略
hoge_error.rb
class HogeError < FugaError  # HogeErrorクラス は FugaErrorクラス を継承している
# 以下省略
hoge.rb
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したFugaErrorrescue_from FugaErrorではなくrescue_from StandardErrorで引っかかることになります。

application_controller.rb
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はここで引っかかる
# 以下省略
fuga_error.rb
class FugaError < StandardError  # FugaErrorクラス は StandardErrorクラス を継承している
# 以下省略
fuga.rb
class Fuga
  def raise_fuga_error
    raise FugaError
  end
end

このようなケースを危惧してか、Railsガイドには以下のように書かれています。

rescue_fromにExceptionやStandardErrorを指定すると、Railsでの正しい例外ハンドリングが阻害されて深刻な副作用が生じる可能性があります。よほどの理由がない限り、この指定はおすすめできません。

例外クラスをラップしている場合、外側の例外から順にrescue_fromのスタックを一巡していく

application_controller.rb
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
# 以下省略
foo.rb
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で引っかかることになります。

参考文献

22
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?