Comment puis-je obtenir des constantes définies par la classe du module Ruby via la reflection?

J’essayais de prendre dans ma tête le chapitre sur le métaprogrammation “Ruby Programming Language” de Matz et Flanagan. Cependant, je ne pouvais pas comprendre le résultat de l’extrait de code suivant que j’avais imaginé:

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"] 

Le livre indique que les constants méthode de classe renvoient la liste des constantes de la classe (comme vous pouvez le voir dans la sortie de A.constants ). J’essayais d’obtenir la liste des constantes définies pour la classe Module lorsque j’ai rencontré ce comportement étrange.

Les constantes de A apparaissent dans Module.constants. Comment puis-je obtenir la liste des constantes définies par la classe Module?

L’état des documents

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

Puisque A hérite de son implémentation de Module.constants , comment se comporte-t-il différemment dans les types de 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, les constants renverraient un tableau de symboles au lieu de chaînes.

Bonne question!

Votre confusion est due au fait que la méthode de classe Module.constants masque la méthode d’instance Module#constants pour Module .

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

 # 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 appelle les Module#constants (méthode de l’instance), tandis que les Module.constants appellent, bien, Module.constants .

Dans Ruby 1.9, vous voulez donc appeler Module.constants(true) .

Dans Ruby 1.8, il est possible d’appeler la méthode d’instance #constants sur le Module . Vous devez obtenir la méthode d’instance et la lier en tant que méthode de classe (en utilisant un nom différent):

 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 

J’aurais aimé pouvoir transférer complètement la fonctionnalité 1.9 vers la version 1.8 pour mon bijou de backports , mais je ne vois pas comment obtenir uniquement les constantes d’un module, à l’exclusion des héritées, dans Ruby 1.8.

Edit : Il suffit de changer la documentation officielle pour bien refléter cela …

Je devais retourner dans ma grotte de reflection pendant un moment 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é logique, elle a été écrite comme un article de blog pour que je n’oublie pas.

Notation: Si A “ est la classe propre de A

Lorsque vous A.constants , la résolution de la méthode (reportez-vous à l’image de mon blog pour obtenir une aide visuelle) recherche les emplacements suivants dans l’ordre.

  • MyClass" , Object" , BasicObject" (méthodes singleton)
  • Class (méthodes d’instance)
  • Module (méthodes d’instance)
  • Object (méthodes d’instance) et kernel
  • BasicObject (méthodes d’instance)

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

Lorsque Module.constants est appelé, Ruby examine

  • Module" , Object" , BasicObject" (méthodes singleton)
  • Class (méthodes d’instance)
  • Module (méthodes d’instance)
  • Object (méthodes d’instance) et kernel
  • BasicObject (méthodes d’instance)

Cette fois, Ruby trouve la méthode singleton / class dans Module".constants comme dit Marc.

Module définit une méthode singleton qui masque la méthode d’instance. La méthode singleton renvoie toutes les constantes connues, tandis que la méthode d’instance renvoie les constantes définies dans la classe actuelle et ses ancêtres.