Передача нескольких классов ошибок в предложение Ruby rescue в режиме СУХОЙ

У меня есть код, который должен спасти несколько типов исключений в ruby:

begin
  a = rand
  if a > 0.5
    raise FooException
  else
    raise BarException
  end
rescue FooException, BarException
  puts "rescued!"
end

То, что я хотел бы сделать, - это как-то сохранить список типов исключений, которые я хочу где-то спасти, и передать эти типы в предложение rescue:

EXCEPTIONS = [FooException, BarException]

а затем:

rescue EXCEPTIONS

Возможно ли это, и возможно ли без каких-либо вызовов hack-y на eval? Я не надеюсь, что я вижу TypeError: class or module required for rescue clause, когда я пытаюсь сделать это.

Ответ 1

Вы можете использовать массив с оператором splat *.

EXCEPTIONS = [FooException, BarException]

begin
  a = rand
  if a > 0.5
    raise FooException
  else
    raise BarException
  end
rescue *EXCEPTIONS
  puts "rescued!"
end

Если вы собираетесь использовать константу для массива, как указано выше (с помощью EXCEPTIONS), обратите внимание, что вы не можете определить ее в определении, а также если вы определяете ее в каком-то другом классе, вы должны ссылаться на нее с его пространством имен. Фактически, он не обязательно должен быть константой.


Оператор Splat

Оператор splat * "распаковывает" массив в своем положении, так что

rescue *EXCEPTIONS

означает то же, что и

rescue FooException, BarException

Вы также можете использовать его в литературе массива как

[BazException, *EXCEPTIONS, BangExcepion]

что совпадает с

[BazException, FooException, BarException, BangExcepion]

или в позиции аргумента

method(BazException, *EXCEPTIONS, BangExcepion)

что означает

method(BazException, FooException, BarException, BangExcepion)

[] расширяется до вакуума:

[a, *[], b] # => [a, b]

Одно различие между рубином 1.8 и ruby ​​1.9 с nil.

[a, *nil, b] # => [a, b]       (ruby 1.9)
[a, *nil, b] # => [a, nil, b]  (ruby 1.8)

Будьте осторожны с объектами, на которых определен to_a, поскольку в таких случаях будет применяться to_a:

[a, *{k: :v}, b] # => [a, [:k, :v], b]

С другими типами объектов он возвращает себя.

[1, *2, 3] # => [1, 2, 3]

Ответ 2

Я просто столкнулся с этой проблемой и нашел альтернативное решение. В случае, если ваши FooException и BarException все будут настраиваемыми классами исключений, и особенно, если они все связаны между собой, вы можете структурировать свою иерархию наследования таким образом, чтобы все они наследовались от одного и того же родительского класса, а затем спасали только родительский класс.

Например, у меня было три исключения: FileNamesMissingError, InputFileMissingError и OutputDirectoryError, которые я хотел спасти одним оператором. Я сделал еще один класс исключений под названием FileLoadError, а затем установил три вышеупомянутых исключения, чтобы наследовать от него. Затем я спас только FileLoadError.