This blog post is part of a series on Class Inheritance. Read Part 1 here.
I recently stumbled upon this line of code that totally floored me. I mean, I had to drop everything I was doing and do some serious Googling, my friends. Well, that and also I asked my fellow developers at work who are far more seasoned programmers than me.
Now, I’m going to share this line of code with you, but you’ve got to try not to totally freak out if you haven’t seen it before. Are you ready? Ok, here we go:
1 2 3 4 5 6 7 |
|
Are you with me? Did you make it past the second line? Do I need to call an ambulance? Well, if your reaction was anything like mine, you saw class << self
and experienced something akin to an aneurysm.
Ok, let’s agree on one thing right here, right now: everything in Ruby is an object. No matter how crazy it’s about to get, just remember that. In fact, it’s worth repeating again: Everything in Ruby is an object – even a class.
Got it? Okay, now let’s figure what the hell that code means, exactly.
Class Methods, Because Your Instances Should Never Be Too Greedy
In order to understand the meaning behind class << self
, we first need to understand the << self
syntax. Let’s recap a couple basics about class inheritance first:
- Classes can have two different types of methods: class methods, which can be called on a
Class
object, and instance methods, which can only be called on instances of aClass
object. - There are multiple ways to create class methods:
You can use self
as the receiver of a method within a class, which is probably the most common way of defining a class method:
1 2 3 4 5 |
|
You can also define a method on the class name (for example, class Book
), which implies that the entire Class Book
object can receive the type
method:
1 2 3 |
|
But, you can also do this:
1 2 3 4 5 |
|
And – wait for it – this:
1 2 3 4 5 6 7 |
|
Okay, so that funky class << self
syntax is just another way of defining a class method? But…but how?
One Method To Rule Them All
So, we know that class methods are a good place to put all the behavior and functionality of all instances of that class. If you want all instances of your Book
objects to have a title and an author, you’d want that behavior to be put into the Book
class, since all instances of Book will inherit from their parent class.
But, what if you wanted a particular instance of a Book
to have a specific kind of functionality. Well, you definitely wouldn’t want to define a method on the class, because it’s only a particular instance that you care about. So why not define a method on that particular instance, then?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
At first glance, nothing here seems all that weird, right? Both the books have access to the type
class method, which we defined earlier, while only the charlottes_web
Book
object has access to the wilbur
method. Ruby seems to know that the wilbur
method is scoped only to this instance, but it also knows that this instance has other methods – class methods – that are also accessible to it. But how is this actually possible?
Well, the answer is for two reasons: singleton methods and Ruby’s method lookup chain.
Singleton methods are methods defined on an object itself, rather than on an object’s class. Ruby’s method lookup chain is pretty intricate:
An object inherits from its class, and its class can inherit from many more objects, which will then inherit from Object
, which inherits from the great grandma of all objects in Ruby: BasicObject
. Remember: everything in Ruby is an object – even a class.
So at this point, you’re probably wondering what this has to do with class methods. Well, a lot, actually.
A Class Of One’s Own
We can create all the singleton methods we want, but where do they all go? Not in the Book
class, that’s for sure. So where do we put them, exactly?
It turns out, an object’s singleton methods all go into their own neat little class called an eigenclass, which is just another name for a singleton class. The word “eigen” comes from a German term meaning “one’s very own”. I personally like to call it an eigenclass because it minimizes confusion between singleton methods and classes. Also, throwing down terms like eigenclass make you sound like a total badass.
Here’s the cool thing about eigenclasses: they’re totally hidden. I should warn you though, the thing that makes them cool is the very same thing that makes them complicated. Because they’re hidden and anonymous, you obviously can’t see them and so it’s hard to figure out where in the method lookup path they are. Here’s the trick, though: whenever you open up an eigenclass, you shift up the original class.
In my book example, the charlottes_web
object inherits from the Book
class. But when I created the wilbur
method, I opened up the object’s eigenclass, and shifted up the original Book
class. Now, the charlottes_web
object inherits from its eigenclass, which in turn inherits from the Book
class. The Book
class has now become the superclass of our object.
Okay, that was a lot. To recap, here’s the most important stuff to know about the mysterious eigenclass:
- An eigenclass is an anonymous class that is created to hold an object’s singleton methods.
- The anonymous eigenclass then becomes the object’s immediate class, which it inherits from.
- The original class is re-designated as the superclass of the anonymous eigenclass.
- But because eigenclasses are hidden, when you call
charlottes_web.class
, it will returnBook
.
Reveal Thyself, You Eigen, You!
So, eigenclasses don’t show themselves. Cool. Except not cool. Because I want to see it! Well, you can see it, but you have to do a little extra work to make the magic happen. Actually, all you have to do is add a method available to all Ruby Object
s, like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
We open up the eigenclass of the object when we use the class << self
syntax. And once we open it up, we are in the scope of the eigenclass. Here, self
is now the eigenclass object (everything in Ruby is an object!), and since we are returning self
, we will get to see what the eigenclass actually looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Interesting. So, the eigenclass of our charlottes_web
object does look pretty anonymous, and you might not even notice its weirdness at first. But, it does clearly seem to be a Class
object and, as we expected, was inserted into the method lookup chain right between the object and the Book
class.
But if you look at the last two lines and their value, that’s where stuff gets really interesting. The wilbur
method, which we defined on a particular instance of an object, is a singleton method on only that object. The type
method, however, lives in the eigenclass of that object.
Is it all coming together now? While the wilbur
method could only be called on an instance, the type
method can be called on an entire class. So, perhaps you’re not not technically creating a class method, but more of an “eigenclass class method”. In fact, there actually is no such thing as a “class method” – you’re actually inside of the eigenclass when you create that. But effectively, it’s the same thing, because you can’t see an eigenclass and it still inherits all the methods from a basic Class
object.
WAT
If all of this was Greek to you, don’t worry. This is just honestly really hard stuff. At the end of the day, all you really need to know is that class << self
is just another way of defining a class method. That and, everything in Ruby is an object.
There’s plenty of debate about whether or not using the class << self
syntax is helpful or just confusing. Personally, I like to use it when I have a lot of class methods to define. It can keep your code clean and easy to read, and helps you avoid typing out self.method
multiple times.
But, if DRYing out your code is more confusing to you than helpful, you should probably avoid it. It’s more about personal preference than anything else. Find a style that works for you and try not to think too much about all the crazy anonymous classes spinning around your head. Try being the keyword here.
tl;dr?
- Technically speaking, there’s actually no such thing as a class method in Ruby. You’re always opening up the eigenclass/singleton class of an object whenever you define a class method. Keep in mind that there are many different syntaxes you can use in order to achieve this!
- Curious about the
self.method
vsclass << self
debate? Check out this thread on Stack Overflow. - Read more about the method lookup path to understand where Ruby looks for stuff.
- Tons of good resources on this stuff here and here. Oh, and here.