以DRY方式将多个错误类传递给ruby的救援条款


100

我有一些代码需要挽救ruby中的多种类型的异常:

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

我想做的是以某种方式将要抢救的异常类型列表存储在某个地方,并将这些类型传递给抢救子句:

EXCEPTIONS = [FooException, BarException]

然后:

rescue EXCEPTIONS

这是否有可能,而且如果没有真正的恶意调用,是否有可能eval?考虑到TypeError: class or module required for rescue clause我尝试上述操作时不会看到我,因此我不抱希望。


2
救援* EXCEPTIONS呢?
罗马

Answers:


197

您可以将数组与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]

ruby 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
即使在红宝石1.8.7中也可以使用。EXCEPTIONS在这种情况下,在前面使用“ *”字符的含义是什么?想了解更多。
建业

2
@安迪这叫摔得痛。它通常具有将数组分解为逗号分隔的对象的作用。当在方法定义的自变量接收位置中使用时,它执行另一种方法:将自变量放到一个数组中。在各种场合下都非常有用。很高兴知道它适用于1.8.7。我相应地编辑了答案。
sawa

20
请注意,如果您要访问的例外情况下,使用此语法:rescue InvalidRequestError, CardError => e(见mikeferrier.com/2012/05/19/...
彼得·埃利希

这种语法可以正常工作:rescue *EXCEPTIONS => e,其中EXCEPTIONS是异常类名称的数组。
aks

3

答案 @sawa给出在技​​术上是正确的,但我认为它滥用了Ruby的异常处理机制。

正如Peter Ehrlich的评论所暗示的那样(通过指向Mike Ferrier的旧博客文章),Ruby已经配备了DRY异常处理程序机制:

puts 'starting up'
begin
  case rand(3)
  when 0
    ([] + '')
  when 1
    (foo)
  when 2
    (3 / 0)
  end
rescue TypeError, NameError => e
  puts "oops: #{e.message}"
rescue Exception => e
  puts "ouch, #{e}"
end
puts 'done'

通过使用这种技术,我们可以访问异常对象,该对象通常包含一些有价值的信息。


1

我刚遇到这个问题,找到了替代解决方案。如果您FooExceptionBarException都是定制的异常类,特别是如果它们都与主题相关,则可以构造继承层次结构,使它们都将从同一父类继承,然后仅对父类进行救援。

例如,我有三个例外:FileNamesMissingErrorInputFileMissingErrorOutputDirectoryError,我想用一条语句来挽救。我创建了另一个异常类FileLoadError,然后设置上述三个异常以从中继承。然后我只救了FileLoadError

像这样:

class FileLoadError < StandardError
end

class FileNamesMissingError < FileLoadError
end

class InputFileMissingError < FileLoadError
end

class OutputDirectoryError < FileLoadError
end

[FileNamesMissingError,
 InputFileMissingError,
 OutputDirectoryError].each do |error| 
   begin  
     raise error
   rescue FileLoadError => e
     puts "Rescuing #{e.class}."
   end 
end
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.