Question

Je suis nouveau dans Ruby, alors j’ai du mal à comprendre ce problème d’exception bizarre que je rencontre. J'utilise la gemme ruby-aaws pour accéder à Amazon ECS: http: //www.caliban .org / ruby ??/ ruby-aws / . Ceci définit une 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

Cela signifie que si vous obtenez un code d'erreur tel que AWS.InvalidParameterValue , cela produira (dans sa variable d'exception) une nouvelle classe Amazon :: AWS :: Error :: InvalidParameterValue qui est une sous-classe de StandardError .

Maintenant, voici où ça devient bizarre. J'ai du code qui ressemble à ceci:

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

Maintenant, si do_aws_stuff lève un NameError , mon bloc de secours est déclenché. Il semble qu'Amazon :: AWS :: Error ne soit pas la superclasse de l'erreur générée - je suppose que puisque c'est un module, tout en est une sous-classe? Certainement si je le fais:

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

Il est écrit true , ce qui me semble source de confusion, surtout compte tenu de ceci:

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

Que se passe-t-il et comment suis-je censé séparer les erreurs AWS des autres types d'erreurs? Devrais-je faire quelque chose comme:

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

Cela semble exceptionnellement janky. Les erreurs renvoyées ne sont pas non plus de la classe AWSError - elles sont soulevées comme suit:

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

Par conséquent, les exceptions sur lesquelles je cherche à rescue sont les types d'exception générés qui héritent uniquement de StandardError.

Pour clarifier, j'ai deux questions:

  1. Pourquoi NameError, une exception Ruby intégrée, est-il un kind_of? (Amazon :: AWS :: Error) , qui est un module?
    Réponse : j'avais indiqué que incluait Amazon :: AWS :: Error en haut de mon fichier, pensant qu'il s'agissait en quelque sorte d'un import Java ou C ++. En fait, tout ce qui était défini dans Amazon :: AWS :: Error (présent et futur) a été ajouté à la classe implicite du noyau, qui est un ancêtre de chaque classe. Cela signifie que n'importe quoi transmettrait kind_of? (Amazon :: AWS :: Error) .

  2. Comment puis-je distinguer les exceptions créées de manière dynamique dans Amazon :: AWS :: Error des autres exceptions aléatoires provenant d'ailleurs?

Était-ce utile?

La solution

Ok, je vais essayer d'aider ici:

Tout d'abord, un module n'est pas une classe, il vous permet de mélanger les comportements dans une classe. en second lieu, voir l'exemple suivant:

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? vous dit que oui, Error possède tous les comportements A :: B :: Error (ce qui est normal car il inclut A :: B :: Error), mais il n'inclut pas tous les comportements de A :: B et n'est donc pas du genre A :: B. (dactylographie)

Maintenant, il y a de très bonnes chances que ruby-aws rouvre l'une des superclasses de NameError et inclue Amazon :: AWS: Error. (correction de singe)

Vous pouvez rechercher par programmation où le module est inclus dans la hiérarchie avec les éléments suivants:

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)

En ce qui concerne votre deuxième question, je ne vois rien de mieux que

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

(edit - le code ci-dessus ne fonctionne pas tel quel: nom inclut le préfixe de module, ce qui n'est pas le cas des tableaux de constantes. Vous devez absolument contacter le mainteneur de la bibliothèque. La classe AWSError ressemble plus à une classe usine: / )

Je n'ai pas de ruby-aws ici et le site de caliban est bloqué par le pare-feu de la société, je ne peux donc pas en tester davantage.

En ce qui concerne l’inclusion: il s’agit peut-être de la correction du singe sur la hiérarchie StandardError. Je ne suis plus sûr de le savoir, mais le faire à la racine d'un fichier en dehors de chaque contexte inclut probablement le module sur Object ou sur la métaclasse Object. (C’est ce qui se produirait dans IRB, où le contexte par défaut est Object, ce qui n’est pas sûr dans un fichier)

sur le pioche sur les modules :

Quelques remarques sur la déclaration include avant de poursuivre. Premièrement, cela n’a rien à voir avec des fichiers. Les programmeurs C utilisent une directive de préprocesseur appelée #include pour insérer le contenu d’un fichier dans un autre pendant la compilation. L'instruction Ruby include fait simplement référence à un module nommé. Si ce module se trouve dans un fichier séparé, vous devez utiliser require pour faire glisser ce fichier avant d'utiliser include.

(modifier - il semble que je ne puisse pas commenter à l'aide de ce navigateur: / yay pour les plateformes bloquées)

Autres conseils

Eh bien, d'après ce que je peux dire:

Class.new( StandardError )

est en train de créer une nouvelle classe avec StandardError comme classe de base, donc ce ne sera pas du tout Amazon :: AWS :: Error. Il est juste défini dans ce module, ce qui explique probablement pourquoi c'est un kind_of? Amazon :: AWS :: Error. Ce n'est probablement pas un genre de? Amazon :: AWS car les modules ne sont peut-être pas imbriqués à des fins de kind_of? ?

Désolé, je ne connais pas très bien les modules en Ruby, mais la classe de base sera certainement StandardError.

UPDATE : à propos, des ruby ??docs :

  

obj.kind_of? (class) = > vrai ou faux

     

Renvoie true si class est la classe d'obj ou si class est l'une des superclasses obj ou des modules inclus dans obj.

Je voulais juste ajouter quelque chose: je conviens que c'est un bogue dans le code de la bibliothèque. Il convient probablement de lire:

      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 des problèmes que vous rencontrez est que Amazon :: AWS :: Error :: AWSError n'est pas réellement une exception. Lorsque raise est appelé, il cherche à savoir si le premier paramètre répond à la méthode exception et utilisera le résultat de celui-ci à la place. Tout ce qui est une sous-classe de Exception se retournera lui-même lorsque exception sera appelé afin que vous puissiez faire des choses comme lever Exception.new ("Quelque chose ne va pas") .

Dans ce cas, AWSError a exception configuré en tant que lecteur d'attribut dont il définit la valeur lors de l'initialisation à quelque chose comme Amazon :: AWS ::. Error :: SOME_ERROR . Cela signifie que lorsque vous appelez augmentez Amazon :: AWS :: Error :: AWSError.new (SOME_XML) , Ruby finit par appeler Amazon :: AWS :: Error :: AWSError.new (SOME_XML ) .exception qui retournera une instance de Amazon :: AWS :: Error :: SOME_ERROR . Comme l’a souligné l’un des autres répondants, cette classe est une sous-classe directe de StandardError au lieu d’être une sous-classe d’une erreur Amazon commune. En attendant que cela soit corrigé, la solution de Jean est probablement votre meilleur choix.

J'espère que cela a permis d'expliquer davantage ce qui se passe réellement dans les coulisses.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top