2008-09-16 37 views
3

我是新来的Ruby,所以我有一些无法理解我在这个奇怪的异常问题。我正在使用ruby-aaws gem访问Amazon ECS:http://www.caliban.org/ruby/ruby-aws/。这定义一个类亚马逊的AWS :::错误:红宝石例外的继承与动态生成的类

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一个子类。

现在这里的地方会很奇怪。我有一些代码,看起来像这样:

begin 
    do_aws_stuff 
rescue Amazon::AWS::Error => error 
    puts "Got an AWS error" 
end 

现在,如果do_aws_stuff抛出一个NameError,我的救援块被触发。看来,亚马逊:: AWS ::错误不是生成的错误的超类 - 我想因为它是一个模块,一切都是它的一个子类?当然,如果我做的:

irb(main):007:0> NameError.new.kind_of?(Amazon::AWS::Error) 
=> true 

它说true,我觉得这混乱的,特别是考虑到这一点:

irb(main):009:0> NameError.new.kind_of?(Amazon::AWS) 
=> false 

这是怎么回事,怎么我应该从其他类型的分离出来AWS的错误错误?我应该这样做:

begin 
    do_aws_stuff 
rescue => error 
    if error.class.to_s =~ /^Amazon::AWS::Error/ 
    puts "Got an AWS error" 
    else 
    raise error 
    end 
end 

这似乎格外junky。抛出的错误是不是类AWSError要么 - 他们提出这样的:

error = Amazon::AWS::Error::AWSError.new(xml) 
raise error.exception 

所以我期待rescue例外是从生成的异常类型,只有从StandardError的继承。

为了澄清,我有两个问题:

  1. 为什么NameError,一个Ruby内置的例外,一个kind_of?(Amazon::AWS::Error),这是一个模块?
    答:我曾在我的文件的开头说:include Amazon::AWS::Error,认为它是一种类似于Java进口或C++包括。实际上做的是将Amazon::AWS::Error(现在和将来)中定义的所有内容添加到隐式Kernel类,这是每个类的祖先。这意味着什么将通过kind_of?(Amazon::AWS::Error)

  2. 我怎样才能最好的从随机其他异常从别处区分Amazon::AWS::Error动态创建的异常?

+0

由于Amazon :: AWS :: Error类型似乎与您在问题顶部提供的源代码无关,因此很难理解您的问题。粘贴错误,也许? – 2008-09-16 07:32:57

+0

糟糕,我忘了“AWS”模块。我剪掉了文件中很多不相关的部分,并且必须忽略它。所以我们有一个亚马逊模块,一个AWS模块,一个错误模块,然后是一个AWSError类,它在Amazon :: AWS :: Error模块中设置常量。 – bhollis 2008-09-16 07:48:02

回答

5

好吧,我会尽力帮助这里:

第一模块是不是一个类,它可以让你在混合行为一类。第二看下面的例子:

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] 

kind_of?告诉你,是的,错误确实拥有所有A :: B ::错误行为(这是正常的,因为它包括A :: B ::错误),但它不包括来自A :: B的所有行为,因此不是A :: B类。 (duck typing)

现在ruby-aws很有可能重新打开NameError的超类之一,并在其中包含Amazon :: AWS:Error。 (猴修补)

你可以找到编程其中模块包括在具有以下层次:

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) 

关于你提到的第二个问题,我看不到任何东西比

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类看起来更像工厂类对我来说:/)

我在这里没有ruby-aws,并且caliban站点被公司的防火墙阻止,所以我无法进一步测试。

关于包括:这可能是在StandardError层次结构上进行猴子修补的事情。我不再确定,但最有可能在每个上下文之外的文件根目录下包含Object或Object元类上的模块。 (这是IRB,在默认情况下为对象,不知道在一个文件中会发生什么)

pickaxe on modules

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.

(编辑 - 我似乎无法到能够评论使用这个浏览器:/ yay锁定平台)

1

好,从我可以告诉:

Class.new(StandardError) 

是创建具有StandardError的一个新类的基类,所以它不打算成为一个亚马逊AWS :: ::错误所有。它只是在该模块中定义的,这可能是为什么它是kind_of?亚马逊AWS :: ::错误。这可能不是一种善意?亚马逊:: AWS,因为可能模块不会为了kind_of而嵌套? ?

对不起,我不太了解Ruby中的模块,但绝大多数基类都将是StandardError。

UPDATE:顺便说一下,from the ruby docs

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.

+0

是的,我知道基类是`StandardError`。是什么驱使我坚持是`NameError.new.kind_of?(Amazon :: AWS :: Error)`是真的。为什么会这样? – bhollis 2008-09-16 07:45:01

+0

看看我的更新...可能解释它;-) – 2008-09-16 07:46:14

1

只是想编钟:我会同意这是一个在lib代码中的错误。它可能应该阅读:

你正在运行到
 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 
0

一个问题是,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错误的子类。在纠正这个问题之前,Jean的解决方案可能是你最好的选择。

我希望能帮助解释更多幕后实际发生的事情。