Explaining Ruby's Singleton Class (Eigenclass) to confused beginners
I got really confused when I started to look up exactly what was the Eigenclass. This confusion was not because of any complexity, but rather because of bad bits of explanations there and there, and no articles to describe the big picture.
So my goal here is to help with that by explaining exactly what is Ruby's Eigenclass and why it's called "The Singleton Class".
To understand the Eigenclass, you just need to know the very basic concepts of Class and Instances. A reminder:
- Classes are what we use to define our objects
- Instances of classes are the objects created by our classes
class MyClass # Body of class end MyClass # => class MyClass.new # => Instance of MyClass
What's the Eigenclass ?
Here's the bit of information I had the most trouble to find: the Eigenclass is called the Singleton class because it is a Class that follows the Singleton pattern.
The Singleton pattern
The Singleton pattern is simply an object-oriented programming pattern where you make sure to only have 1 and only 1 instance of some class.
Ruby implement the Singleton pattern with a module: just write
Include Singleton in your class definition and you're good to go.
This module basically hides the
MySingletonObject.new will always ERROR. Instead, it will give you an
instance method that will always return the same unique instance of your class.
require 'singleton' class NotSingleton # 'initialize' is called everytime an instance is created def initialize puts 'This will be printed many times' end end class MySingleton include Singleton # 'initialize' is called everytime an instance is created def initialize puts 'This will be printed once' end end NotSingleton.new # => 'This will be printed many times' NotSingleton.new # => 'This will be printed many times' NotSingleton.new # => 'This will be printed many times' MySingleton.instance # => 'This will be printed once' MySingleton.instance # Nothing is printed MySingleton.instance # Nothing is printed
This is useful if you ever want to restrict a class so it never creates more than one instance of itself.
When you create an instance of a class, Ruby creates a hidden class, basically a copy of the original class, but that is owned exclusively by this instance. This is the Eigenclass. If you modify the Eigenclass of your first instance, it won't change anything for another instance.
# This class is NOT a singleton class ExampleObject def print_hello puts 'Hello' end end object1 = ExampleObject.new object2 = ExampleObject.new object1.print_hello # => 'Hello' object2.print_hello # => 'Hello' def object2.print_hello puts 'Bonjour' end object1.print_hello # => 'Hello' object2.print_hello # => 'Bonjour'
There we go !
object2 is not defined in the scope of the class ExampleObject, it is defined by a copy of its class that it carries around. So by redefining a method in
object2, we "open the Eigenclass" and modify properties just for this object.
Since the Eigenclass can exist only in one instance, it is sometimes called the Singleton class, althought ExampleObject is not a Singleton at all. Only the Eigenclasses of its instances are Singletons, because they each are one and unique.
The class << self notation
The previous example can be rewritten this way:
class ExampleObject def print_hello puts 'Hello' end end object1 = ExampleObject.new object2 = ExampleObject.new class << object2 def print_hello puts 'Aloha' end end object1.print_hello # => 'Hello' object2.print_hello # => 'Aloha'
As you can see, the
class << object2 syntax is used to access the Eigenclass of
Now for the neat trick: you might know that, in Ruby, classes are objects too. In Ruby. Everything is an object !
Class of any class is always, well,
Class. I love this about Ruby: it seems to not make any sense, but it actually does, in its own way.
You can try it yourself:
# We can create ExampleObject like this : class ExampleObject end # Or like this : ExampleObject = Class.new ExampleObject.new.class # => ExampleObject ExampleObject.class # => Class Class.class # => Class ExampleObject.class.class.class.class # => Class
So, we just saw that the class
ExampleObject is an object too, and more precisely, it is an instance of the class
Class. This must means that it also have its own Eigenclass !
And if the Eigenclass of an instance of your class 'ExampleObject' is somewhat of a copy of the class 'ExampleObject', then the Eigenclass of the class 'ExampleObject' must be somewhat of a copy of the class 'Class'.
# Instance of class ExampleObject ExampleObject.new # => Eigenclass : a copy of ExampleObject # Instance of class Class ExampleObject # => Eigenclass : a copy of Class
Now, let's say we want to access the Eigenclass of the ExampleObject class.
We can do it this way:
class ExampleObject # Either inside the definition of the class class << self def print_hello puts 'Hello' end end end ExampleObject.print_hello # => 'Hello' # or outside class << ExampleObject def print_hello puts 'Sayonara' end end ExampleObject.print_hello # => 'Sayonara'
As you see here, we don't call the method on an instance of the class (ExampleObject.new) but rather on the class itself (ExampleObject). It's okay though, because ExampleObject is itself an instance of
If you're using Class methods, you're really using the Eigenclass
If you do some Rails, you'll often have in your models:
class MyModel def self.print_hello puts 'Hello' end end MyModel.print_hello # => 'Hello'
As you can see, defining a method on the
self object inside a class definition is exactly the same thing as opening the Eigenclass with
class << self.
self.print_hello is also called a Class method, to differenciate with an Instance method. See the following:
class MyModel # Class Method def self.print_hello puts 'Hello' end # Instance Method def print_bonjour puts 'Bonjour' end end MyModel.print_hello # => 'Hello' MyModel.new.print_hello # => ERROR MyModel.print_bonjour # => ERROR MyModel.new.print_bonjour # => 'Bonjour'
So as you see, the Class method of your class is actually just an Instance method of the Eigenclass of your class.
Don't hesitate and repeat that last sentence to your company's interns: they'll think you're a Ruby genius !