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

时间:2011-04-25 18:24:15

标签: ruby exception 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

这是否有可能,如果没有对eval进行一些真正的黑客调用,是否可能?考虑到我在尝试上述情况时看到TypeError: class or module required for rescue clause,我没有希望。

3 个答案:

答案 0 :(得分:182)

您可以将数组与splat运算符*一起使用。

EXCEPTIONS = [FooException, BarException]

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

如果您要使用上面的数组常量(使用EXCEPTIONS),请注意您无法在定义中定义它,如果您在其他类中定义它,则必须引用它的命名空间。实际上,它不一定是常数。


Splat Operator

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]

答案 1 :(得分:0)

我刚遇到这个问题并找到了另一种解决方案。如果你的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

答案 2 :(得分:0)

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

正如Peter Ehrlich的注释所暗示的那样(指向旧的blog post by 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'

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