Mastering the Singleton Pattern in Ruby: A Complete Guide

The Singleton pattern ensures that only one instance of a class exists in a program and provides a global access point to that instance. In Ruby, the Singleton module is available in the singleton library, making it easy to implement.

Using the Singleton Module

Ruby provides a built-in Singleton module that ensures only one instance of a class is created.

Example: Implementing Singleton Class

require 'singleton'

class Logger
include Singleton

def log(message)
puts "[LOG]: #{message}"
end
end

# Getting the single instance
logger1 = Logger.instance
logger2 = Logger.instance

logger1.log("Application started")

# Checking if both instances are the same
puts logger1.object_id == logger2.object_id # true

Explanation:

  • include Singleton ensures that only one instance of Logger can be created.
  • .instance method provides the singleton instance.
  • Direct instantiation is prevented (Logger.new will raise an error).

Creating a Singleton Without the Singleton Module

If you prefer not to use the Singleton module, you can manually implement the Singleton pattern.

Example: Manual Singleton Implementation

class Configuration
@instance = nil

private_class_method :new # Prevents direct instantiation

def self.instance
@instance ||= new
end

def setting
"Dark Mode Enabled"
end
end

# Getting the singleton instance
config1 = Configuration.instance
config2 = Configuration.instance

puts config1.setting # "Dark Mode Enabled"
puts config1.object_id == config2.object_id # true

How It Works:

  • @instance ||= new ensures that only one instance is created.
  • private_class_method :new prevents direct instantiation.
  • Only Configuration.instance can access the instance.

When to Use Singleton in Ruby?

Use a Singleton when: ✅ You need a single shared resource (e.g., configuration, logging, database connection).
✅ You want to control access to an object, ensuring only one instance exists.
✅ You need global access to an instance without passing it around manually.

Common Use Cases

  • Logger Service
  • Application Configuration
  • Cache Storage
  • Database Connection Management

Singleton vs. Global Variables

FeatureSingleton PatternGlobal Variable ($var)
EncapsulationYes (Encapsulated in class)No (Accessible everywhere)
Control Over InstantiationYes (Only one instance allowed)No (Can be reassigned anytime)
Thread SafetySafer with Singleton ModuleRisky (Mutable state)

Using a Singleton is generally safer than using global variables, as it provides better encapsulation and prevents accidental modifications.

Thread-Safe Singleton

The built-in Singleton module ensures thread safety in Ruby. However, if implementing manually, you need to make it thread-safe.

Example: Thread-Safe Singleton

require 'thread'

class SafeSingleton
@instance = nil
@mutex = Mutex.new

private_class_method :new

def self.instance
@mutex.synchronize do
@instance ||= new
end
end
end

How It Works:

  • @mutex.synchronize ensures that only one thread initializes the instance.
  • This prevents race conditions in multi-threaded applications.

Singleton with Lazy Initialization

In some cases, you might want to initialize the singleton only when needed.

Example: Lazy Singleton

class LazyLoader
@instance = nil

def self.instance
@instance ||= new
end

def load_data
puts "Loading data..."
end
end

# Instance is created only when first used
loader = LazyLoader.instance
loader.load_data

Why Use Lazy Initialization?

  • Saves memory by not creating the instance until needed.
  • Useful for performance optimization in applications.

Database Connection Pooling

Most web frameworks, like Ruby on Rails, Django, or Spring Boot, use the Singleton pattern for managing database connections.

How It Works:

  • A single instance of a connection pool is created and shared across the application.
  • The connection pool is responsible for managing database connections, ensuring they are reused rather than recreated, which reduces overhead.
  • Ruby on Rails uses the ActiveRecord::Base.connection singleton to manage and provide database connections.
# Example in Rails
ActiveRecord::Base.connection.execute("SELECT * FROM users")
  • The ActiveRecord::Base.connection is a Singleton, ensuring all parts of the application share the same connection object for efficiency.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top