Design, OOP, Ruby

Eigenclasses for lunch – The Ruby Object Model

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:

Ruby Basic Model

*’C’ means Class and ‘S’ means Superclass.

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:

The Ruby Object Model

* ‘C’ means class and ‘S’ means superclass.

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…

Padrão

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s