2008-09-16 7 views
3

Je suis nouveau à Ruby, donc j'ai du mal à comprendre ce problème d'exception bizarre que j'ai. 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: Erreur:Ruby exception inheritance avec des classes générées dynamiquement

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 comme AWS.InvalidParameterValue, cela produira (dans sa variable d'exception) une nouvelle classe Amazon::AWS::Error::InvalidParameterValue qui est une sous-classe de StandardError.

Maintenant, c'est ici que ça devient bizarre. J'ai un code qui ressemble à ceci:

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

Maintenant, si do_aws_stuff jette une NameError, mon bloc de secours se déclenche. Il semble que Amazon :: AWS :: Error n'est pas la superclasse de l'erreur générée - je suppose que c'est un module tout est une sous-classe de celui-ci? Certes, si je fais:

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

Il dit true, que je trouve confus, compte tenu surtout:

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

Qu'est-ce qui se passe, et comment suis-je censé séparer les erreurs AWS d'autres types de les erreurs? Dois-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 mignon. Les erreurs ne sont pas jetés classe AWSError soit - ils sont élevés comme ceci:

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

Ainsi, les exceptions que je cherche à rescue de sont les types d'exception générés héritant seulement de StandardError.

Pour clarifier, j'ai deux questions:

  1. Pourquoi NameError, Ruby construit en exception, un kind_of?(Amazon::AWS::Error), qui est un module?
    Réponse: J'avais dit include Amazon::AWS::Error en haut de mon fichier, pensant que c'était un peu comme une importation Java ou C++ include. Qu'est-ce que cela a réellement fait était d'ajouter tout ce qui est défini dans Amazon::AWS::Error (présent et futur) à la classe noyau implicite, qui est un ancêtre de chaque classe. Cela signifie que tout passerait kind_of?(Amazon::AWS::Error). Comment puis-je distinguer au mieux les exceptions créées dynamiquement dans Amazon::AWS::Error d'autres exceptions aléatoires d'ailleurs?

+0

Il est difficile de donner un sens à votre question, car le type Amazon :: AWS :: Error ne semble pas être lié au code source que vous fournissez en haut de la question. Erreur de collage, peut-être? –

+0

Oups, j'ai oublié le module "AWS". Je découpais beaucoup de parties non pertinentes du fichier et je dois le manquer. Nous avons donc un module Amazon, puis un module AWS, puis un module Error, puis une classe AWSError, qui définit des constantes dans le module Amazon :: AWS :: Error. – bhollis

Répondre

5

Ok, je vais essayer d'aider ici:

D'abord un module est pas une classe, il vous permet de mélanger le comportement en une classe. deuxième 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 tout le comportement A :: B :: Error (ce qui est normal car il inclut A :: B :: Error) mais il n'inclut pas tout le comportement 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 inclut Amazon :: AWS: Error dedans. (Patching singe)

Vous pouvez savoir programatically 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: le nom inclut le préfixe du module qui n'est pas le cas des tableaux constants Vous devriez contacter le mainteneur lib la classe AWSError ressemble plus à une classe usine: /)

Je n'ai pas de ruby-aws ici et le site caliban est bloqué par le pare-feu de l'entreprise, donc je ne peux pas tester beaucoup plus loin.

En ce qui concerne l'include: cela pourrait être la chose qui fait le patch singe sur la hiérarchie StandardError. Je ne suis plus sûr, mais très probablement à la racine d'un fichier en dehors de tous les contextes, il y a le module sur Object ou sur la métaclasse Object. (Ce qui est ce qui se passerait dans la CISR, où le contexte par défaut est l'objet, pas sûr dans un fichier)

du 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.

(modifier - Je ne peux pas sembler être en mesure de commenter en utilisant ce navigateur:/yay pour les plates-formes verrouillées)

+0

Je pense que vous avez raison .... J'ai essayé le code exact présenté dans la question et cela ne donne pas vrai ... donc il doit y avoir des patchs de singe en cours ... –

+0

Hm, pas exactement. Donc, ce truc AWS est définitivement dans la hiérarchie pour * tout *. Je suppose que j'ai foiré en disant "inclure Amazon :: AWS :: Error" en haut de mon fichier. Comment ça marche? Il comprend juste ce module dans la base de tout? – bhollis

+0

Malheureusement (comme je viens de le préciser dans ma question) la bibliothèque jette l'exception générée, pas AWSError. Ainsi, les exceptions apparaissent comme Amazon :: AWS :: Error :: InvalidParameterValue. – bhollis

1

Eh bien, ce que je peux dire:

Class.new(StandardError) 

crée une nouvelle classe avec StandardError comme la classe de base, donc il ne va pas être une Amazone :: AWS :: Erreur à tout. Il est juste défini dans ce module, ce qui est probablement pourquoi c'est un kind_of? Amazon :: AWS :: Erreur. Ce n'est probablement pas un kind_of? Amazon :: AWS parce que 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 certainement la classe de base sera StandardError.

MISE À JOUR: Soit dit en passant, 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

Ouais, je sais que la classe de base est 'StandardError'. Ce qui me rend fou, c'est que NameError.new.kind_of? (Amazon :: AWS :: Error) 'est vrai. Pourquoi cela serait-il? – bhollis

+0

Regarde ma mise à jour ... ça l'explique probablement ;-) –

+0

Non, je ne suis pas convaincu. ;-) Amazon :: AWS :: Error est un module. Ce n'est pas la même classe que NameError. Ce n'est pas une super-classe de NameError. Et NameError n'inclut pas, à ma connaissance, le module Amazon :: AWS :: Error. – bhollis

1

Je voulais juste entrer dans: Je suis d'accord c'est un bug dans le code de la bibliothèque. Il devrait probablement 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 
+0

Encore mieux serait d'avoir une classe AWSException qui était une sous-classe de StandardError, c'est-à-dire Amazon :: AWS :: AWSException

0

Une question que vous utilisez en est que Amazon::AWS::Error::AWSError n'est pas en fait une exception. Lorsque raise est appelée, il vérifie si le premier paramètre répond à la méthode exception et utilise le résultat de cette méthode à la place. Tout ce qui est une sous-classe de Exception renverra lui-même lorsque exception est appelé afin que vous puissiez faire des choses comme raise Exception.new("Something is wrong").

Dans ce cas, AWSError a exception mis en place comme un lecteur d'attributs dont il définit la valeur à l'initialisation à quelque chose comme Amazon::AWS::Error::SOME_ERROR. Cela signifie que lorsque vous appelez raise Amazon::AWS::Error::AWSError.new(SOME_XML) Ruby finit par appeler Amazon::AWS::Error::AWSError.new(SOME_XML).exception qui renvoie 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 courante. Jusqu'à ce que cela soit rectifié, la solution de Jean est probablement votre meilleur pari.

J'espère que cela a aidé à expliquer plus de ce qui se passe réellement dans les coulisses.

Questions connexes