Cover Image credit: Samier Saeed and SitePoint.

Have you ever wondered what a "singleton class" is? Have you ever been talking to someone or reading a blog post and "singleton class" or "singleton method" got used, and you just smile and nod, making a mental note to look it up later? Now is your time. Now is your moment. I'm hoping to explain this concept in more intuitive language and show you how handy it can be.

Side note: a lot of this information came from reading The Well-Grounded Rubyist by David A. Black. This book has a ton of great information and is currently one of my favorite books on Ruby.

Side note #2: At the very least, scroll down to the bottom for some cheesy and wholesome singleton-based positivity. 😁

The Code

If you've written much Ruby, you've used these "singleton classes" already without knowing it! First, I'll show you the code that you've probably already written, so you have some context.



class Config def self . from_file ( filename ) Config . new ( YAML . load_file ( filename )) end end dev_config = Config . from_file ( "config.dev.yaml" ) # => Config object with dev settings

You may have also seen it like this:



module Geometry class << self def rect_area ( length , width ) length * width end end end Geometry . rect_area ( 4 , 5 ) # => 20

Up until now, you've probably referred to these as "class methods." You are mostly right. But why do they work? What's happening here?

Individualization

This is a concept that is central to what makes Ruby so awesome. Individual objects, even of the same class, are different from each other, and they can have different methods defined on them. I'm going to shamelessly use our pets to aid in this example.



class Pet def smolder "Generic cute pet smolder" end end succulent = Pet . new momo = Pet . new willy = Pet . new def momo . smolder "sassy cat smolder" end def willy . smolder "well-meaning dingus smolder" end

Now, when we call smolder on succulent , which we haven't changed, things go as planned.



succulent . smolder # => Generic cute pet smolder"

But when we call smolder on willy or momo , something different happens!



momo . smolder # => "sassy cat smolder"





willy . smolder # => "well-meaning dingus smolder"

So, how does this work? Did we re-define smolder for each pet? Do me a favor and check out the output of the following:



succulent . singleton_methods # => [] momo . singleton_methods # => [:smolder] willy . singleton_methods # => [:smolder]

That's right! You're using a singleton method! Now, I think, we're ready to talk about what a singleton class is.

What is a Singleton Class?

First, a more general programming, less Ruby-specific question: what is a singleton? While there are various definitions that might be more specific for different cases, at its core, a singleton is just something that there is only one of. It is the only one of its kind.

What does that mean in the context of Ruby? Here it is: when you instantiate an object from a class in Ruby, it knows about the methods that its class gives it. It also knows how to look up all of the ancestors to its class. That's why inheritance works.

"Oh, my class doesn't have that method? Let's check its parent class. And that class's parent class. Etc."

One of the cool things about Ruby is that the ancestry chain is very unambiguous by design. There is a specific set of rules by which objects search their ancestors, such that there is never any doubt which method gets called.

In addition to knowing about its class, each object is created with a singleton class that it knows about. All the singleton class is is a kind of "ghost class" or, more simply, a bag to hold any methods that are defined only for this particular object. Try this out:



momo . singleton_class # => #<Class:#<Pet:0x00007fea40060220>>

In the inheritance hierarchy, it sits invisibly, just before the objects actual class. However, you can't see it by looking at the object's ancestors.



momo . class . ancestors # => [Pet, Object, Kernel, BasicObject]

But if we look at the ancestry tree for the singleton class itself:



momo . singleton_class . ancestors # => [#<Class:#<Pet:0x00007fea40060220>>, Pet, Object, Kernel, BasicObject]

You can see that it comes in right at the beginning. Thus, when momo goes to look for the smolder method, it looks first in its singleton class. Since there is a smolder method there, it calls that one, instead of looking further up the tree to the one defined in the Pet class.

What Does This Have to Do with Class Methods?

Now is when we start to see the power of the singleton class. Don't forget that every class is just an object of the class Class . If that sentence made you start to hyperventilate, don't worry. I'll explain.



Pet . class # => Class

And Class is just a class that provides some methods to every instance of it (classes) you create, just like any other class.



Class . instance_methods ( false ) # => [:new, :allocate, :superclass]

So, really, when you're defining "class methods" that you plan to call directly on the class, what you're actually doing is defining methods on that particular Class object — in its singleton class!



class Pet def self . random %w{cat dog bird fish banana} . sample end end Pet . singleton_methods # => [:random]

And… if the singleton class exists, it becomes the parent class to singleton_classes of classes that inherit from the main class. An example should help.



class Pet def self . random %w{cat dog bird fish banana} . sample end end class Reptile < Pet def self . types %w{lizard snake other} end end Reptile . singleton_methods # => [:types, :random] Reptile . singleton_class . ancestors # => [#<Class:Reptile>, #<Class:Pet>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]

See how Reptile 's singleton class inherits from Pet 's singleton class, and thus the "class methods" available to Pet are also available to Reptile ?

Other Tidbits

I think so far, we've pretty much covered all of the important bits. There are, however, a couple more interesting details that I thought were cool that are sort of tangentially related: the somewhat hard to decipher class << self syntax, the different ways of creating class methods, and the use of extend . Feel free to read on if you're interested.

Class << self

There are actually two ways to use the class keyword: directly followed by a constant (a la class Gelato ), or followed by the "shovel operator" and an object (a la class << momo ). You already know about the first one — it's the way you usually declare a class! Let's focus on the second one, which is the syntax to directly open up an object's singleton class. You can think about it as essentially the same as defining methods like we were doing above.

What I mean is this:



# This: def momo . snug "*snug*" end # is the same (pretty much) as this: class << momo def snug "*snug*" end end

You do this all the time when you re-open regular classes to add more functionality.



class Gelato attr_reader :solidity def initialize @solidity = 100 end def melt @solidity -= 10 end end # And re-open it to add one more method class Gelato def refreeze @solidity = 100 end end dessert = Gelato . new 5 . times { dessert . melt } dessert . solidity # => 50 dessert . refreeze # => 100

The syntax class << object; end is just another way of re-opening the object's singleton class. The benefit here is that you can define constants and multiple methods all at once instead of one at a time.



# Instead of: def momo . pounce "pounce!" end def momo . hiss "HISS" end def momo . lives 9 end # We can do class << momo def pounce "pounce!" end def hiss "HISS" end def lives 9 end end momo . singleton_methods # => [:pounce, :hiss, :lives, :smolder]

It's a common pattern when adding multiple class methods to a class to see the following:



class Pet class << self def random %w{cat dog bird fish banana} . sample end end end # Which, since "self" is inside of the class # declaration, means that 'self == Pet', so you could # also do this: class Pet class << Pet def random # ... end end end

Maybe you've seen this pattern and not known what it is, or maybe you knew it adds class methods but didn't know what why. Now you know! It's all thanks to the singleton class!

class << self vs def self.method vs def Pet.method

There are a few different ways to declare class methods.



# 1. In global scope def Pet . random %w{cat dog bird fish banana} . sample end # 2. Inside the class definition, using 'self' class Pet def self . random %w{cat dog bird fish banana} . sample end end # 3. Inside the class definition, using the shovel class Pet class << self def random %w{cat dog bird fish banana} . sample end end end # 4. Outside the class definition, using the shovel class << Pet def random %w{cat dog bird fish banana} . sample end end

So what's the difference?? When do you use one or the other?

The good news is that they're all basically the same. You can use whichever one makes you the happiest and matches the style of your codebase. The only difference is with #3, and how it deals with constants and scope.



MAX_PETS = 3 def Pet . outer_max_pets MAX_PETS end class Pet MAX_PETS = 1000 class << self def inner_max_pets MAX_PETS end end end Pet . outer_max_pets # => 3 Pet . inner_max_pets # => 1000

See that the inner_max_pets function has access to the scope inside the Pet class and the constants there? That's really the only difference. Feel free to carry on using your favorite syntax with confidence.

Using Extend to Safely Modify Built-In Classes

Hopefully, at some point, you've read a blog post or had someone warn you about the dangers of re-opening built-in Ruby classes. Doing something like the following should be done veeeery carefully.



class String def verbify self + "ify" end end "banana" . verbify # => "bananaify"

The dangers include accidentally overwriting built-in methods, having methods clash with other libraries in the same project, and generally making things not behave as expected. The extend keyword can help with all of that!

What is Extend?

The extend keyword is a lot like include in that it allows you to load functionality into your class/module from other classes/modules. The difference, however, is that extend puts these methods onto the target object's singleton class.



module Wigglable def wiggle "*shimmy*" end end willy . extend ( Wiggleable ) willy . singleton_methods # => [:wiggle, :smolder]

Thus, if you use extend inside a class definition instead of include , the methods will get added to the class's singleton class as class methods instead of being added to the class itself as instance methods.



module Hissy def hiss "HISS" end end class Reptile extend Hissy end snek = Reptile . new snek . hiss # => Error! Undefined method hiss for 'snek' Reptile . hiss # => "HISS"

How Does That Help Us?

So, let's say that we really needed to have that verbify method on the strings we were using. While you could create and use a subclass of String , another option would be to extend individual strings!



module Verby def verbify self + "ify" end end noun = "pup" noun . extend ( Verby ) noun . verbify # => "pupify"

Cheesy Wrap Up

So remember, singletons aren't just an intimidating-sounding but not-super-complicated Ruby topic. You are the real singleton — yes, you're a human, but there's nobody else quite like you. You've got class and your own methods of doing things, and that's valuable. And now we've just added a little more functionality to you.

