I don’t know about you, but that was my exact reaction when I first heard
Nadia Odunayo’s talk in
RubyConf KL 2018 (mind you, one of the most captivating talk I’ve ever heard!), in which she showed that that Ruby has no class methods.
When I first started learning Ruby, I’ve been told that Ruby basically have two types of methods:
- Class Methods
- Instance Methods
Well at first, it makes a lot of sense —
A class is an object, and when you call
.newon a class, you create a new instance of that object.
Hence, instance methods are only available on instances of that class.
Okay so then.. what am I talking about when I said Ruby has no class methods?
Parents. Grandparents. Ancestors..!
First thing we need to understand is something called Ancestor Chains in Ruby.
Ancestors Chains is vital to understanding how Ruby works; the entire method calling chain works because of the chain, but here I’ll attempt at an overly simplified explanation.
In Ruby, everything inherits
Object , and every
Object inherits from a
Let’s do a quick check!
When you call a method, it calls the method on the current class; if the current class does not have that method, it goes up the ancestor chain until it finds one.
Not clear yet? I’ve gotchu! Cue weird metaphor
Imagine you’re just a kid, and that annoying Little Johnny who is trying to outsmart you asks you sheepishly: “Do you know what
.planetsare in our solar system? I bet you don’t!”.
Well you actually don’t know the answer, but you wouldn’t let Little Johnny get his victory, and so you make this your life goal to find out, and you ask your parents: “What are the
.planetsin our solar system?”. And well, your parents don’t know that either, so they go on and ask their parents, who happen to know the answer.
Now it’s your turn to smirk sheepishly back at Little Johnny and tell him what the answer is.
So now whenever someone asks you what are the
.planetsin our solar system, you have the answer, from your Grandparents.
How is this relevant? Well, we talked about how method lookup work, and in my last post I talked about reusing methods (
prepend. Now let’s look at their respective ancestor chains when you
prepend a module.
You’ll realize something strange:
- When you
include, the module is being inserted after the current class, which means it’ll be the first thing you hit when you can’t find a method on current class.
prependis the opposite of
include; it inserts before, so technically, you still wouldn’t be able to call it.
- You don’t see the module on the ancestor chain when you extend a module
Hold on, we know for a fact that when we
extend a module, we get access to class methods, and we also know that method calling happen via ancestor chains, but.. how can
extend work if the module is not on the ancestor chain?
The reason is because of something in Ruby called Singleton Class (sometimes referred to as
Anonymous Class or
Whenever you initialize a class in Ruby, two objects are generated —
Foo itself, and
Foo’s Singleton Class. To prove this, in her talk Nadia showed a great way to inspect the current count of classes using
ObjectSpace is basically a way for you to interact with all currently living Ruby objects in memory.
Let’s take a look at what is the
.class of the class you just created.
As you may have noticed earlier, there's also an alternative way to initialize a class as below:
Looks really familiar now doesn’t it? Foo is an instance of Class.
Why is this important? Because this means that the normal Class Methods that you have come to think you’re executing on
Foo is in fact, an instance method on
Class. — It works because
Foo is an instance of
We now know singleton classes aren’t visible on ancestor chains. Remember what else wasn’t visible on the chain? The module when you do an
We can check the ancestor chain of the singleton class itself.
We can also prove that what we come to know as
class_method, is actually just an instance method on the singleton class.
So now we know what
extend actually does behind the scene is basically an
include on the singleton class itself. Let’s prove it!
But yeah, writing the full form requires a staggering three more words, that’s a lot of work, so we’ve got
extend to save us that keystroke instead!
I’ll be pumping out a few more articles, meanwhile do check-out my last post on
Phew, that’s it for my second blogpost. Looking forward to learning and sharing more!