解决方案:
class FooError < StandardError
attr_reader :foo
def initialize(foo)
super
@foo = foo
end
end
如果您遵循Rubocop 样式指南并始终将您的消息作为第二个参数传递给:这是最好的方法raise
:
raise FooError.new('foo'), 'bar'
你可以得到foo
这样的:
rescue FooError => error
error.foo # => 'foo'
error.message # => 'bar'
如果要自定义错误消息,请编写:
class FooError < StandardError
attr_reader :foo
def initialize(foo)
super
@foo = foo
end
def message
"The foo is: #{foo}"
end
end
如果foo
需要,这很有效。如果您想foo
成为可选参数,请继续阅读。
解释:
将您的消息作为第二个参数传递给raise
正如Rubocop 风格指南所说,消息和异常类应该作为单独的参数提供,因为如果你写:
raise FooError.new('bar')
并且想要将回溯传递给raise
,没有两次传递消息就没有办法做到这一点:
raise FooError.new('bar'), 'bar', other_error.backtrace
正如这个答案所说,如果要将异常重新引发为具有相同回溯和不同消息或数据的新实例,则需要传递回溯。
实施FooError
问题的症结在于 iffoo
是一个可选参数,有两种不同的引发异常的方式:
raise FooError.new('foo'), 'bar', backtrace # case 1
和
raise FooError, 'bar', backtrace # case 2
我们希望FooError
与两者合作。
在案例 1中,由于您提供了错误实例而不是类,因此raise
设置'bar'
为错误实例的消息。
在案例 2中,为您raise
实例化FooError
并作为唯一参数传递'bar'
,但它不会像案例 1 那样在初始化后设置消息。要设置消息,您必须使用消息作为唯一参数进行调用super
。FooError#initialize
因此,在情况 1 中,FooError#initialize
收到'foo'
,在情况 2 中,收到'bar'
。它已超载,通常无法区分这些情况。这是 Ruby 的设计缺陷。所以 iffoo
是一个可选参数,你有三个选择:
(a) 接受传递给的值FooError#initialize
可能是foo
或者是消息。
(b) 仅使用案例 1 或案例 2 样式,raise
但不能同时使用。
(c) 做foo
一个关键字参数。
如果您不想foo
成为关键字参数,我建议 (a) 并且我的FooError
上述实现旨在以这种方式工作。
如果您raise
是FooError
用例 2 样式,则 的值foo
是消息,它被隐式传递给super
. 如果您super(foo)
向FooError#initialize
.
如果您使用关键字参数(h/t Lemon Cat's answer),则代码如下所示:
class FooError < StandardError
attr_reader :foo
def initialize(message, foo: nil)
super(message)
@foo = foo
end
end
提高看起来像:
raise FooError, 'bar', backtrace
raise FooError(foo: 'foo'), 'bar', backtrace