我是Ruby的新手,所以在理解我遇到的这个奇怪的异常问题时遇到了一些麻烦。我正在使用ruby-aaws gem访问Amazon ECS:http://www.caliban.org/ruby/ruby-aws/。这定义了一个类Amazon :: AWS:Error:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| module Amazon
module AWS
# All dynamically generated exceptions occur within this namespace.
#
module Error
# An exception generator class.
#
class AWSError
attr_reader :exception
def initialize(xml)
err_class = xml.elements['Code'].text.sub( /^AWS.*\\./, '' )
err_msg = xml.elements['Message'].text
unless Amazon::AWS::Error.const_defined?( err_class )
Amazon::AWS::Error.const_set( err_class,
Class.new( StandardError ) )
end
ex_class = Amazon::AWS::Error.const_get( err_class )
@exception = ex_class.new( err_msg )
end
end
end
end
end |
这意味着,如果收到类似AWS.InvalidParameterValue的错误代码,则将生成(在其异??常变量中)新类Amazon::AWS::Error::InvalidParameterValue,它是StandardError的子类。
现在这是奇怪的地方。我有一些看起来像这样的代码:
1 2 3 4 5
| begin
do_aws_stuff
rescue Amazon::AWS::Error => error
puts"Got an AWS error"
end |
现在,如果do_aws_stuff抛出NameError,我的救援块将被触发。似乎Amazon :: AWS :: Error不是生成的错误的超类-我想因为它是一个模块,所以它是它的子类吗?当然,如果我这样做:
1 2
| irb(main):007:0> NameError.new.kind_of?(Amazon::AWS::Error)
=> true |
它说true,我感到困惑,尤其是考虑到以下情况:
1 2
| irb(main):009:0> NameError.new.kind_of?(Amazon::AWS)
=> false |
这是怎么回事,我应该如何将AWS错误与其他类型的错误分开?我应该做类似的事情:
1 2 3 4 5 6 7 8 9
| begin
do_aws_stuff
rescue => error
if error.class.to_s =~ /^Amazon::AWS::Error/
puts"Got an AWS error"
else
raise error
end
end |
这似乎异常怪异。抛出的错误也不是AWSError类-像这样引发:
1 2
| error = Amazon::AWS::Error::AWSError.new( xml )
raise error.exception |
因此,我要从中查找rescue的异常是仅从StandardError继承的生成的异常类型。
为了澄清,我有两个问题:
为什么NameError是一个内置的Ruby异常kind_of?(Amazon::AWS::Error),它是一个模块?
答:我在文件顶部说过include Amazon::AWS::Error,认为它有点像Java导入或C ++ include。这实际上是将Amazon::AWS::Error(现在和将来)中定义的所有内容添加到隐式Kernel类中,该类是每个类的祖先。这意味着任何东西都会通过kind_of?(Amazon::AWS::Error)。
如何最好地区分Amazon::AWS::Error中动态创建的异常与其他地方的随机其他异常?
好的,我会在这里帮助您:
首先,模块不是类,它允许您混合类中的行为。第二看下面的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| module A
module B
module Error
def foobar
puts"foo"
end
end
end
end
class StandardError
include A::B::Error
end
StandardError.new.kind_of?(A::B::Error)
StandardError.new.kind_of?(A::B)
StandardError.included_modules #=> [A::B::Error,Kernel] |
有点儿?告诉您,是的,Error确实具有所有A :: B :: Error行为(这是正常的,因为它包含A :: B :: Error),但是它不包括A :: B的所有行为,因此不是属于A :: B类型。 (鸭子打字)
现在,ruby-aws非常有可能重新打开NameError的超类之一,并在其中包含Amazon :: AWS:Error。 (猴子修补)
您可以通过以下方式以编程方式找到模块在层次结构中的位置:
1 2 3 4 5 6 7 8 9 10 11
| class Class
def has_module?(module_ref)
if self.included_modules.include?(module_ref) and not self.superclass.included_modules.include?(module_ref)
puts self.name+" has module"+ module_ref.name
else
self.superclass.nil? ? false : self.superclass.has_module?(module_ref)
end
end
end
StandardError.has_module?(A::B::Error)
NameError.has_module?(A::B::Error) |
关于你的第二个问题,我没有比这更好的了
1 2 3 4 5 6 7 8 9
| begin
#do AWS error prone stuff
rescue Exception => e
if Amazon::AWS::Error.constants.include?(e.class.name)
#awsError
else
whatever
end
end |
(编辑-上面的代码无法按原样工作:名称包含模块前缀,而常量数组则不是这样。您绝对应该联系lib维护者,AWSError类对我来说更像是工厂类:/)
我在这里没有红宝石偏光,并且caliban网站被公司的防火墙阻止了,所以我无法进行进一步的测试。
关于include:可能是在StandardError层次结构上进行猴子修补的事情。我现在不确定,但是最有可能在每个上下文之外的文件根目录中执行的操作包括在Object或Object元类上的模块。 (这就是在IRB中会发生的情况,其中默认上下文是Object,不确定文件中是否存在)
从模块上的镐:
A couple of points about the include statement before we go on. First, it has nothing to do with files. C programmers use a preprocessor directive called #include to insert the contents of one file into another during compilation. The Ruby include statement simply makes a reference to a named module. If that module is in a separate file, you must use require to drag that file in before using include.
(编辑-我似乎无法使用此浏览器发表评论:/是,锁定平台)
只是想插话:我同意这是lib代码中的错误。它可能应该显示为:
1 2 3 4 5
| unless Amazon::AWS::Error.const_defined?( err_class )
kls = Class.new( StandardError )
Amazon::AWS::Error.const_set(err_class, kls)
kls.include Amazon::AWS::Error
end |
好吧,据我所知:
1
| Class.new( StandardError ) |
正在创建一个以StandardError为基类的新类,因此它根本不会成为Amazon :: AWS :: Error。它只是在该模块中定义的,这可能就是为什么它是kind_of?亚马逊:: AWS ::错误。可能不是kind_of? Amazon :: AWS,因为可能不是出于kind_of的目的而嵌套模块? ?
抱歉,我不太了解Ruby中的模块,但是最肯定的是,基类将是StandardError。
更新:顺便说一下,从ruby文档:
obj.kind_of?(class) => true or false
Returns true if class is the class of obj, or if class is one of the superclasses of obj or modules included in obj.
您遇到的一个问题是Amazon::AWS::Error::AWSError实际上并不是一个例外。调用raise时,它将查看第一个参数是否响应exception方法,并将使用该结果。当exception被调用时,任何属于exception子类的东西都将返回自身,因此您可以执行raise Exception.new("Something is wrong")之类的事情。
在这种情况下,AWSError将exception设置为属性读取器,它在初始化时将值定义为Amazon::AWS::Error::SOME_ERROR之类的值。这意味着当您调用raise Amazon::AWS::Error::AWSError.new(SOME_XML)时,Ruby最终会调用Amazon::AWS::Error::AWSError.new(SOME_XML).exception,这将返回Amazon::AWS::Error::SOME_ERROR的实例。正如其他响应者之一指出的那样,此类是StandardError的直接子类,而不是常见的Amazon错误的子类。在纠正此问题之前,让的解决方案可能是您最好的选择。
我希望这有助于解释更多幕后实际情况。