3

我是 Ruby 的新手,所以我在理解我遇到的这个奇怪的异常问题时遇到了一些麻烦。我正在使用 ruby​​-aaws gem 访问 Amazon ECS: http: //www.caliban.org/ruby/ruby-aws/。这定义了一个类 Amazon::AWS:Error:

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抛出 a NameError,我的救援块就会被触发。似乎 Amazon::AWS::Error 不是生成错误的超类 - 我想既然它是一个模块,那么一切都是它的子类?当然,如果我这样做:

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

这似乎异常笨拙。抛出的错误也不是类 AWSError - 它们是这样引发的:

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

因此,我要查找rescue的异常是仅从 StandardError 继承的生成的异常类型。

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

  1. 为什么 NameError 是一个 Ruby 内置异常,akind_of?(Amazon::AWS::Error)是一个模块?
    答:include Amazon::AWS::Error我在文件顶部说过,认为它有点像 Java 导入或 C++ 包含。这实际上所做的是将Amazon::AWS::Error(现在和将来)中定义的所有内容添加到隐式内核类中,该类是每个类的祖先。这意味着任何事情都会过去kind_of?(Amazon::AWS::Error)

  2. 如何最好地将动态创建的异常与Amazon::AWS::Error来自其他地方的随机其他异常区分开来?

4

4 回答 4

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]

有点儿?告诉你是的,Error 确实拥有 A::B::Error 的所有行为(这是正常的,因为它包括 A::B::Error)但是它不包括 A::B 的所有行为,因此不是A::B 类型的。(鸭打字)

现在,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 站点被公司的防火墙阻止,所以我无法进一步测试。

关于 include :这可能是在 StandardError 层次结构上做猴子修补的事情。我不再确定,但很可能在每个上下文之外的文件根目录中执行此操作,包括 Object 或 Object 元类上的模块。(这是在 IRB 中会发生的情况,其中默认上下文是对象,不确定在文件中)

模块上的镐

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.

(编辑——我似乎无法使用此浏览器发表评论:/ 是的,锁定平台)

于 2008-09-16T08:00:17.450 回答
1

好吧,据我所知:

Class.new( StandardError )

正在创建一个以 StandardError 作为基类的新类,因此它根本不会是 Amazon::AWS::Error。它只是在那个模块中定义的,这可能就是为什么它是 kind_of 的原因?亚马逊::AWS::错误。它可能不是一种?Amazon::AWS 因为也许模块不是出于 kind_of 的目的而嵌套的??

抱歉,我不太了解 Ruby 中的模块,但最肯定的基类将是 StandardError。

更新:顺便说一句,来自 ruby​​ 文档

obj.kind_of?(class) => true or false

如果 class 是 obj 的类,或者 class 是 obj 的超类之一或 obj 中包含的模块,则返回 true。

于 2008-09-16T07:40:51.817 回答
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
于 2008-09-16T11:54:52.517 回答
0

您遇到的一个问题是这Amazon::AWS::Error::AWSError实际上并不是一个例外。当raise被调用时,它会查看第一个参数是否响应该exception方法,并将使用该方法的结果。任何属于 的子类的东西在被调用Exception时都会返回自己,exception这样你就可以做类似raise Exception.new("Something is wrong").

在这种情况下,AWSErrorexception设置为属性读取器,它在初始化时将值定义为Amazon::AWS::Error::SOME_ERROR. 这意味着当您调用raise Amazon::AWS::Error::AWSError.new(SOME_XML)Ruby 时,最终调用Amazon::AWS::Error::AWSError.new(SOME_XML).exceptionwhich 将返回Amazon::AWS::Error::SOME_ERROR. 正如其他响应者之一所指出的,此类是直接子类,StandardError而不是常见 Amazon 错误的子类。在此问题得到纠正之前,Jean 的解决方案可能是您最好的选择。

我希望这有助于解释更多幕后实际发生的事情。

于 2009-10-05T00:39:53.927 回答