Comment accéder aux classes de parents fantômes en rubis?
Question
Je regardais le premier screencast de métaprogrammation de ruby ??réalisé par prag dave. À un moment donné, il a dit que ruby ??introduisait des «classes fantômes» lorsque vous ajoutez une méthode à une variable d'instance. 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
Où est vraiment le magasin de méthode de parole? S'il s'agit d'une classe proxy invisible, comment pouvons-nous accéder à cette classe proxy?
La solution
La méthode speak
est stockée dans une métaclasse (également appelée classe propre), ce que vous appelez une "classe de proxy invisible". En Ruby, les variables d'instance n'ont pas de place pour stocker des méthodes. Ils ne peuvent stocker que des variables d'instance et leur classe. Ainsi, lorsque vous ajoutez une méthode à une instance, une métaclasse est créée et insérée dans sa chaîne de classes. Pour une meilleure compréhension des éléments internes, je recommande cet article de Klank Boom Klang.
Pour accéder à la méta-classe, vous pouvez procéder comme suit:
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
Autres conseils
Certains ppl l'appellent "Singleton Class"
singleton_class = class << animal; self; end
Et en réalité, cette classe singleton est l'hôte des méthodes de classe de toute classe. Vérifiez cet exemple, tout d'abord en définissant la classe Foo avec les méthodes de classe 'hi' et 'bye':
class Foo
def self.hi ; p "hi" ; end
def self.bye ; p "bye" ; end
end
Foo.singleton_methods #=> ["hi","bye"]
Définissons maintenant une méthode qui retourne la classe singleton pour nous:
class Object
def singleton_class
class << self
self
end
end
end
Maintenant, essayez ceci:
Foo.singleton_methods #=> ["bye", "hi"]
Foo.singleton_class.instance_methods(false) #=> ["bye", "hi"]