understanding-the-singleton-class.jpg

What is the Singleton Class in Ruby?

December 04, 2020 4 min read

The singleton class, also referred to as the metaclass or the eigenclass. What is it exactly?

Let’s begin by discussing the main purpose of metaclasses in Ruby.

Classes and Method Dispatching

In Ruby, everything is an object, and every object has a class, which defines the methods the object can respond to.

These two statements also apply to classes, which is what makes it possible for classes in Ruby to have their own instance variables and to receive methods.

class Animal
  @description = 'A multicellular eukaryotic organism.'

  def self.description
    @description
  end
end

Animal.description # => "A multicellular eukaryotic organism."

In contrast with other languages, in Ruby what we usually refer to as class methods are simply instance methods of a class object.

Now, if a class in Ruby is an object, and every object has a class which defines its methods, then a class in Ruby has a class which defines its methods.

Animal.class # => Class

If Class defined those methods, then they would be available for any class object.

Integer.description # NoMethodError (undefined method `description' for Integer:Class)

How does Ruby pull it off then?

The Metaclass of a Class

Ruby deals with it by giving every object its own unique class, which defines the methods available for that object. Its very own metaclass.

The metaclass is called the singleton class because there is one single instance of it.

Animal.singleton_class # => #<Class:Animal>

This class is hidden in the inheritance chain (it doesn’t show up when calling ancestors, and class returns Class), but we can think of it as the first ancestor when it comes to dispatching methods.

When calling a method on an object, Ruby will perform the method lookup by first checking on the object’s metaclass, before traversing the rest of the method chain.

Animal.singleton_methods # => [:description]

Just like instance methods are defined in a class, class methods are defined in the metaclass of a class object.

This design enables Ruby to share the same dispatch mechanism for class methods: internally all methods are instance methods.

The Metaclass Hierarchy

In the diagram below we can see that each class has a corresponding metaclass, which is where their respective class methods are defined.

Diagram of the Metaclass Hierarchy

The inheritance chain of metaclasses is what allows to inherit and override class methods and call super.

And that is the main purpose of metaclasses in Ruby.

The Metaclass of an Object

Notice how the instance also has its own metaclass: classes are just a particular kind of object. When we call a method on an instance, Ruby will also look in its metaclass first.

Let’s see a few examples of how we can use the metaclass to define methods that are specific to a particular instance.

animal = Animal.new
other_animal = Animal.new

class << animal
  def bark
    'Woof'
  end
end

def animal.greet
  'hi'
end

animal.define_singleton_method(:roar) { 'Rawr!' }

animal.bark  # => "Woof"
animal.greet # => "Hi"
animal.roar  # => "Rawr!"

animal.singleton_methods # => [:bark, :roar, :greet]
animal.singleton_class.instance_methods(false) # => [:bark, :roar, :greet]

other_animal.bark # NoMethodError (undefined method `bark' for #<Animal>)
other_animal.singleton_methods # => []

You will certainly recognize the first two patterns, they use the same syntax that is typically used with self to define class methods!

Once again, classes are just a particular kind of object. And yet, unlike classes, modifying the behavior of a particular object is rarely used in practice.

In the next post I share a few useful applications of this feature, read on!

by Máximo Mussini

Practical Applications of the Singleton Class in Ruby

In the previous post, we discussed how the singleton class powers class methods in Ruby, and how every object instance has its own singleton class.

In this post, we will cover a few practical usages of the singleton class as a way to modify the behavior of a particular object.

Adding Methods and Mixins

Since the singleton class of an object is specific to it, we can add methods to it, remove me…