All original content is created in Ukrainian. Not all content has been translated yet. Some posts may only be available in Ukrainian.Learn more

Singleton class (eigenclass) in Ruby: what it is and why it is needed

Post cover: Singleton class (eigenclass) in Ruby: what it is and why it is needed
Table of contentsClick link to navigate to the desired location
This content has been automatically translated from Ukrainian.
In Ruby everything is an object (well, almost everything), even classes. And each object can have its own unique methods that do not belong to other instances of the same class.
What Proc and Lambda are can be read in my previous post.
These methods are stored not in a regular class, but in a special hidden class called singleton class or eigenclass.

What is a singleton class?

A singleton class is the "personal" class of a specific object. This is where Ruby stores methods that are defined only for that object.
user1 = "Alice"
user2 = "Bob"

def user1.greet
  "Hello, I'm #{self}!"
end

p user1.greet  # => "Hello, I'm Alice!"
p user2.greet  # => NoMethodError
Here greet is a method that exists only for the object user1.
Ruby created its own singleton class for user1 and placed this method there.

How to see singleton_class?

Every object has a singleton_class method that allows you to access this "hidden" class:
user1.singleton_class
# => #<Class:#<String:0x0000000103c4b8f0>>
If you define a method directly in this class, it will also become unique:
user = "Ruby"

user.singleton_class.class_eval do
  def version
    "3.2.1"
  end
end

p user.version     # => "3.2.1"

class << self

When you write class << self, you are actually opening the singleton class of the current object. This is a convenient syntax for defining methods inside it.
Example 1: in a regular object
user = "John"

class << user
  def greet
    "Hi, I'm #{self}"
  end
end

p user.greet  # => "Hi, I'm John"
Example 2: in a class (defining class methods)
class Person
  class << self
    def species
      "Human"
    end
  end
end

p Person.species  # => "Human"
Ruby simply opened the singleton class for the class Person and added the species method there.
In short:
  • In Ruby every object has its own singleton class - a hidden class for its unique methods.
  • The method def obj.method or class << obj adds a method to the singleton class of that object.
  • You often use this unconsciously when writing class methods (class << self) or unique methods for a single object.
But to solidify the understanding, let's consider a question. Can you call a class singleton method on its object? NO. It will not be available on the class object (i.e., on the instance).
In the example:
class Person
  class << self
    def species
      "Human"
    end
  end
end
Ruby creates a class method, that is, a method that lives in the singleton class of the class Person, not in Person’s instance methods.
Therefore:
Person.species     # => "Human"
Person.new.species # => NoMethodError
Explanation:
  • Person is an object of class Class.
  • class << self opens the singleton class for this object (Person).
  • The species method is stored there.
  • Instances of Person.new (created objects of the class) do not see it because they look for methods in the regular class Person, not in its singleton class.
If you want the method to be available both as a class method and as an instance method, you can simply define it twice or extract it into a module:
class Person
  def self.species
    "Human"
  end

  def species
    "Human"
  end
end

Person.species     # => "Human"
Person.new.species # => "Human"
Or more elegantly - through a module:
module Species
  def species
    "Human"
  end
end

class Person
  extend Species   # adds method as class method
  include Species  # adds method as instance method
end

This post doesn't have any additions from the author yet.

How arrays work in Ruby: practical examples of each, map, select, inject, reduce, filter_map
28 Oct 10:38

How arrays work in Ruby: practical examples of each, map, select, inject, reduce, filter_map

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
How self, protected, and private work (Ruby)
28 Oct 13:52

How self, protected, and private work (Ruby)

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Integer division in Ruby: why 6 / 4 equals 1
28 Oct 14:10

Integer division in Ruby: why 6 / 4 equals 1

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
28 Oct 14:42

How &:to_s works in Ruby and what Symbol#to_proc is

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
What are Proc and Lambda in Ruby?
28 Oct 15:57

What are Proc and Lambda in Ruby?

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
What happens if you call [1, 2, 3].map(&Person)
29 Oct 17:54

What happens if you call [1, 2, 3].map(&Person)

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
==, equal?, eql?, === in Ruby: what they check and when to use them
29 Oct 20:47

==, equal?, eql?, === in Ruby: what they check and when to use them

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Include, Extend, Prepend in Ruby: how they work and what the difference is
29 Oct 21:20

Include, Extend, Prepend in Ruby: how they work and what the difference is

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
module_function in Ruby: when module methods are available as module methods and as functions
29 Oct 21:53

module_function in Ruby: when module methods are available as module methods and as functions

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
What is memoization in Ruby?
30 Oct 10:17

What is memoization in Ruby?

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
is_a?, kind_of?, instance_of? — how does Ruby check the type of an object?
30 Oct 19:55

is_a?, kind_of?, instance_of? — how does Ruby check the type of an object?

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
&& vs and — the difference in Ruby that can break your code
30 Oct 20:23

&& vs and — the difference in Ruby that can break your code

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska