ruby_cool_kid.rb — Meta Programming series: Like Object, Like Class. Ruby Ancestors I

Pablo Adell
6 min readSep 26, 2021

--

Photo by Joshua Fuller on Unsplash

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:

Base code for MyHouse class

After running it, if we could look inside pablo_house object, it would look like this:

pablo_house object inside look

Our object (or instance of the classMyHouse) pablo_housecontains 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.

Console output of instance_variables of pablo_house

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:

Difference between instance_methods and methods

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:

Console output of the class of Class

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:

Console output of instance methods of Class

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 :superclasson 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

Console output of the superclass of Class

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.

Inheritance chain of MyHouse

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 of Module 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 !!

--

--

Pablo Adell
Pablo Adell

Written by Pablo Adell

Hello there!! My name is Pablo, I am a Full-Stack Software Engineer at Affirm, focused mainly in Ruby on Rails and React. I hope you enjoy my content!

No responses yet