Module#extend: Understanding Ruby Singleton Classes

Jem Zornow
4 min readOct 22, 2018

--

There are three major means of importing code into a Ruby class or module: Module#include, Module#prepend, and Module#extend. As covered in an earlier post, #include and #prepend make methods available by tricking out an object’s ancestor chain, giving Ruby new instructions on how, where, and in what order to look for a classes’ methods.

Module#extend does something very different. First, let’s look at what happens when we use #extend to import a module’s methods. Then we’ll pop open the hood and see if we can figure out just how that’s happening.

What Does Module#extend do?

The simple answer: Module#extend makes a module’s methods available as class methods:

When RobotMethods are included into the Extender class, that module’s method is made accessible at the class level. Those methods, however, are not made accessible as instance methods, as they would be if we’d used #include or #prepend.

But how? In order to understand what’s happening here, we’ll have to take a broader look at Ruby method dispatch, classes, and the idea of class methods.

Ruby Method Dispatch

The first question we should ask is “Where do methods live?”

If you say “on the class” you’d only be half right. Ruby classes define instance methods, accessible to instances of that class.

Consider an instance of the class Employee:

Here’s how an instance of this object is actually represented in memory.

some_employee doesn’t know that it has a method called work. All it knows is that it is a member of the class Employee. When you call a method on some_employee, Ruby follows that *class* reference and then looks for a matching method where they’re defined.

Makes sense, right? That way, instances of each class don’t need to carry copies of every instance method that they answer to. It’s a good way to save memory in the system.

So that’s all well and good for instance methods, but what about class methods?

Class Methods — A Terrible Secret

In your travels, you may have noticed that there’s a Module#instance_methods but there’s no Module#class_methods. There’s a good reason for that, but you might not be ready to hear about it.

Take a deep breath. Are you sitting down? Good. There is no such thing as a Ruby class method, only instance methods that look and smell like class methods.

When we call that class method validate_id!, Ruby will follow the same flow that we did before:

We’ll check the Employee class for an instance method. When we don’t find it, we’ll follow that pointer reference up to the Class class and look for it There. Employee is, after all, an instance of the class Class.

But wait. If we find validate_id! on the Class class, then that method should be available on every class, not just Employee. What in the blazes is going on?

Ruby Singleton Classes

You’ve probably seen a trick like this before:

We call these instance-specific method singleton methods. These methods live on an object called a singleton class, which actually lives invisibly between the object itself and its formal class. Need proof? Check this out:

Makes sense, right? If the method definition was on the Employee class, then it wouldn’t be instance-specific.

So then what is a Class Method?

A class method is a method that lives on a classes’ singleton class.

That statement deserves some unpacking, no?

When we make a call to Employee.validate_id!, we’re invoking the Employee class, which is actually an instance of Ruby’s Class class. When that Employee class is created, a singleton class is created on-the-fly that contains the method validate_id!.

Check out this example for proof:

So to summarize: class methods aren’t methods that are define at the class level of an object. They’re methods that are defined on the implicitly-created singleton class that’s built at the moment that the class is created in the system.

Finally: How does Module#extend work?

When you invoke Module#extend to import methods, you’re not bringing them into the instance. You’re not bringing them into the class. You’re actually bringing them into the singleton class that lives between that object’s class and Ruby’s Class class, which lives above it.

This isn’t vital need-to-know information for Ruby developers. It’s not even need-to-know for most advanced Rubyists. But it’s really useful to know if you’re using #extend extensively in your system and you ever need to debug funky method behavior.

And for nerds like me, it’s just cool to see how this stuff works behind the scenes.

My understanding of class method dispatch came largely from this brilliant article by Leo Hetsch. If you want to go deeper, I’d call that required reading.

--

--

Jem Zornow

Rubyist, Writer, Musician, Educator, Bread Enthusiast