ruby_multiton 0.0.1.pre → 0.0.2.pre

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e8cf6a0a0a714b94d3b1fb2ee4445c3eed9e463d
4
- data.tar.gz: 60969f326372e90e304b652abf64b11f3fc27185
3
+ metadata.gz: afff779cb9cfe9e4064c5ae34eb3b586eb4d9fd8
4
+ data.tar.gz: a56bd90775c2936ac005e71096d2e18da0dc423d
5
5
  SHA512:
6
- metadata.gz: e61c552243c7343d68fd446616fd3ab0b934fb6053b67701e726b790c345e01e60b7ea455c51f06b79c1e127502be4718fead2334ae35d0c838bcf9f66c2c5f7
7
- data.tar.gz: 0b1ac68e83427d364f70a5983ad96a497956d50322cfc8c097bf942ac21ab8636e50943994c59b60a95e36fabaa2cfad64c0de66d79b39d0df807b4d3110134c
6
+ metadata.gz: 2fe09106e68a4a800ca2b14a6fcb8bca4c81a66d8ae97c77987a7b909f8a23223d6d3f29d9187821dbfafd5e00243954b21d84672d37dccf60baa60d54cdda1a
7
+ data.tar.gz: e065a49b34a460e8b7982becb9d5f39fc9752059f89b00c68088a774bc9f7edb0b95373a67828b5324d8ca2cb1bfb0eca7382f7028471eaf033fb3004672cf78
@@ -0,0 +1,60 @@
1
+ require "sync".freeze
2
+ require "multiton/utils".freeze
3
+
4
+ module Multiton
5
+ ##
6
+ # InstanceBox is a thread safe container for storing and retrieving multiton instances.
7
+ class InstanceBox
8
+ ##
9
+ # call-seq:
10
+ # new => new_instance
11
+ #
12
+ # Returns a new InstanceBox instance.
13
+ def initialize
14
+ self.hash = {}
15
+ self.sync = Sync.new
16
+ self
17
+ end
18
+
19
+ ##
20
+ # call-seq:
21
+ # get(key) => instance
22
+ #
23
+ # Returns the instance associated with +key+. If +key+ does not exist it returns +nil+.
24
+ def get(key)
25
+ sync.synchronize(:SH) { hash[key] }
26
+ end
27
+
28
+ ##
29
+ # call-seq:
30
+ # key(instance) => key
31
+ #
32
+ # Returns the multiton key associated with +instance+. If +instance+ does not exist it returns +nil+.
33
+ def key(instance)
34
+ sync.synchronize(:SH) { Utils.hash_key(hash, instance) }
35
+ end
36
+
37
+ ##
38
+ # call-seq:
39
+ # store(key, instance) => instance
40
+ #
41
+ # Stores +instance+ indexed by +key+.
42
+ #
43
+ # Returns +instance+.
44
+ def store(key, instance)
45
+ sync.synchronize(:EX) { hash[key] ||= instance }
46
+ end
47
+
48
+ private
49
+
50
+ ##
51
+ # A hash that contains the created instances indexed by their multiton key.
52
+ attr_accessor :hash
53
+
54
+ ##
55
+ # An instance of Sync[https://ruby-doc.org/stdlib-2.4.1/libdoc/sync/rdoc/Sync.html] to provide thread safety (via
56
+ # a {shared-exclusive lock}[https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock]) when reading and writing
57
+ # multiton instances to #hash.
58
+ attr_accessor :sync
59
+ end
60
+ end
@@ -0,0 +1,38 @@
1
+ module Multiton
2
+ ##
3
+ # Mixin adds appropriate behavior to multiton instances.
4
+ module Mixin
5
+ ##
6
+ # call-seq:
7
+ # _dump(level) => string
8
+ #
9
+ # Serializes the instance as a string that can be reconstituted at a later point.
10
+ #
11
+ # Returns string.
12
+ def _dump(_level)
13
+ self.class.instance_variable_get(:@__multiton_instances).key self
14
+ end
15
+
16
+ ##
17
+ # call-seq:
18
+ # clone
19
+ #
20
+ # Raises a TypeError[https://ruby-doc.org/core-2.4.1/TypeError.html] since multiton instances can not be cloned.
21
+ #
22
+ # Never returns.
23
+ def clone
24
+ raise TypeError, "can't clone instance of multiton `#{self.class.name}`"
25
+ end
26
+
27
+ ##
28
+ # call-seq:
29
+ # dup
30
+ #
31
+ # Raises a TypeError[https://ruby-doc.org/core-2.4.1/TypeError.html] since multiton instances can not be duplicated.
32
+ #
33
+ # Never returns.
34
+ def dup
35
+ raise TypeError, "can't dup instance of multiton `#{self.class.name}`"
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,30 @@
1
+ module Multiton
2
+ ##
3
+ # Adds compatibility methods to support different Ruby versions.
4
+ module Utils
5
+ class << self
6
+ if RUBY_VERSION >= "1.9".freeze
7
+ ##
8
+ # call-seq:
9
+ # hash_key(hash, value) => key
10
+ #
11
+ # Returns the key associated with +value+ on +hash+. If +value+ does not exist returns +nil+.
12
+ def hash_key(hash, value)
13
+ hash.key value
14
+ end
15
+ else
16
+ # :nocov:
17
+
18
+ ##
19
+ # call-seq:
20
+ # hash_key(hash, value) => key
21
+ #
22
+ # Returns the key associated with +value+ on +hash+. If +value+ does not exist returns +nil+.
23
+ def hash_key(hash, value)
24
+ hash.index value
25
+ end
26
+ # :nocov:
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,5 @@
1
+ module Multiton
2
+ ##
3
+ # Current version of Multiton.
4
+ VERSION = "0.0.2.pre".freeze
5
+ end
data/lib/multiton.rb ADDED
@@ -0,0 +1,166 @@
1
+ require "extensible".freeze
2
+ require "multiton/instance_box".freeze
3
+ require "multiton/mixin".freeze
4
+ require "multiton/version".freeze
5
+
6
+ ##
7
+ # The Multiton extension implements the {multiton pattern}[https://en.wikipedia.org/wiki/Multiton_pattern] in a manner
8
+ # similar to Ruby standard library's Singleton[https://ruby-doc.org/stdlib-2.4.1/libdoc/singleton/rdoc/Singleton.html]
9
+ # module. As a matter of fact Multiton can be used to implement the singleton pattern in a very straightforward way:
10
+ #
11
+ # class C
12
+ # extend Multiton
13
+ # end
14
+ #
15
+ # C.instance.object_id #=> 47362978769640
16
+ # C.instance.object_id #=> 47362978769640
17
+ #
18
+ # In order to generate and access different instances a +key+ must be provided as a parameter to the +initialize+
19
+ # method:
20
+ #
21
+ # class C
22
+ # extend Multiton
23
+ #
24
+ # def initialize(key)
25
+ # # Initialize this instance (if needed).
26
+ # end
27
+ # end
28
+ #
29
+ # one = C.instance(:one)
30
+ # two = C.instance(:two)
31
+ #
32
+ # one.object_id #=> 46941338337020
33
+ # C.instance(:one).object_id #=> 46941338337020
34
+ #
35
+ # two.object_id #=> 46941338047140
36
+ # C.instance(:two).object_id #=> 46941338047140
37
+ #
38
+ # Almost any object or set of objects will work as a valid +key+. The +key+ assembly is transparently handled by
39
+ # Multiton internally, leaving the end user with an uncluttered and familiar way of designing their classes:
40
+ #
41
+ # class Person
42
+ # extend Multiton
43
+ #
44
+ # attr_reader :full_name
45
+ #
46
+ # def initialize(name:, surname:)
47
+ # self.full_name = "#{surname}, #{name} --- (id: #{object_id})".freeze
48
+ # end
49
+ #
50
+ # private
51
+ #
52
+ # attr_writer :full_name
53
+ # end
54
+ #
55
+ # alice = Person.instance(name: "Alice", surname: "Alcorta")
56
+ # bob = Person.instance(name: "Bob", surname: "Berman")
57
+ #
58
+ # alice.full_name #=> "Alcorta, Alice --- (id: 46921440327980)"
59
+ # Person.instance(name: "Alice", surname: "Alcorta").full_name #=> "Alcorta, Alice --- (id: 46921440327980)"
60
+ #
61
+ # bob.full_name #=> "Berman, Bob --- (id: 46921440022260)"
62
+ # Person.instance(name: "Bob", surname: "Berman").full_name #=> "Berman, Bob --- (id: 46921440022260)"
63
+ #
64
+ # Note that even though keyword arguments will work with Multiton, passing the keywords in a different order will
65
+ # generate a different instance. It is left up to the end user to design their +key+ in a way that minimizes the
66
+ # possibility of improper use.
67
+ module Multiton
68
+ extend Extensible
69
+
70
+ when_extended do |klass|
71
+ unless klass.is_a? Class
72
+ raise TypeError, "expected to extend object of type `Class` with module `#{name}`, got `#{klass.class}` instead"
73
+ end
74
+
75
+ klass.class_eval do
76
+ include Mixin
77
+ @__multiton_instances = InstanceBox.new
78
+ end
79
+ end
80
+
81
+ ##
82
+ # call-seq:
83
+ # _load(args_string) => an_instance
84
+ #
85
+ # Creates or reconstitutes a multiton instance from +args_string+, which is a marshalled representation of the +key+
86
+ # argument(s) passed to #instance.
87
+ #
88
+ # Returns a multiton instance.
89
+ def _load(args_string)
90
+ instance(*Marshal.load(args_string)) # rubocop:disable Security/MarshalLoad
91
+ end
92
+
93
+ ##
94
+ # call-seq:
95
+ # dup => a_class
96
+ #
97
+ # Creates a duplicate of the multiton class. Instances will not be shared between the original and duplicate classes.
98
+ #
99
+ # Returns a new class.
100
+ def dup
101
+ super.tap {|klass| klass.instance_variable_set(:@__multiton_instances, InstanceBox.new) }
102
+ end
103
+
104
+ ##
105
+ # call-seq:
106
+ # instance(*args_key) => an_instance
107
+ #
108
+ # Creates or retrieves the instance corresponding to +args_key+.
109
+ #
110
+ # Returns a multiton instance.
111
+ def instance(*args_key)
112
+ key = Marshal.dump(args_key)
113
+ @__multiton_instances.get(key) || @__multiton_instances.store(key, new(*args_key))
114
+ end
115
+
116
+ private
117
+
118
+ ##
119
+ # call-seq:
120
+ # allocate => nil
121
+ #
122
+ # Empty implementation of the +allocate+ method to block the original implemented in +Class+. Effectively does
123
+ # nothing.
124
+ #
125
+ # Returns +nil+.
126
+ def allocate; end
127
+
128
+ ##
129
+ # call-seq:
130
+ # inherited(subclass) => nil
131
+ #
132
+ # This is called when a multiton class is inherited to properly initialize +subclass+. Instances will not be shared
133
+ # between the superclass and its subclasses.
134
+ #
135
+ # Returns +nil+.
136
+ def inherited(subclass)
137
+ super
138
+ subclass.instance_variable_set(:@__multiton_instances, InstanceBox.new)
139
+ nil
140
+ end
141
+
142
+ ##
143
+ # call-seq:
144
+ # initialize_copy(source) => self
145
+ #
146
+ # This is called (on the clone) when a multiton class (+source+) is cloned to properly initialize it. Instances will
147
+ # not be shared between the original and cloned classes.
148
+ #
149
+ # Returns +self+.
150
+ def initialize_copy(_source)
151
+ super
152
+ extend Multiton
153
+ self
154
+ end
155
+
156
+ ##
157
+ # call-seq:
158
+ # new(*args) => new_instance
159
+ #
160
+ # Creates a new multiton instance and initializes it by passing +args+ to its constructor.
161
+ #
162
+ # Returns a new multiton instance.
163
+ def new(*args)
164
+ super
165
+ end
166
+ end
@@ -0,0 +1 @@
1
+ require "multiton".freeze
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
  "*.gemspec".freeze,
12
12
  "LICENSE.*".freeze,
13
13
  "README.*".freeze,
14
- "liv/**/*.rb".freeze
14
+ "lib/**/*.rb".freeze
15
15
  ]
16
16
  spec.name = "ruby_multiton".freeze
17
17
  spec.summary = "Ruby Multiton pattern implementation.".freeze
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_multiton
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.pre
4
+ version: 0.0.2.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gabriel de Oliveira
@@ -172,6 +172,12 @@ extra_rdoc_files: []
172
172
  files:
173
173
  - LICENSE.txt
174
174
  - README.md
175
+ - lib/multiton.rb
176
+ - lib/multiton/instance_box.rb
177
+ - lib/multiton/mixin.rb
178
+ - lib/multiton/utils.rb
179
+ - lib/multiton/version.rb
180
+ - lib/ruby_multiton.rb
175
181
  - ruby_multiton.gemspec
176
182
  homepage: https://github.com/gdeoliveira/ruby_multiton
177
183
  licenses: