以 DRY 方式将多个错误类传递给 ruby​​ 的救援子句

Posted

技术标签:

【中文标题】以 DRY 方式将多个错误类传递给 ruby​​ 的救援子句【英文标题】:Passing multiple error classes to ruby's rescue clause in a DRY fashion 【发布时间】:2011-08-12 12:06:07 【问题描述】:

我有一些代码需要在 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 的一些真正的hack-y 调用,这是否可能?鉴于我在尝试上述操作时看到TypeError: class or module required for rescue clause,所以我不抱希望。

【问题讨论】:

救援 *EXCEPTIONS 怎么样? 【参考方案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]

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]

【讨论】:

这似乎在 ruby​​ 1.8.7 中也有效。在这种情况下,在EXCEPTIONS 前面使用“*”字符的术语是什么?想多学一点。 @Andy 这叫做 splat。它通常具有将数组分解为逗号分隔对象的效果。当用于方法定义的参数接收位置时,它的作用是相反的:将参数一起放入一个数组中。它在各种场合都非常有用。很高兴知道它适用于 1.8.7。我相应地编辑了我的答案。 请注意,如果要访问异常实例,请使用以下语法:rescue InvalidRequestError, CardError => e(参见mikeferrier.com/2012/05/19/…) 这个语法很好用:rescue *EXCEPTIONS => e,其中EXCEPTIONS 是一个异常类名数组。【参考方案2】:

编辑/更新

我错过了原始问题的全部要点。 虽然公认的答案是有效的,但在我看来,使用建议的技术并不是一个好习惯。人们总是可以使用所需(和通用)try/rescue 的包装函数。


虽然@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'

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

【讨论】:

-1 这是 OP 在他的问题中的语法。他想将该列表提取到一个更易于维护的地方,我假设是因为必须在多个地方重复相同的异常类型列表,并且对列表的更新容易出错。 @Segfault 仅在阅读了您的评论后,我终于理解了 OP 的问题..【参考方案3】:

我刚遇到这个问题并找到了替代解决方案。如果您的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

【讨论】:

以上是关于以 DRY 方式将多个错误类传递给 ruby​​ 的救援子句的主要内容,如果未能解决你的问题,请参考以下文章

如何以 DRY 方式基于 current_page 将“活动”类应用于我的导航? - 导轨 3

如何将多个参数作为数组传递给 ruby​​ 方法?

将列表传递给函数以充当多个参数

如何在 Typescript 中保持代码 DRY 的同时解析多个 Json 文件?

将多个属性传递给 Vue 组件的几种方式

在 Ruby on Rails 应用程序中尽可能 DRY