123456789_123456789_123456789_123456789_123456789_

Module: Singleton

Relationships & Source Files
Namespace Children
Modules:
Defined in: lib/singleton.rb

Overview

The Singleton module implements the Singleton pattern.

Usage

To use Singleton, include the module in your class.

class Klass
   include Singleton
   # ...
end

This ensures that only one instance of Klass can be created.

a,b  = Klass.instance, Klass.instance

a == b
# => true

Klass.new
# => NoMethodError - new is private ...

The instance is created at upon the first call of Klass.instance().

class OtherKlass
  include Singleton
  # ...
end

ObjectSpace.each_object(OtherKlass){}
# => 0

OtherKlass.instance
ObjectSpace.each_object(OtherKlass){}
# => 1

This behavior is preserved under inheritance and cloning.

Implementation

This above is achieved by:

  • Making Klass.new and Klass.allocate private.

  • Overriding Klass.inherited(sub_klass) and Klass.clone() to ensure that the Singleton properties are kept when inherited and cloned.

  • Providing the Klass.instance() method that returns the same object each time it is called.

  • Overriding Klass._load(str) to call Klass.instance().

  • Overriding Klass#clone and Klass#dup to raise TypeErrors to prevent cloning or duping.

Singleton and Marshal

By default Singleton's #_dump(depth) returns the empty string. Marshalling by default will strip state information, e.g. instance variables and taint state, from the instance. Classes using Singleton can provide custom _load(str) and _dump(depth) methods to retain some of the previous state of the instance.

require 'singleton'

class Example
  include Singleton
  attr_accessor :keep, :strip
  def _dump(depth)
    # this strips the @strip information from the instance
    Marshal.dump(@keep, depth)
  end

  def self._load(str)
    instance.keep = Marshal.load(str)
    instance
  end
end

a = Example.instance
a.keep = "keep this"
a.strip = "get rid of this"
a.taint

stored_state = Marshal.dump(a)

a.keep = nil
a.strip = nil
b = Marshal.load(stored_state)
p a == b  #  => true
p a.keep  #  => "keep this"
p a.strip #  => nil

Class Method Summary

Instance Method Summary

  • #_dump(depth = -1))

    By default, do not retain any state when marshalling.

  • #clone

    Raises a TypeError to prevent cloning.

  • #dup

    Raises a TypeError to prevent duping.

Class Method Details

.__init__(klass)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/singleton.rb', line 132

def __init__(klass) # :nodoc:
  klass.instance_eval {
    @singleton__instance__ = nil
    @singleton__mutex__ = Thread::Mutex.new
  }
  def klass.instance # :nodoc:
    return @singleton__instance__ if @singleton__instance__
    @singleton__mutex__.synchronize {
      return @singleton__instance__ if @singleton__instance__
      @singleton__instance__ = new()
    }
    @singleton__instance__
  end
  klass
end

._load

By default calls instance(). Override to retain singleton state.

[ GitHub ]

  
# File 'lib/singleton.rb', line 169

RDoc directive :singleton-method: _load

.append_features(mod) (private)

[ GitHub ]

  
# File 'lib/singleton.rb', line 153

def append_features(mod)
  #  help out people counting on transitive mixins
  unless mod.instance_of?(Class)
    raise TypeError, "Inclusion of the OO-Singleton module in module #{mod}"
  end
  super
end

.included(klass) (private)

[ GitHub ]

  
# File 'lib/singleton.rb', line 161

def included(klass)
  super
  klass.private_class_method :new, :allocate
  klass.extend SingletonClassMethods
  Singleton.__init__(klass)
end

Instance Method Details

#_dump(depth = -1))

By default, do not retain any state when marshalling.

[ GitHub ]

  
# File 'lib/singleton.rb', line 108

def _dump(depth = -1)
  ''
end

#clone

Raises a TypeError to prevent cloning.

Raises:

  • (TypeError)
[ GitHub ]

  
# File 'lib/singleton.rb', line 98

def clone
  raise TypeError, "can't clone instance of singleton #{self.class}"
end

#dup

Raises a TypeError to prevent duping.

Raises:

  • (TypeError)
[ GitHub ]

  
# File 'lib/singleton.rb', line 103

def dup
  raise TypeError, "can't dup instance of singleton #{self.class}"
end