DEV Community

Ivan Shamatov
Ivan Shamatov

Posted on

Каскадный rescue в ruby и в ActiveSupport::Rescuable

Возьмем пример

class E < StandardError; end begin raise E rescue E puts "here we rescue E" rescue puts "here we rescue StandardError" end 
Enter fullscreen mode Exit fullscreen mode

Если вы используете вот такой вот каскадный rescue, то вы наверняка знаете, что сработает только обработчик первой ошибки, которая подпадает под условие.

=> here we rescue E nil 
Enter fullscreen mode Exit fullscreen mode

Если мы повторно сделаем raise в обработке ошибки, то этот raise уже отловлен не будет и приведет к эксепшену

class E < StandardError; end begin raise E rescue E puts "here we rescue E" raise rescue puts "here we rescue StandardError" end 
Enter fullscreen mode Exit fullscreen mode
=> here we rescue E E: E from (pry):27:in '__pry__' 
Enter fullscreen mode Exit fullscreen mode

rescue_from из ActiveSupport::Rescuable

Если мы захотим перенести такую логику в контроллер, или у нас есть какой-то кастомный бизнес-экшен, который использует ActiveSupport::Rescuable, то нужно помнить, что работать обработчик будет по-другому.

Пусть у нас будет вот такой базовый бизнес-экшн.

class Action include ActiveSupport::Rescuable def call call_impl rescue => ex rescue_with_handler(ex) or raise end end 
Enter fullscreen mode Exit fullscreen mode

И мы наследуемся от него, определяя call_impl и добавляя обработчики ошибок,

class A < Action class E < StandardError; end rescue_from E do |ex| puts "here we rescue E" raise end rescue_from StandardError do |ex| puts "here we rescue StandardError" end def call_impl raise E end end 
Enter fullscreen mode Exit fullscreen mode

то получим вот такой результат.

> A.new.call here we rescue StandardError => #<A::E: A::E> 
Enter fullscreen mode Exit fullscreen mode

Дело в том, что rescue_from работает в FILO режиме (First In Last Out), то есть последний обработчик, который мы определили, будет первым попробованным.

Поменяем местами обработчики местами

class A < Action class E < StandardError; end rescue_from StandardError do |ex| puts "here we rescue StandardError" end rescue_from E do |ex| puts "here we rescue E" raise end def call_impl raise E end end 
Enter fullscreen mode Exit fullscreen mode

и вот результат

irb(main):044:0> A.new.call here we rescue E Traceback (most recent call last): ... 1: from (irb):41:in `call_impl' A::E (A::E) 
Enter fullscreen mode Exit fullscreen mode

Таким образом мы видим, что сработал именно обработчик ошибки A::E, а не StandardError, а так же, что raise внутри обработчика ошибки не привел к еще повторному rescue из StandardError.

Top comments (0)