Como acessar a classes fantasma pais em Ruby?
Pergunta
Eu estava assistindo o primeiro screencast rubi metaprogramming por prag dave. Em algum momento ele disse que ruby ??introduzir 'classes fantasmas' quando você adicionar um método para uma variável de instância. 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
Onde é realmente a loja método de falar? Se é uma classe de proxy invisível, Como podemos ter acesso a essa classe de proxy?
Solução
O speak
método é armazenado em um metaclasse (também conhecido como um eigenclass), o que você chama uma "classe de proxy invisível". Em Ruby, variáveis ??de instância não têm lugar para armazenar métodos. Eles só podem armazenar variáveis ??de instância e sua classe. Então, quando você adicionar um método para uma instância, uma metaclasse é criado e inserido na sua cadeia de classe. Para uma melhor compreensão dos internos, eu recomendo este artigo de Klank lança Klang.
A fim de obter a classe meta, você pode fazer o seguinte:
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
Outras dicas
Alguns ppl chamá-lo de "Singleton Classe"
singleton_class = class << animal; self; end
E, na verdade, essa classe singleton é o anfitrião para métodos de classe em qualquer classe, verifique este exemplo, primeiro através da definição de classe Foo com métodos de classe 'oi' e 'tchau':
class Foo
def self.hi ; p "hi" ; end
def self.bye ; p "bye" ; end
end
Foo.singleton_methods #=> ["hi","bye"]
Agora vamos definir um método que retorna a classe singleton para nós:
class Object
def singleton_class
class << self
self
end
end
end
Agora, tente o seguinte:
Foo.singleton_methods #=> ["bye", "hi"]
Foo.singleton_class.instance_methods(false) #=> ["bye", "hi"]