ruby reflection metaprogramming

¿Cómo obtengo las constantes definidas por la clase de Ruby''s Module a través de la reflexión?



reflection metaprogramming (2)

Intenté captar en mi cabeza el capítulo de metaprogramación "Lenguaje de programación de Ruby" de Matz y Flanagan. Sin embargo, no pude entender el resultado del siguiente fragmento de código que 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"]

El libro indica que las constants método de clase devuelve la lista de constantes para la clase (como se puede ver en la salida para A.constants ). Estaba tratando de obtener la lista de constantes definidas para la clase Module cuando encontré el extraño comportamiento anterior.

A constantes de A aparecen en Module.constants. ¿Cómo obtengo la lista de constantes definidas por la clase Módulo?

El estado de los docs

Module.constants devuelve todas las constantes definidas en el sistema. incluyendo nombres de todas las clases y métodos

Como A hereda su implementación de Module.constants , ¿cómo se comporta de forma diferente en los tipos base y derivado?

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

Nota: Si está usando Ruby 1.9, las constants devolverían una matriz de símbolos en lugar de cadenas.


¡Buena pregunta!

Su confusión se debe al hecho de que el método de clase Module.constants oculta el método de instancia Module#constants de Module Module#constants para Module .

En Ruby 1.9, esto se ha solucionado al agregar un parámetro opcional:

# 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).

En el ejemplo anterior, A.constants llama a las Module#constants (el método de instancia), mientras que Module.constants llama, bueno, Module.constants .

En Ruby 1.9, desea llamar a Module.constants(true) .

En Ruby 1.8, es posible llamar al método de instancia #constants en Module . Necesita obtener el método de instancia y vincularlo como un método de clase (usando un nombre diferente):

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

Desearía poder respaldar completamente la funcionalidad 1.9.0 a 1.8 para mi joya de backports , pero no puedo pensar en una forma de obtener solo las constantes de un Módulo, excluyendo las heredadas, en Ruby 1.8.

Editar : acaba de cambiar la documentación oficial para reflejar correctamente esto ...


Tuve que volver a mi cueva de pensamiento por un tiempo después de la respuesta de Marc. Tintado con más fragmentos de código y algo más. Finalmente, cuando la resolución del método de Ruby parecía tener sentido, la anoté como una publicación de blog para que no la olvidara.

Notación: si A " es la clase de creación de A

Cuando se llama a A.constants , la resolución del método (consulte la imagen en la publicación de mi blog para obtener ayuda visual) busca las siguientes ubicaciones en orden

  • MyClass" , Object" , BasicObject" (métodos singleton)
  • Class (métodos de instancia)
  • Module (métodos de instancia)
  • Object (métodos de instancia) y Kernel
  • BasicObject (métodos de instancia)

Ruby encuentra el método de instancia Module#constants

Cuando se llama Module.constants , Ruby mira

  • Module" , Object" , BasicObject" (métodos singleton)
  • Class (métodos de instancia)
  • Module (métodos de instancia)
  • Object (métodos de instancia) y Kernel
  • BasicObject (métodos de instancia)

esta vez, Ruby encuentra el método de singleton / clase en Module".constants como dijo Marc.

El módulo define un método singleton que sombrea el método de la instancia. El método singleton devuelve todas las constantes conocidas, mientras que el método de instancia devuelve las constantes definidas en la clase actual y sus antecesores.