Ruby-on-rails 使用参数引发自定义异常
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11636874/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Raise custom Exception with arguments
提问by Chris Keele
I'm defining a custom Exception on a model in rails as kind of a wrapper Exception: (begin[code]rescue[raise custom exception]end)
我在 rails 中的模型上定义了一个自定义异常作为一种包装异常:(begin[code]rescue[raise custom exception]end)
When I raise the Exception, I'd like to pass it some info about a) the instance of the model whose internal functions raise the error, and b) the error that was caught.
当我引发异常时,我想向它传递一些关于 a) 其内部函数引发错误的模型实例和 b) 被捕获的错误的信息。
This is going on an automated import method of a model that gets populated by POST request to from foreign datasource.
这是一个模型的自动导入方法,该模型由来自外部数据源的 POST 请求填充。
tldr; How can one pass arguments to an Exception, given that you define the Exception yourself? I have an initialize method on that Exception but the raisesyntax seems to only accept an Exception class and message, no optional parameters that get passed into the instantiation process.
tldr; 考虑到您自己定义了异常,如何将参数传递给异常?我对该异常有一个初始化方法,但raise语法似乎只接受一个异常类和消息,没有传递到实例化过程中的可选参数。
回答by phoet
create an instance of your exception with new:
使用 new 创建一个异常实例:
class CustomException < StandardError
def initialize(data)
@data = data
end
end
# => nil
raise CustomException.new(bla: "blupp")
# CustomException: CustomException
回答by Max Wallace
Solution:
解决方案:
class FooError < StandardError
attr_reader :foo
def initialize(foo)
super
@foo = foo
end
end
This is the best way if you follow the Rubocop Style Guideand always pass your message as the second argument to raise:
如果您遵循Rubocop 风格指南并始终将您的消息作为第二个参数传递给raise:
raise FooError.new('foo'), 'bar'
You can get foolike this:
你可以foo这样:
rescue FooError => error
error.foo # => 'foo'
error.message # => 'bar'
If you want to customize the error message then write:
如果要自定义错误消息,请编写:
class FooError < StandardError
attr_reader :foo
def initialize(foo)
super
@foo = foo
end
def message
"The foo is: #{foo}"
end
end
This works well if foois required. If you want footo be an optional argument, then keep reading.
如果foo需要,这很有效。如果您想foo成为可选参数,请继续阅读。
Explanation:
解释:
Pass your message as the second argument to raise
将您的消息作为第二个参数传递给 raise
As the Rubocop Style Guidesays, the message and the exception class should be provided as separate arguments because if you write:
正如Rubocop 风格指南所说,消息和异常类应该作为单独的参数提供,因为如果你写:
raise FooError.new('bar')
And want to pass a backtrace to raise, there is no way to do it without passing the message twice:
并且想要将回溯传递给raise,如果不传递消息两次,就无法做到这一点:
raise FooError.new('bar'), 'bar', other_error.backtrace
As this answersays, you will need to pass a backtrace if you want to re-raise an exception as a new instance with the same backtrace and a different message or data.
正如this answer所说,如果您想将异常重新引发为具有相同回溯和不同消息或数据的新实例,则需要传递回溯。
Implementing FooError
实施 FooError
The crux of the problem is that if foois an optional argument, there are two different ways of raising exceptions:
问题的关键是 iffoo是一个可选参数,有两种不同的方式引发异常:
raise FooError.new('foo'), 'bar', backtrace # case 1
and
和
raise FooError, 'bar', backtrace # case 2
and we want FooErrorto work with both.
我们想FooError与两者合作。
In case 1, since you've provided an error instance rather than a class, raisesets 'bar'as the message of the error instance.
在情况 1 中,由于您提供了错误实例而不是类,因此raise设置'bar'为错误实例的消息。
In case 2, raiseinstantiates FooErrorfor you and passes 'bar'as the only argument, but it does not set the message after initialization like in case 1. To set the message, you have to call superin FooError#initializewith the message as the only argument.
在第二种情况下,raise实例FooError为您和传递'bar'作为唯一的参数,但它并没有在情况1.设置初始化后的消息像设定信息,你必须调用super在FooError#initialize与消息作为唯一的参数。
So in case 1, FooError#initializereceives 'foo', and in case 2, it receives 'bar'. It's overloaded and there is no way in general to differentiate between these cases. This is a design flaw in Ruby. So if foois an optional argument, you have three choices:
所以在情况 1 中,FooError#initialize接收'foo',在情况 2 中,它接收'bar'。它过载了,一般无法区分这些情况。这是 Ruby 的设计缺陷。所以 iffoo是一个可选参数,你有三个选择:
(a) accept that the value passed to FooError#initializemay be either fooor a message.
(a) 接受传递给的值FooError#initialize可能是foo或消息。
(b) Use only case 1 or case 2 style with raisebut not both.
(b) 仅使用 case 1 或 case 2 样式,raise但不能同时使用两者。
(c) Make fooa keyword argument.
(c)foo进行关键字参数。
If you don't want footo be a keyword argument, I recommend (a) and my implementation of FooErrorabove is designed to work that way.
如果你不想foo成为关键字参数,我推荐 (a) 并且我FooError上面的实现旨在以这种方式工作。
If you raisea FooErrorusing case 2 style, the value of foois the message, which gets implicitly passed to super. You will need an explicit super(foo)if you add more arguments to FooError#initialize.
如果raise一个FooError使用案例2的风格,价值foo是信息,这被隐式传递给super。您将需要一个明确的super(foo),如果你增加更多的参数FooError#initialize。
If you use a keyword argument (h/t Lemon Cat's answer) then the code looks like:
如果您使用关键字参数(h/t Lemon Cat's answer),则代码如下所示:
class FooError < StandardError
attr_reader :foo
def initialize(message, foo: nil)
super(message)
@foo = foo
end
end
And raising looks like:
和提高看起来像:
raise FooError, 'bar', backtrace
raise FooError(foo: 'foo'), 'bar', backtrace
回答by cyrilchampier
Here is a sample code adding a code to an error:
这是向错误添加代码的示例代码:
class MyCustomError < StandardError
attr_reader :code
def initialize(code)
@code = code
end
def to_s
"[#{code}] #{super}"
end
end
And to raise it:
raise MyCustomError.new(code), message
并提出它:
raise MyCustomError.new(code), message
回答by Lemon Cat
TL;DR 7 years after this question, I believe the correct answer is:
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")
WARNING: you will get identical results with:
警告:您将获得相同的结果:
raise CustomException.new(extra: 'blupp'), 'some message'
but that is because Exception#exception(string)does a #rb_obj_cloneon self, and then calls exc_initialize(which does NOT call CustomException#initialize. From error.c:
但那是因为Exception#exception(string)做了一个#rb_obj_cloneon self,然后调用exc_initialize(它不调用CustomException#initialize。来自error.c:
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;
}
In the latter example of #raiseup above, a CustomExceptionwill be raised with messageset to "a message" and extraset to "blupp" (because it is a clone) but TWOCustomExceptionobjects are actually created: the first by CustomException.new, and the second by #raisecalling #exceptionon the first instance of CustomExceptionwhich creates a second cloned CustomException.
在后面的示例中#raise向上的上方,一个CustomException将被raise与D-message设置为“消息”和extra设置为“blupp”(因为它是一个克隆),但TWOCustomException第一条件:对象实际上创建CustomException.new通过,而第二#raise呼叫#exception的第一个实例CustomException创建了第二个克隆的CustomException.
My extended dance remix version of whyis at: https://stackoverflow.com/a/56371923/5299483
我的扩展舞蹈混音版本为什么在:https: //stackoverflow.com/a/56371923/5299483
回答by Matt
Simple pattern for custom errors with additional information
带有附加信息的自定义错误的简单模式
If the extra information you're looking to pass is simply a type with a message, this works well:
如果您希望传递的额外信息只是一种带有消息的类型,那么这很有效:
# define custom error class
class MyCustomError < StandardError; end
# raise error with extra information
raise MyCustomError, 'Extra Information'
The result (in IRB):
结果(在 IRB 中):
Traceback (most recent call last):
2: from (irb):22
1: from (irb):22:in `rescue in irb_binding'
MyCustomError (Extra Information)
Example in a class
课堂上的例子
The pattern below has become exceptionally useful for me (pun intended). It's clean, can be easily modularized, and the errors are expressive. Within my class I define new errors that inherit from StandardError, and I raise them with messages (for example, the object associated with the error).
下面的模式对我来说非常有用(双关语)。它很干净,可以轻松模块化,并且错误具有表现力。在我的类中,我定义了从 继承的新错误StandardError,并用消息(例如,与错误关联的对象)引发它们。
Here's a simple example, similar to OP's original question, that raises a custom error within a class and captures the method name in the error message:
这是一个简单的示例,类似于 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)
回答by Emily
You can create an new instance of your Exceptionsubclass, then raise that. For instance:
您可以创建Exception子类的新实例,然后提升它。例如:
begin
# do something
rescue => e
error = MyException.new(e, 'some info')
raise error
end

