2010-02-22 5 views
38

Je tentais d'obtenir Matz et le chapitre metaprogramming « Ruby Programming Language » Flanagan dans ma tête, mais je ne pouvais pas comprendre la sortie de l'extrait de code suivant que j'Imaginée:Comment obtenir des constantes définies par la classe Module de Ruby par réflexion?

p Module.constants.length   # => 88 
$snapshot1 = Module.constants  
class A 
    NAME=:abc 

    $snapshot2 = Module.constants 
    p $snapshot2.length    # => 90 
    p $snapshot2 - $snapshot1   # => ["A", "NAME"] 

end 
p Module.constants.length   # => 89 
p Module.constants - $snapshot1  # => ["A"] 
p A.constants      # => ["NAME"] 

Les Etats livre que la méthode de classe constants renvoie la liste des constantes pour la classe (comme vous pouvez le voir dans la sortie A.constants). J'essayais d'obtenir la liste des constantes définies pour la classe Module quand je suis tombé sur le comportement étrange ci-dessus.

A Les constantes s'affichent dans les constantes Module. Comment obtenir la liste des constantes définies par la classe Module?

Les docs état

Module.constants renvoie toutes les constantes définies dans le système. y compris les noms de toutes les classes et méthodes

Depuis sa mise en œuvre A hérite de Module.constants, comment il se comportent différemment dans la base et les types dérivés?

p A.class    # => Class 
p A.class.ancestors  # => [Class, Module, Object, Kernel] 

Remarque: Si vous utilisez Ruby 1.9, constants retournerait un tableau de symboles au lieu de chaînes.

+0

Ai-je répondu à votre question? Je demande parce que ma réponse n'a pas été «acceptée», mais n'a pas demandé d'informations supplémentaires ... –

+0

@Marc - Votre réponse m'a amené à plus de questions et plus de gribouillage et d'effacement. J'ai passé cette semaine à essayer de comprendre le système de résolution de la méthode ... Je ne sais toujours pas quelles sont les possibilités - mais je pense que je suis sûr à 90% de son fonctionnement. Voir mon message ci-dessous. – Gishu

Répondre

47

Bonne question! Votre confusion est due au fait que la méthode de classe Module.constants cache la méthode d'instance Module#constants pour Module.

Dans Ruby 1.9, il a été résolu en ajoutant un paramètre optionnel:

# No argument: same class method as in 1.8: 
Module.constants   # ==> All constants 
# One argument: uses the instance method: 
Module.constants(true) # ==> Constants of Module (and included modules) 
Module.constants(false) # ==> Constants of Module (only). 

Dans votre exemple ci-dessus, A.constants appels Module#constants (la méthode d'instance), tandis que les appels Module.constants, eh bien, Module.constants. En Ruby 1.9, vous voulez donc appeler Module.constants(true). En Ruby 1.8, il est possible d'appeler la méthode d'instance #constants en Module. Vous devez obtenir la méthode d'instance et le lier comme méthode de classe (en utilisant un autre nom):

class << Module 
    define_method :constants_of_module, Module.instance_method(:constants) 
end 

# Now use this new class method: 
class Module 
    COOL = 42 
end 
Module.constants.include?("COOL") # ==> false, as you mention 
Module.constants_of_module   # ==> ["COOL"], the result you want 

Je voudrais être en mesure de rétroporter la fonctionnalité 1,9 complètement à 1,8 pour mon petit bijou backports, mais je peux » Je pense à un moyen d'obtenir uniquement les constantes d'un module, à l'exclusion de celles héritées, dans Ruby 1.8.

Modifier: Il suffit de changer la documentation officielle pour refléter correctement cette ...

+0

@ Marc - apprécierait si vous pouviez aller sur ma compréhension de la résolution de la méthode et le confirmer. Lien de blog dans mon post ci-dessous. Merci et accepté :) – Gishu

3

je devais retourner dans ma grotte de penser pendant un certain temps après la réponse de Marc. Bricolé avec plus d'extraits de code et plus encore. Enfin, lorsque la résolution de la méthode de Ruby a semblé avoir du sens, elle a été rédigée comme article de blog pour que je n'oublie pas.

Notation: Si A » est le eigenclass de A

Lorsque A.constants est appelée, la résolution de la méthode (voir l'image en my blog post d'avoir une aide visuelle) recherche les endroits suivants afin

  • MyClass", Object", BasicObject" (méthodes singletons)
  • Class (instance méthodes)
  • Module (méthodes d'instance)
  • Object (méthodes d'instance) et le noyau
  • BasicObject (méthodes d'instance)

Ruby trouve la méthode d'instance Module#constants

Lorsque Module.constants est appelé, Ruby regarde

  • Module", Object", BasicObject" (méthodes singletons)
  • Class (méthodes d'instance)
  • Module (méthodes d'instance)
  • Object (méthodes d'instance) et le noyau
  • BasicObject (méthodes d'instance)

cette fois, Ruby trouve la méthode singleton/class au comme Marc l'a dit.

Le module définit une méthode singleton qui ombrage la méthode d'instance. La méthode singleton renvoie toutes les constantes connues alors que la méthode d'instance renvoie les constantes définies dans la classe actuelle et ses ancêtres.