DEV Community

Ivan Shamatov
Ivan Shamatov

Posted on

2

Каскадный 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.

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay