Ruby Singleton

last modified: March 12, 2010
require 'singleton'
class Aardvark
  include Singleton
end

That's it. Mixing in the Singleton module makes the "new" method private and creates an "instance" method that returns the sole instance of Aardvark. (So you'd access the Aardvark by saying "Aardvark.instance".) It also takes care of thread safety.

For educational purposes, this is how you would implement it without the use of the standard library:

class Aardvark
          private_class_method :new
          @@instance = new
          def Aardvark.instance
            @@instance
          end
end

Or, if you want it to be lazy-loading:

class Aardvark
          private_class_method :new
          def Aardvark.instance
            @@instance = new unless @@instance
            @@instance
          end
end

The following is an implementation of a module (MixIn) that will turn a class into a thread-safe, lazy-loading singleton.

module ThreadSafeSingleton
  def self.append_features(clazz)
    require 'thread'
    clazz.module_eval { 
      private_class_method :new
      @instance_mutex = Mutex.new
      def self.instance 
        @instance_mutex.synchronize {
          @instance = new unless @instance
          @instance
        },
      end
    },
  end
end

 # For further educational enjoyment, the following provides a test for
 # thread-safeness and a contrasting non-thread-safe implementation

require 'test/unit'

module SimpleSingleton
  def self.append_features(clazz)
    clazz.module_eval { 
      private_class_method :new
      def self.instance 
        @instance = new unless @instance
        @instance
      end
    },
  end
end
class SimpleSingletonClass
  include SimpleSingleton
  def initialize
    sleep 1
  end
end

class ThreadSafeSingletonClass
  include ThreadSafeSingleton
  def initialize
    sleep 1
  end
end

class TestSingleton < Test::Unit::TestCase
  def testSimpleIsNotThreadSafe
    threads = Array.new(101) { Thread.new { SimpleSingletonClass.instance },},
    assert(threads.collect { |t| t.value },.uniq.size > 1)
  end
  def testThreadSafeIsThreadSafe
    threads = Array.new(101) { Thread.new { ThreadSafeSingletonClass.instance },},
    assert_equal(1, threads.collect { |t| t.value },.uniq.size)
  end
end

Compare with PerlSingleton and PythonSingleton


CategoryRuby


Loading...