ruby_cool_kid.rb — Meta Programming series: Like Object, Like Class. Ruby Ancestors I
The phrase “Like father, like son” would be very appropriate for this story. Ruby, as any OOP language, is based on objects, and those objects, can inherit or be inherited (dramatic pause)
After this dramatic pause, let’s start with a concept well known for everybody, a Class
. One would say a class is just a “template” for creating objects in OOP. But, what is really a class in Ruby?
Well, we could say that a Class
is a template for creating an object, but also the sum of its ancestors. Sounds weird right? At this point, one could be wondering what Class
is in Ruby. Here it comes the plot twist, Class
in Ruby is nothing more than an object.
I know it can be confusing and overwhelming, I was in shocked the first time I heard about it. As it seems that it all comes to the objects, let’s see what is an object in Ruby.
Objects in Ruby
Imagine executing the following code:
After running it, if we could look inside pablo_house
object, it would look like this:
Our object (or instance of the classMyHouse
) pablo_house
contains just a reference to its class and its own instance_variables
, but no methods.
If you are a little familiar with Ruby or OOP, you would know that instance_variables
are variables that only live in the object itself, not being shared among objects of the same class. Ruby allows us to obtain them (even though we should not do it) by callinginstance_variables
on the object.
On the other hand, methods
in the class are shared among the objects, so they must be stored inside the class.
Bearing this in mind, it is important that we talk properly about methods. Methods defined inside the class, are nothing but instance_methods
(shared among all instances of the class), as we need an object (instance of the class) to call them, but just methods if we talked about them from the object’s perspective.
To put it into real-world words, we could say that pablo_house
has a method called my_room
and, the class MyHouse
, has an instance_method
called my_room
, but we could not say that MyHouse
has a method called my_room
as the class by itself cannot call it.
Ruby speaks this same language so, we could run:
After understanding what an object is, we can go one level up and go back to our main character, Classes
Classes in Ruby
A few lines above we said:
Class
in Ruby is nothing more than an object
Therefore, it is obvious that the same principles of the objects apply to it. But wait a minute, if Class
is an object, what is the class of Class
?
Don’t panic! Bear here with me, the class of Class
could be obtained by simply running:
Yes! The class of Class
is Class
. Funny right? 😜
If you are familiar with the description of Class
from other OOP languages, Class
is just a “read-only description of the class” (from the book Metaprogramming in Ruby 2, by Paolo Perrotta) . This might sound confusing but, in Ruby, Class
is literally the class itself, and can be manipulated as you would do with any other object.
But what could be done with this power? For example, given the dynamic nature of Ruby, defining classes at runtime calling Class.new
, as that new class would just be an instance of Class
being able to call all the instance_methods
available for it. In the end classes, like any object, also have methods.
Given our previous note on methods for an object and a class, what would happen if we execute Class.instance_methods
. Try it. The output should be:
In this output, we can see a really interesting instance method of Class
, which is :superclass
, that is related with the concept of inheritance.
If we were curious and called :superclass
on String
, we would see that the superclass, or parent, of it is Object
, a class that contains useful methods for any object, like is_a?
.
However, if we went up and call superclass
on Object
, we would get to the top of the Ruby class hierarchy, BasicObject
, which contains just the essential methods for an object.
What about the superclass of Class
? Well, easy, Module
Wait, what? 😵
Do not vanish, folks, let’s analyze it, shall we?
Modules in Ruby
Module
is the superclass of Class
, so every Class
is also a Module
. Actually, if we take a closer look at their instance_methods
, Class
just has three additional methods that Module
does not.
Which ones? :superclass, :new and :allocate
. Surprisingly enough, those are the ones that allow us to create objects and arrange classes into hierarchies.
But, why such difference? Well, it is just a matter of making your code clearer by being more explicit. If we just want to include somewhere the code, we will choose Module
, whereas, if we want to inherit or instantiate that code, we will pick Class
. Easy right?
Taking all the information we gathered, let’s picture it to make it clearer by using our MyHouse
class, as well as its superclasses, thus, inheritance.
Finally, let’s put the cherry on top to end this story.
Anybody that has, anytime, worked with OOP, knows that a variable can reference an object or instance of a class. Thus, it is logical that a variable can reference a class itself because, as we said before, Class
is just an object. Taking this into account, we could:
But …. looks weird right? There is something off. What would happen if we did:
Of course, a variable can be changed, thus losing the class. So, what could we use that can store a reference to the code inside a class and cannot be (or should not be) overwritten 🤔.
Co.. Cons.. Constants you say? Yes! you are completely right! That is why, all the names of classes and modules in Ruby are capitalised, marking their constant nature, in Ruby, everything are constants.
Quick concepts before wrapping up:
- An object is just a bunch of instance variables and a link to a class. Methods don’t live in the object but rather on the object’s class-.
- A class is just an object ( or instance of
Class
) that has a bunch of methods and a link to a superclass.Class
is the subclass ofModule
so, in the end, a class is also a module with instance methods like:new
. - The name of a class must be a constant so that we have a constant (pun intended 😉) reference to it.
This was all for today! Stay tuned for the next episode of Ruby Ancestors, Ruby magic has just begun and everything will make sense in the end. I promise. See you around !!