Singleton Class Exec
Ruby’s “singleton classes” are like dark matter: they are everywhere, and they explain why things work the way they do. But they are, for the most part, invisible. Which makes them hard to understand.
In this episode we’re going pull the curtain back on singleton classes. You’ll learn how to see these elusive entities hovering in the background of Ruby’s object model, and relate them to the language features you use every day.
Video transcript & code
In episode #453 we met Cletus, the Dancing Cow.
And we discovered that when we defined the dance
method on the single Cow
object named cletus
, Ruby was actually storing this method definition in a special object-specific class called a "singleton class".
class Cow
def speak
"moo"
end
end
cletus = Cow.new
def cletus.dance
"tappety tappety tap"
end
cletus.dance
# => "tappety tappety tap"
cletus.singleton_class
# => #<Class:#<Cow:0x0055c2fa1f8160>>
cletus.singleton_class.instance_methods(false)
# => [:dance]
Later on, in episode #454, we discovered that so-called class methods in Ruby are also just another example of singleton methods.
When we define a plural_term
class method, , it turns out it's really just another instance method. An instance method defined on the singleton class of the Cow
class object.
class Cow
def self.plural_term
"herd"
end
def speak
"moo"
end
end
Cow.singleton_class
# => #<Class:Cow>
Cow.singleton_class.instance_methods(false)
# => [:plural_term]
Now, I've always found singleton classes to be one of the most difficult Ruby concepts to wrap my brain around. And I know a lot of other folks have the same problem.
And I think that maybe the biggest reason for this difficulty is simply that singleton classes are invisible most of the time. Unless we specifically go looking for them, as we have here.
Let's change that. Let's get rid of our our current definition of the dance
method.
Now let's rewrite it. This time, we'll start by asking for the singleton_class
corresponding to the cletus
object.
Then we tell that class, we want to execute some code inside its context, using class_exec
.
And inside the class_exec
block, we define an ordinary instance method called dance
.
The outcome of this definition is exactly the same as before.
We can tell cletus
to dance, and he does.
And we can see that this dance
method is in fact defined in the object's singleton class.
class Cow
def self.plural_term
"herd"
end
def speak
"moo"
end
end
cletus = Cow.new
cletus.singleton_class.class_exec do
def dance
"tappety tappety tap"
end
end
cletus.dance
# => "tappety tappety tap"
cletus.singleton_class.instance_methods(false)
# => [:dance]
But this time, this should come as less of a surprise. Partly because we've already learned about it. But also, because this time we explicitly defined the method inside the singleton class.
Now, Ruby hasn't always had a method for getting at the singleton_class
. In fact, it hasn't always had a class_exec
method either.
But even before either of these methods existed, Ruby had a way of explicitly modifying an object's singleton class. And it looked like this.
class << cletus
This special variation on class definition syntax opens up an objects singleton class and allows us to add or modify the methods defined there. In this case, we're adding a sing
method.
The outcome is the same as our class_exec
version. Cletus is now a singing and dancing cow. And we can see both methods are stored in the singleton class.
class Cow
def self.plural_term
"herd"
end
def speak
"moo"
end
end
cletus = Cow.new
cletus.singleton_class.class_exec do
def dance
"tappety tappety tap"
end
end
class << cletus
def sing
"ga ga moo-la-la!"
end
end
cletus.dance
# => "tappety tappety tap"
cletus.sing
# => "ga ga moo-la-la!"
cletus.singleton_class.instance_methods(false)
# => [:dance, :sing]
So next time you see per-object methods defined using the "def object dot method" syntax, you can realize exactly what this really is: a shorthand for defining instance methods in a singleton class.
class Cow
def self.plural_term
"herd"
end
def speak
"moo"
end
end
cletus = Cow.new
def cletus.dance
"tappety tappety tap"
end
def cletus.sing
"ga ga moo-la-la!"
end
And you can even rewrite it in your head to one of these more explicit versions.
class Cow
def self.plural_term
"herd"
end
def speak
"moo"
end
end
cletus = Cow.new
cletus.singleton_class.class_exec do
def dance
"tappety tappety tap"
end
end
class << cletus
def sing
"ga ga moo-la-la!"
end
end
And the same goes for class object singleton methods, otherwise known as "class methods".
class Cow
def self.plural_term
"herd"
end
def speak
"moo"
end
end
You can mentally rewrite this to a version that explicitly acknowledges the existence of the singleton class.
class Cow
singleton_class.class_exec do
def plural_term
"herd"
end
end
def speak
"moo"
end
end
Once you start seeing singleton classes this way, you'll realize that they are everywhere. And that they aren't as mysterious as they first appeared. Happy hacking!
Responses