tags:

views:

200

answers:

2

Hi, I was watching the first ruby metaprogramming screencast by prag dave. At some point he said that ruby introduce 'ghost classes' when you add a method to an instance variable. 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

Where is really the speak method store? If it is an invisible proxy class, How can we access to that proxy class?

+3  A: 

The method speak is stored in a metaclass (also known as an eigenclass), what you call an "invisible proxy class". In Ruby, instance variables have no place to store methods. They can only store instance variables and their class. So when you add a method to an instance, a metaclass is created and inserted into its class chain. For a better understanding of the internals, I recommend this article from Klank Boom Klang.

In order to get at the meta class, you can do the following:

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
Pesto
Thx, thats a very good answer.
Pedro
+1  A: 

Some ppl call it "Singleton Class"

singleton_class = class << animal; self; end

And actually this singleton class is the host for class methods at any class, check this example, first by defining class Foo with class methods 'hi' and 'bye':

class Foo
  def self.hi ; p "hi" ; end
  def self.bye ; p "bye" ; end
end
Foo.singleton_methods #=> ["hi","bye"]

Now let's define a method that returns back the singleton class for us:

class Object
  def singleton_class 
     class << self
       self
     end
  end
end

Now try this :

Foo.singleton_methods #=> ["bye", "hi"]
Foo.singleton_class.instance_methods(false) #=> ["bye", "hi"]
khelll