我在rails中的模型上定义了一个自定义Exception作为一种包装异常:(begin[code]rescue[raise custom exception]end
)
当我提出异常时,我想传递一些关于a)内部函数引发错误的模型实例,以及b)捕获的错误的信息。
这是一个模型的自动导入方法,该方法由来自外部数据源的POST请求填充。
tldr;如果您自己定义Exception,如何将参数传递给Exception?我在该Exception上有一个初始化方法,但raise
语法似乎只接受一个Exception类和消息,没有可传递给实例化过程的可选参数。
答案 0 :(得分:65)
使用new:
创建例外实例class CustomException < StandardError
def initialize(data)
@data = data
end
end
# => nil
raise CustomException.new(bla: "blupp")
# CustomException: CustomException
答案 1 :(得分:13)
class FooError < StandardError
attr_reader :foo
def initialize(foo)
super
@foo = foo
end
end
如果您按照Rubocop Style Guide并且始终将您的消息作为第二个参数传递给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 Style Guide所说,消息和异常类应作为单独的参数提供,因为如果你写:
raise FooError.new('bar')
并希望将回溯传递给raise
,如果不传递消息两次就无法做到:
raise FooError.new('bar'), 'bar', other_error.backtrace
如this answer所述,如果您想将异常重新引发为具有相同回溯和不同消息或数据的新实例,则需要传递回溯。
FooError
问题的症结在于,如果foo
是一个可选参数,则有两种不同的方式来引发异常:
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中的一个设计缺陷。因此,如果foo
是可选参数,则有三种选择:
(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
答案 2 :(得分:8)
以下是向错误添加代码的示例代码:
class MyCustomError < StandardError
attr_reader :code
def initialize(code)
@code = code
end
def to_s
"[#{code}] #{super}"
end
end
并提出它:
raise MyCustomError.new(code), message
答案 3 :(得分:1)
TL; DR在提出此问题7年后,我认为正确答案是:
class CustomException < StandardError
attr_reader :extra
def initialize(message=nil, extra: nil)
super(message)
@extra = extra
end
end
# => nil
raise CustomException.new('some message', extra: "blupp")
警告:您将获得与以下结果相同的结果:
raise CustomException.new(extra: 'blupp'), 'some message'
但这是因为Exception#exception(string)
在#rb_obj_clone
上进行了self
,然后调用exc_initialize
(不调用CustomException#initialize
。来自{{3} }:
static VALUE
exc_exception(int argc, VALUE *argv, VALUE self)
{
VALUE exc;
if (argc == 0) return self;
if (argc == 1 && self == argv[0]) return self;
exc = rb_obj_clone(self);
exc_initialize(argc, argv, exc);
return exc;
}
在上面的#raise
的后一个示例中,将CustomException
raise
设置为message
并将其设置为“邮件”,并将extra
设置为“ blupp”(因为它是一个克隆),但是实际上创建了两个 CustomException
个对象:第一个对象是CustomException.new
,第二个对象是#raise
调用{{ 1}}在#exception
的第一个实例上创建了第二个克隆的CustomException
。
我的扩展舞蹈混音版本为什么位于:error.c
答案 4 :(得分:0)
如果您希望传递的额外信息只是带有消息的类型,则效果很好:
# define custom error class
class MyCustomError < StandardError; end
# raise error with extra information
raise MyCustomError, 'Extra Information'
结果(在IRB中):
Traceback (most recent call last):
2: from (irb):22
1: from (irb):22:in `rescue in irb_binding'
MyCustomError (Extra Information)
以下模式对我(对双关语有意)特别有用。它很干净,可以轻松地进行模块化,并且这些错误很容易表达。在我的课程中,我定义了从StandardError
继承的新错误,并通过消息(例如,与错误关联的对象)引发了这些错误。
这是一个简单的示例,类似于OP的原始问题,它在类中引发自定义错误,并在错误消息中捕获方法名称:
class MyUser
# class errors
class MyUserInitializationError < StandardError; end
# instance methods
def simulate_failure
raise MyUserInitializationError, "method failed: #{__method__}"
end
end
# example usage:
MyUser.new.simulate_failure
# => MyUser::MyUserInitializationError (method failed: simulate_failure)
答案 5 :(得分:-2)
您可以创建Exception
子类的新实例,然后提升它。例如:
begin
# do something
rescue => e
error = MyException.new(e, 'some info')
raise error
end