Pregunta

Soy nuevo en Ruby, así que tengo problemas para entender este extraño problema de excepción que estoy teniendo.Estoy usando la gema ruby-aaws para acceder a Amazon ECS: http://www.caliban.org/ruby/ruby-aws/.Esto define una clase 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

Esto significa que si recibe un código de error como AWS.InvalidParameterValue, esto producirá (en su variable de excepción) una nueva clase Amazon::AWS::Error::InvalidParameterValue que es una subclase de StandardError.

Ahora aquí es donde se pone raro.Tengo un código que se parece a este:

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

Ahora si do_aws_stuff lanza un NameError, mi bloque de rescate se activa.Parece que Amazon::AWS::Error no es la superclase del error generado. Supongo que, dado que es un módulo, ¿todo es una subclase del mismo?Ciertamente si lo hago:

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

Dice true, lo cual me parece confuso, especialmente teniendo en cuenta esto:

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

¿Qué está pasando y cómo se supone que debo separar los errores de AWS de otros tipos de errores?¿Debo hacer algo como:

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

Eso parece excepcionalmente chiflado.Los errores arrojados tampoco son de clase AWSError; se generan así:

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

Entonces, las excepciones que estoy buscando rescue from son los tipos de excepción generados que solo heredan de StandardError.

Para aclarar, tengo dos preguntas:

  1. ¿Por qué NameError, una excepción integrada en Ruby, es una kind_of?(Amazon::AWS::Error), ¿cuál es un módulo?
    Respuesta: Había dicho include Amazon::AWS::Error en la parte superior de mi archivo, pensando que era algo así como una importación de Java o una inclusión de C++.Lo que esto realmente hizo fue agregar todo lo definido en Amazon::AWS::Error (presente y futuro) a la clase Kernel implícita, que es un antepasado de cada clase.Esto significa cualquier cosa pasaría kind_of?(Amazon::AWS::Error).

  2. ¿Cómo puedo distinguir mejor las excepciones creadas dinámicamente en Amazon::AWS::Error ¿De otras excepciones aleatorias de otros lugares?

¿Fue útil?

Solución

Ok, intentaré ayudar aquí:

Primero, un módulo no es una clase, te permite mezclar comportamientos en una clase.segundo vea el siguiente ejemplo:

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]

¿un poco?le dice que sí, Error posee el comportamiento Todo A::B::Error (lo cual es normal ya que incluye A::B::Error), sin embargo, no incluye todo el comportamiento de A::B y por lo tanto no es del tipo A::B.(escribiendo pato)

Ahora hay muchas posibilidades de que ruby-aws vuelva a abrir una de las superclases de NameError e incluya Amazon::AWS:Error allí.(parche de mono)

Puede averiguar mediante programación dónde está incluido el módulo en la jerarquía con lo siguiente:

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)

Respecto a tu segunda pregunta no veo nada mejor que

begin 
#do AWS error prone stuff
rescue Exception => e
  if Amazon::AWS::Error.constants.include?(e.class.name)
    #awsError
  else
    whatever
  end 
end

(editar: el código anterior no funciona como está:El nombre incluye el prefijo del módulo, lo cual no es el caso de las matrices de constantes.Definitivamente deberías contactar al mantenedor de la biblioteca, la clase AWSError me parece más una clase de fábrica :/)

No tengo Ruby-aws aquí y el sitio de Caliban está bloqueado por el firewall de la compañía, por lo que no puedo realizar pruebas mucho más.

En cuanto a incluir:eso podría ser lo que hace que el mono parchee la jerarquía StandardError.Ya no estoy seguro, pero lo más probable es que hacerlo en la raíz de un archivo fuera de cada contexto incluya el módulo en Objeto o en la metaclase de Objeto.(Esto es lo que sucedería en IRB, donde el contexto predeterminado es Objeto, no estoy seguro en un archivo)

desde el pico en módulos :

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.

(editar: parece que no puedo comentar usando este navegador :/ yay para plataformas bloqueadas)

Otros consejos

Bueno, por lo que puedo decir:

Class.new( StandardError )

Está creando una nueva clase con StandardError como clase base, por lo que no será un Amazon::AWS::Error en absoluto.Simplemente está definido en ese módulo, por lo que probablemente sea un tipo_de?Amazon::AWS::Error.¿Probablemente no sea una especie de?Amazon::AWS porque tal vez los módulos no se anidan para fines de kind_of.?

Lo siento, no conozco muy bien los módulos en Ruby, pero definitivamente la clase base será StandardError.

ACTUALIZAR:Por cierto, de los documentos de rubí:

obj.kind_of?(clase) => verdadero o falso

Devuelve verdadero si clase es la clase de obj, o si clase es una de las superclases de obj o módulos incluidos en obj.

Sólo quería intervenir:Estoy de acuerdo en que esto es un error en el código lib.Probablemente debería leer:

      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

Un problema con el que te estás encontrando es que Amazon::AWS::Error::AWSError en realidad no es una excepción.Cuando raise se llama, mira para ver si el primer parámetro responde a la exception método y utilizará el resultado de eso en su lugar.Todo lo que sea una subclase de Exception regresará solo cuando exception se llama para que puedas hacer cosas como raise Exception.new("Something is wrong").

En este caso, AWSError tiene exception configurar como un lector de atributos que define el valor en la inicialización de algo como Amazon::AWS::Error::SOME_ERROR.Esto significa que cuando llamas raise Amazon::AWS::Error::AWSError.new(SOME_XML) Ruby termina llamando Amazon::AWS::Error::AWSError.new(SOME_XML).exception que devolverá una instancia de Amazon::AWS::Error::SOME_ERROR.Como señaló uno de los otros respondedores, esta clase es una subclase directa de StandardError en lugar de ser una subclase de un error común de Amazon.Hasta que esto se rectifique, la solución de Jean es probablemente su mejor opción.

Espero que haya ayudado a explicar más de lo que realmente sucede detrás de escena.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top