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