¿Cómo acceder a las clases de padres fantasmas en ruby?
Pregunta
Estuve viendo la primera metaprogramación de rubíes grabada por Prag Dave. En algún momento, dijo que ruby ??introduce "clases de fantasmas" cuando agregas un método a una variable de instancia. i.
animal = "cat"
def animal.speak
puts "hola"
end
animal.speak # => hola
animal.class # => String
dog = "dog"
dog.speak # Undefined method `speak' for "dog":String
String.send :speak # NoMethodError: undefined method `speak' for String:Class
animal.send :speak # hola
¿Dónde está realmente la tienda de métodos de conversación? Si se trata de una clase de proxy invisible, ¿cómo podemos acceder a esa clase de proxy?
Solución
El método speak
se almacena en una metaclase (también conocida como eigenclass), lo que usted llama una "clase proxy invisible". En Ruby, las variables de instancia no tienen lugar para almacenar métodos. Solo pueden almacenar variables de instancia y su clase. Entonces, cuando agrega un método a una instancia, se crea una metaclase y se inserta en su cadena de clase. Para una mejor comprensión de los aspectos internos, recomiendo este artículo de Klank Boom Klang.
Para llegar a la meta clase, puedes hacer lo siguiente:
animal = "cat"
def animal.speak
puts "hola"
end
animal.speak # => hola
animal.class # => String
metaclass = class << animal; self; end
metaclass.inspect # => #<Class:#<String:0x2c9c460>>
metaclass.instance_methods.index 'speak' # => 102
metaclass.class # => Class
Otros consejos
Algunas personas lo llaman " Clase Singleton "
singleton_class = class << animal; self; end
Y, de hecho, esta clase de singleton es el host de los métodos de clase en cualquier clase, verifique este ejemplo, primero definiendo la clase Foo con los métodos de clase 'hi' y 'bye':
class Foo
def self.hi ; p "hi" ; end
def self.bye ; p "bye" ; end
end
Foo.singleton_methods #=> ["hi","bye"]
Ahora definamos un método que devuelva la clase singleton para nosotros:
class Object
def singleton_class
class << self
self
end
end
end
Ahora prueba esto:
Foo.singleton_methods #=> ["bye", "hi"]
Foo.singleton_class.instance_methods(false) #=> ["bye", "hi"]