DEV Community

Cover image for Explaining Ruby's Singleton Class (Eigenclass) to confused beginners
Samuel-Zacharie FAURE
Samuel-Zacharie FAURE

Posted on • Edited on

Explaining Ruby's Singleton Class (Eigenclass) to confused beginners

Original of this post on my blog

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 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 
Enter fullscreen mode Exit fullscreen mode

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 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 :new method. 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 
Enter fullscreen mode Exit fullscreen mode

This is useful if you ever want to restrict a class so it never creates more than one instance of itself.

The Eigenclass

When you create an instance of a class, Ruby creates a hidden class, basically a copy of the original class, but which 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' 
Enter fullscreen mode Exit fullscreen mode

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' 
Enter fullscreen mode Exit fullscreen mode

As you can see, the class << object2 syntax is used to access the Eigenclass of object2.

Now for the neat trick: you might know that, in Ruby, classes are objects too. In Ruby. Everything is an object !

And the 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 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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 class_print_hello puts 'Hello' end end end ExampleObject.class_print_hello # => 'Hello' # or outside class << ExampleObject def class_print_hello puts 'Sayonara' end end ExampleObject.class_print_hello # => 'Sayonara' 
Enter fullscreen mode Exit fullscreen mode

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 Class.

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' 
Enter fullscreen mode Exit fullscreen mode

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.printHello 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' 
Enter fullscreen mode Exit fullscreen mode

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 !

Top comments (5)

Collapse
 
pabuisson profile image
Pierre-Adrien Buisson • Edited

hey hello Samuel, super interesting article, thanks for writing this! not the easiest concept in ruby and a pretty confusing one πŸ™‚

Only one tiny detail bothered me: in Ruby, the convention is to use snake_case case for methods and variables, and seeing these camelCase method names really hurts my rubyist brain. It's really a minor thing though but maybe that can be useful, I hope you won't mind me saying ☺️

Thanks again for this great post, keep'em coming πŸ’ͺ

Collapse
 
samuelfaure profile image
Samuel-Zacharie FAURE

Oh indeed, I must have been working on javascript while writing this! I'll correct it soon!

Collapse
 
zenya profile image
Eugene

Hey Samuel!

This is without exaggeration the best explanation I have ever come across about Eigenclass!

Thanks for your efforts!!

Collapse
 
samuelfaure profile image
Samuel-Zacharie FAURE

You're welcome :)

Collapse
 
alexdesi profile image
Alessandro De Simone

Nice article!
Just check the comment on the class MySingleton, it's the same as class NotSingleton.