Pergunta

Sou novo em Ruby, então estou tendo problemas para entender esse estranho problema de exceção que estou tendo.Estou usando a gema ruby-aaws para acessar o Amazon ECS: http://www.caliban.org/ruby/ruby-aws/.Isso define uma classe 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

Isso significa que se você receber um código de erro como AWS.InvalidParameterValue, isso produzirá (em sua variável de exceção) uma nova classe Amazon::AWS::Error::InvalidParameterValue que é uma subclasse de StandardError.

Agora é aqui que fica estranho.Eu tenho um código parecido com este:

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

Agora se do_aws_stuff lança um NameError, meu bloco de resgate é acionado.Parece que Amazon::AWS::Error não é a superclasse do erro gerado - acho que por ser um módulo, tudo é uma subclasse dele?Certamente se eu fizer:

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

Diz true, o que considero confuso, especialmente considerando isto:

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

O que está acontecendo e como devo separar os erros da AWS de outros tipos de erros?Devo fazer 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

Isso parece excepcionalmente desagradável.Os erros gerados também não são da classe AWSError - eles são gerados assim:

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

Então, as exceções que estou procurando rescue from são os tipos de exceção gerados que herdam apenas de StandardError.

Para esclarecer, tenho duas perguntas:

  1. Por que NameError, uma exceção construída em Ruby, é um kind_of?(Amazon::AWS::Error), que é um módulo?
    Responder: eu tinha dito include Amazon::AWS::Error no topo do meu arquivo, pensando que era como uma importação de Java ou inclusão de C++.O que isso realmente fez foi adicionar tudo definido em Amazon::AWS::Error (presente e futuro) à classe Kernel implícita, que é ancestral de toda classe.Isso significa qualquer coisa passaria kind_of?(Amazon::AWS::Error).

  2. Como posso distinguir melhor as exceções criadas dinamicamente em Amazon::AWS::Error de outras exceções aleatórias de outros lugares?

Foi útil?

Solução

Ok, vou tentar ajudar aqui:

Primeiro, um módulo não é uma classe, ele permite misturar comportamentos em uma classe.segundo veja o seguinte exemplo:

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]

tipo de?diz que sim, Error possui todo o comportamento de A::B::Error (o que é normal, pois inclui A::B::Error), no entanto, não inclui todo o comportamento de A::B e, portanto, não é do tipo A::B.(digitando pato)

Agora há uma boa chance de que ruby-aws reabra uma das superclasses de NameError e inclua Amazon::AWS:Error lá.(remendação de macaco)

Você pode descobrir programaticamente onde o módulo está incluído na hierarquia com o seguinte:

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)

Em relação à sua segunda pergunta, não consigo ver nada melhor do 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 - o código acima não funciona como está:nome inclui o prefixo do módulo, o que não é o caso das matrizes de constantes.Definitivamente, você deve entrar em contato com o mantenedor da lib, a classe AWSError parece mais uma classe de fábrica para mim:/)

Não tenho Ruby-aws aqui e o site caliban está bloqueado pelo firewall da empresa, então não posso testar muito mais.

Em relação à inclusão:essa pode ser a coisa que está fazendo o patch do macaco na hierarquia StandardError.Não tenho mais certeza, mas provavelmente fazê-lo na raiz de um arquivo fora de cada contexto inclui o módulo no Object ou na metaclasse Object.(isto é o que aconteceria no IRB, onde o contexto padrão é Object, não tenho certeza em um arquivo)

de picareta em 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 - não consigo comentar usando este navegador: / yay para plataformas bloqueadas)

Outras dicas

Bem, pelo que posso dizer:

Class.new( StandardError )

Está criando uma nova classe com StandardError como classe base, portanto, não será um Amazon::AWS::Error.Ele está definido apenas naquele módulo, provavelmente por isso é um kind_of?Amazon::AWS::Erro.Provavelmente não é uma espécie de?Amazon::AWS porque talvez os módulos não sejam aninhados para fins de kind_of??

Desculpe, não conheço muito bem os módulos em Ruby, mas definitivamente a classe base será StandardError.

ATUALIZAR:Por falar nisso, dos documentos Ruby:

obj.kind_of?(class) => verdadeiro ou falso

Retorna verdadeiro se class for a classe de obj ou se class for uma das superclasses de obj ou módulos incluídos em obj.

Só queria intervir:Eu concordaria que isso é um bug no código lib.Provavelmente deveria ler:

      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

Um problema que você está enfrentando é que Amazon::AWS::Error::AWSError na verdade não é uma exceção.Quando raise é chamado, ele verifica se o primeiro parâmetro responde ao exception método e usará o resultado disso.Qualquer coisa que seja uma subclasse de Exception retornará quando exception é chamado para que você possa fazer coisas como raise Exception.new("Something is wrong").

Nesse caso, AWSError tem exception configurado como um leitor de atributos que define o valor na inicialização para algo como Amazon::AWS::Error::SOME_ERROR.Isso significa que quando você ligar raise Amazon::AWS::Error::AWSError.new(SOME_XML) Ruby acaba ligando Amazon::AWS::Error::AWSError.new(SOME_XML).exception que retornará uma instância de Amazon::AWS::Error::SOME_ERROR.Como foi apontado por um dos outros respondentes, esta classe é uma subclasse direta de StandardError em vez de ser uma subclasse de um erro comum da Amazon.Até que isso seja corrigido, a solução de Jean é provavelmente sua melhor aposta.

Espero que isso tenha ajudado a explicar mais sobre o que realmente está acontecendo nos bastidores.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top