Wie man Mutter Geist in Ruby-Klassen zugreifen?
Frage
Ich war das erste Rubin metaprogramming Screencasts von prag dave beobachten. Irgendwann sagte er, dass Rubin "Geister Klassen einführen, wenn Sie eine Methode zu einer Instanzvariablen hinzufügen. 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
Wo ist wirklich die sprechen Methode speichern? Wenn es eine unsichtbare Proxy-Klasse ist: Wie können wir auf diese Proxy-Klasse zugreifen?
Lösung
Das Verfahren speak
in einem metaclass (auch als Eigenklasse genannt) gespeichert, was Sie eine „unsichtbare Proxy-Klasse“ nennen. In Ruby hat Instanzvariablen keinen Platz Methoden zu speichern. Sie können nur Instanzvariablen und ihre Klasse speichern. Also, wenn Sie eine Methode zu einer Instanz hinzufügen, wird ein Metaklasse erstellt und in seine Klasse Kette eingefügt. Zum besseren Verständnis der Einbauten, empfehle ich diesem Artikel aus Klank Boom Klang.
Um bei der Meta-Klasse zu erhalten, können Sie wie folgt vor:
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
Andere Tipps
Einige ppl nennen es "Singleton Klasse"
singleton_class = class << animal; self; end
Und tatsächlich diese Singletonklasse ist der Host für Klassenmethoden in jeder Klasse, lesen Sie in diesem Beispiel zunächst durch die Definition der Klasse Foo mit Klassenmethoden ‚hallo‘ und ‚bye‘:
class Foo
def self.hi ; p "hi" ; end
def self.bye ; p "bye" ; end
end
Foo.singleton_methods #=> ["hi","bye"]
Nun wollen wir eine Methode definieren, die für uns die Singleton-Klasse gibt zurück:
class Object
def singleton_class
class << self
self
end
end
end
Jetzt versuchen Sie dies:
Foo.singleton_methods #=> ["bye", "hi"]
Foo.singleton_class.instance_methods(false) #=> ["bye", "hi"]