So if you’re a Ruby programmer you probably know that everything in Ruby is an object. Ok, good. But another thing that’s worth noting is Ruby objects hierarchy. Since Ruby 1.9, its hierarchy is like this:
You can open your irb console and see it for yourself by typing:
class Person end Person.class #=> Class Person.class.superclass #=> Module Person.class.superclass.superclass #=> Object Person.class.superclass.superclass.superclass #=> BasicObject Person.class.superclass.superclass.superclass.superclass #=> nil (there is nothing above BasicObject)
Differently from languages like Java and C# for example, you can change a Ruby Class on-the-fly. You can pretty much do:
class Fixnum def even? self%2 == 0 end end 1.even? #=> false 2.even? #=> true
Because classes are open in Ruby, you’re allowed to modify them during the execution of your program. This is a very very powerful feature and opens an ocean of possibilities. But wait! There is more! It’s even possible to modify an instance on-the-fly too!
french = Person.new def french.hello 'Bonjour' end french.hello #=> Bonjour person = Person.new person.hello # => undefined method 'hello'
We just created a method exclusively for the object and not for every instance of the Person class. But once again, everything in Ruby is an object, therefore, belong to a class. So here’s the 64k dollars question: when we add a method to an instance of Person, which class are we modifying? It can’t be Person, otherwise it would alter all the instances of Person. And we know it didn’t! Neither can it be in the object: we know objects don’t hold methods, but data. So…
Where the heck is the hello method?
The term “eigenclass” has been used in the ruby community to denote a class of a specific object. Some people like to call it “singleton classes” instead. One way or another, the concept remains. And what really happens when you modify an instance’s method is that Ruby creates an anonymous class to hold the object’s singleton methods. Then this anonymous class assumes the role of the object’s class. And finally, the original (and former) object’s class becomes the superclass of the anonymous class. Complicated eh? No really, it is. But hey, classes are open, what did you expect?
And just to confirm that the eigenclass holds the objects modified methods, try this in your irb console:
#reopens the Object class and create a method that returns the eigenclass class Object def eigenclass class << self; self; end end end french.eigenclass.instance_methods.include? :hello #=> true person.eigenclass.instance_methods.include? :hello #=> false
So there you go. The notation “class << self” is like saying “open the class of self for me and do:”. Since eigenclass() is an instance method, self inside it refers to the object calling the method. Once inside the self’s class, we can just return self, which will refer to the current class, which is the eigenclass.
The (complete) Ruby Object Model
So what happens when I create an eigenclass of a class? Let’s say that Person now inherits from the Animal class, that has the following method definition:
def Animal.lives? true end
Well, in that moment, an eigenclass is created to store the class method “lives?”. So now we have Animal and its eigenclass. When we call a class method in the Animal object itself, Ruby is going to look for it (1) in the Animal class and (2) in Animal’s eigenclass. This is called “The method lookup chain”.
But what happens when we call ‘Person.lives?’ ? It works! But how is it possible to inherit the superclass’s class methods? The thing is, when a class method is created inside Person’s superclass, Ruby not only generates Animal’s eigenclass, but also Person’s. And yet it makes Animal’s eigenclass become the superclass of Person’s eigenclass! I know, I know. Unbelievably complicated. You need to see the diagram to grasp the idea of it:
Remember, that’s the Ruby object method lookup path. This is how Ruby finds your methods. It is a really complicated subject, especially if you’re just starting to get acquainted with the Ruby language. But once you get it, you just don’t forget it. Just go ahead and play with these concepts. You’ll get the grasp before you know it…