Как получить доступ к родительским классам-призракам в ruby?
Вопрос
Я смотрел первый скринкаст по метапрограммированию ruby от прага Дейва.В какой-то момент он сказал, что ruby вводит "классы-призраки", когда вы добавляете метод к переменной экземпляра.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
Где на самом деле находится магазин speak method store?Если это невидимый прокси-класс, как мы можем получить доступ к этому прокси-классу?
Решение
Способ speak
хранится в метаклассе (также известном как собственный класс), который вы называете "невидимым прокси-классом".В Ruby переменным экземпляра негде хранить методы.Они могут хранить только переменные экземпляра и их класс.Таким образом, когда вы добавляете метод к экземпляру, создается метакласс и вставляется в его цепочку классов.Для лучшего понимания внутренних компонентов я рекомендую эта статья из Кланк-Бум-Кланга.
Чтобы получить доступ к метаклассу, вы можете сделать следующее:
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
Другие советы
Некоторые ppl называют это "Одноэлементным классом".
singleton_class = class << animal; self; end
И на самом деле этот одноэлементный класс является хостом для методов класса в любом классе, проверьте этот пример, сначала определив класс Foo с методами класса 'hi' и 'bye':
class Foo
def self.hi ; p "hi" ; end
def self.bye ; p "bye" ; end
end
Foo.singleton_methods #=> ["hi","bye"]
Теперь давайте определим метод, который возвращает нам одноэлементный класс:
class Object
def singleton_class
class << self
self
end
end
end
А теперь попробуй вот это :
Foo.singleton_methods #=> ["bye", "hi"]
Foo.singleton_class.instance_methods(false) #=> ["bye", "hi"]