Multiton

Gem Version Build Status Inline docs

Multiton is an implementation of the multiton pattern in pure Ruby. Some its features include:

Installation

Add this line to your application's Gemfile:

gem "ruby_multiton"

And then execute:

$ bundle

Or install it yourself as:

$ gem install ruby_multiton

Usage

Implementing a singleton class

The easiest use case for Multiton is to implement a singleton class. To achieve this Multiton mimics the approach used by the Singleton module from Ruby's standard library:

require "multiton"

class C
  extend Multiton
end

C.instance.object_id  #=> 47143710911900
C.instance.object_id  #=> 47143710911900

As an example lets create a singleton object that returns the amount of seconds elapsed since it was first initialized:

class Timestamp
  extend Multiton

  def initialize
    self.initialized_at = Time.now
  end

  def elapsed_seconds
    Time.now - initialized_at
  end

  private

  attr_accessor :initialized_at
end

Timestamp.instance  #=> #<Timestamp:0x00562e486384c8 @initialized_at=2017-04-17 20:00:15 +0000>

# Some time later...
Timestamp.instance.elapsed_seconds  #=> 542.955918039

Implementing a multiton class

To implement a multiton class we will need a key to be able to access the different instances afterwards. Multiton achieves this by using the parameters passed to the initialize method as the key:

require "multiton"

class C
  extend Multiton

  def initialize(key)
  end
end

C.instance(:one).object_id  #=> 47387777147320
C.instance(:one).object_id  #=> 47387777147320

C.instance(:two).object_id  #=> 47387776632880
C.instance(:two).object_id  #=> 47387776632880

As an example lets create multiton objects representing playing cards that we can check if they were drawn or not:

class Card
  extend Multiton

  def initialize(number, suit)
    self.has_been_drawn = false
  end

  def draw
    self.has_been_drawn = true
  end

  def drawn?
    has_been_drawn
  end

  private

  attr_accessor :has_been_drawn
end

Card.instance(10, :spades).drawn?  #=> false

Card.instance(5, :hearts).draw
Card.instance(10, :spades).draw

Card.instance(5, :hearts).drawn?    #=> true
Card.instance(10, :spades).drawn?   #=> true
Card.instance(2, :diamonds).drawn?  #=> false

Serializing and deserializing multiton instances

Multiton instances can be serialized and deserialized like regular objects. Continuing with the previous Card example:

Card.instance(2, :diamonds).drawn?  #=> false

serialized_card = Marshal.dump(Card.instance(2, :diamonds))

Marshal.load(serialized_card).drawn?  #=> false

Card.instance(2, :diamonds).draw

Marshal.load(serialized_card).drawn?  #=> true

Clonning, duplicating and inheriting from multiton classes

Multiton supports cloning, duplicating and inheriting from a multiton class:

require "multiton"

class C
  extend Multiton
end

D = C.clone

E = D.dup

class F < E
end

F.instance.object_id  #=> 47418482076880
F.instance.object_id  #=> 47418482076880

Note that C, D, E and F are all considered different classes and will consequently not share any instances.

Contributing

  1. Fork it ( https://github.com/gdeoliveira/ruby_multiton/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am "Add some feature")
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request