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.