Breaking the Singleton: How to Reload Ruby Singleton Instance

… and check why 5600+ Rails engineers read also this

Breaking the Singleton: How to Reload Ruby Singleton Instance

As you may know, the Singleton module implements the singleton pattern in Ruby. Technically it ensures that the class that includes the Singleton module will have one and only one instance throughout the application’s lifecycle available with the class method instance. The most common usage is for some configuration objects, logging or some global third-party clients. What Ruby Singleton effectively does is that it hides new and allocate methods on the class level so you can’t create a new instance and undefines the extend_object method of your class. It also raises an exception when you try to clone an instance using clone or dup method. On the first call of the instance method the singleton instance will be created and stored internally for the whole application’s lifecycle.

But what if you actually need to re-instantiate an instance of a specific singleton?

A few days ago I was involved in migrating multiple client libraries that were using singleton pattern to a new internal implementation in a Rails application. The original implementation was implementing some logic for the target URL generation that was using YAML files that were storing some per-env configuration. It was good enough for running the code in a specific environment but I wanted to be 100% sure that my existing production configuration wouldn’t break after the migration.

The initial idea was simple - let’s generate target URLs on production for every client that needs to be migrated and then write a test case that will change the Rails environment to return production in a test and will check whether client URLs are correct after changing the internal implementation. In theory this should work and indeed, it was working when I was running the test in isolation. But then I realised that when I run the whole test suite, the first test that is using an instance of my singleton class will set its state for the whole test suite. This means that my test will pass only if it’s called before any other test that is using any of client instances and - even worse - it will return instances with production-like state for all test cases that are executed after my client test. I really needed to re-instantiate my singleton classes or I would need to run a separate test suite where I run only my single test for the migration.

I started from inspecting the Ruby singleton sources and shortly thereafter I found the undocumented __init__ method that does exactly what I needed - it resets the singleton class state by removing the instance (setting it to nil) and creating a new mutex for thread-safety. So the next time you call the instance method of your singleton class, it will create a new instance. Now I only needed to setup the stage in my test so that I stub the Rails env and reset singleton instance before my test and remember to remove the env stub and reset singleton instances again after the test is run:

RSpec.describe "Clients migration" do
  before { setup_env('production') }
  after { setup_env('test') }

  it 'generates the correct URL for client instance' do
    # ...
  end

  def setup_env(env)
    Rails.env = env
    Singleton.__init__(MyClient)
    Singleton.__init__(AnotherClient)
    # ...
  end
end

This way I managed to safely migrate my internal implementation and test that it will behave exactly in the same way as the previous one.

Cloning a singleton class

Singleton module adds also the clone method on class level that is calling __init__ but it has a slightly different behaviour as it returns a new anonymous singleton class with a fresh state rather than resetting the existing one:

class Timer
  include Singleton
  attr_reader :timestamp

  def initialize
    @timestamp = Time.now
  end
end

Timer.instance.timestamp #=> 2025-07-04 09:30:36.98988 +0200
Timer.clone.instance.timestamp #=> 2025-07-04 09:30:57.770478 +0200
Timer.instance.timestamp #=> 2025-07-04 09:30:36.98988 +0200
Singleton.__init__(Timer).instance.timestamp #=> 2025-07-04 09:31:42.419874 +0200
Timer.instance.timestamp #=> 2025-07-04 09:31:42.419874 +0200

From my experience the Singleton pattern is not used very often but if you are using it and find a use case where you need to reset the instance for any reason, using Singeton::__init__ may help you do so.

You might also like