inoculate 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +127 -1
- data/lib/inoculate/configurer.rb +18 -0
- data/lib/inoculate/manufacturer.rb +81 -17
- data/lib/inoculate/version.rb +1 -1
- data/sig/inoculate.rbs +4 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f86297ac65acf1b0193d5afc05cda7cd6970b6ffcd3699f0003256f2fc06b8b
|
4
|
+
data.tar.gz: ce435f6820bd36596f50fa23d5bfbd1a018bca2f2926f5d404f04ac0f54a1296
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 885671688aa9dc16c6bde1f677326049d8c5d53fbaf8300e77c4c86d5ded34719ba1dbee542f3baa29a1d5d6c35e75773564142aab6e159448a564a8f49ca9f0
|
7
|
+
data.tar.gz: 2b709302a5cba52f5eff6d8faae9e0d1fbfefe893c20763d6ea4865f113af010993bbf89b15cdfe6c6090a52b36e3fb9d8ff513d16d24a0741b323f7c5aff0c5
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -25,6 +25,7 @@ It provides several life-cycles and provides dependency access through private a
|
|
25
25
|
1. [Dependency Life Cycles](#dependency-life-cycles)
|
26
26
|
1. [Transient](#transient)
|
27
27
|
2. [Instance](#instance)
|
28
|
+
3. [Singleton](#singleton)
|
28
29
|
2. [Renaming the Declaration API](#renaming-the-declaration-api)
|
29
30
|
3. [Hide Your Dependency on Inoculate](#hide-your-dependency-on-inoculate)
|
30
31
|
3. [Installation](#installation)
|
@@ -100,13 +101,16 @@ class Example
|
|
100
101
|
end
|
101
102
|
end
|
102
103
|
|
103
|
-
|
104
|
+
a = Example.new
|
105
|
+
puts a, a, a
|
104
106
|
```
|
105
107
|
|
106
108
|
This results in:
|
107
109
|
|
108
110
|
```
|
109
111
|
Count is: 0
|
112
|
+
Count is: 0
|
113
|
+
Count is: 0
|
110
114
|
=> nil
|
111
115
|
```
|
112
116
|
|
@@ -158,6 +162,128 @@ This results in:
|
|
158
162
|
=> nil
|
159
163
|
```
|
160
164
|
|
165
|
+
#### Singleton
|
166
|
+
Singleton dependencies are constructed once.
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
class Counter
|
170
|
+
attr_reader :count
|
171
|
+
|
172
|
+
def initialize
|
173
|
+
@count = 0
|
174
|
+
end
|
175
|
+
|
176
|
+
def inc
|
177
|
+
@count += 1
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
Inoculate.initialize do |config|
|
182
|
+
config.singleton(:counter) { Counter.new }
|
183
|
+
end
|
184
|
+
|
185
|
+
class Example
|
186
|
+
include Inoculate::Porter
|
187
|
+
inoculate_with :counter
|
188
|
+
|
189
|
+
def initialize(name)
|
190
|
+
@name = name
|
191
|
+
end
|
192
|
+
|
193
|
+
def to_s
|
194
|
+
counter.inc
|
195
|
+
"[#{@name}] Count is: #{counter.count}"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
a = Example.new("a")
|
200
|
+
b = Example.new("b")
|
201
|
+
puts a, a, b
|
202
|
+
```
|
203
|
+
|
204
|
+
This results in:
|
205
|
+
|
206
|
+
```
|
207
|
+
[a] Count is: 1
|
208
|
+
[a] Count is: 2
|
209
|
+
[b] Count is: 3
|
210
|
+
=> nil
|
211
|
+
```
|
212
|
+
|
213
|
+
#### Thread Singleton
|
214
|
+
Thread Singleton dependencies are constructed once for any thread or fiber.
|
215
|
+
|
216
|
+
```ruby
|
217
|
+
class Counter
|
218
|
+
attr_reader :count
|
219
|
+
|
220
|
+
def initialize
|
221
|
+
@count = 0
|
222
|
+
end
|
223
|
+
|
224
|
+
def inc
|
225
|
+
@count += 1
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
Inoculate.initialize do |config|
|
230
|
+
config.thread_singleton(:counter) { Counter.new }
|
231
|
+
end
|
232
|
+
|
233
|
+
class Example
|
234
|
+
include Inoculate::Porter
|
235
|
+
inoculate_with :counter
|
236
|
+
|
237
|
+
def initialize(name)
|
238
|
+
@name = "Example: #{name}"
|
239
|
+
end
|
240
|
+
|
241
|
+
def to_s
|
242
|
+
counter.inc
|
243
|
+
"[#{@name}] Count is: #{counter.count}"
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
class AnotherExample
|
248
|
+
include Inoculate::Porter
|
249
|
+
inoculate_with :counter
|
250
|
+
|
251
|
+
def initialize(name)
|
252
|
+
@name = "AnotherExample: #{name}"
|
253
|
+
end
|
254
|
+
|
255
|
+
def to_s
|
256
|
+
5.times { counter.inc }
|
257
|
+
"[#{@name}] Count is: #{counter.count}"
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
threads = %w[a b].map do |tag|
|
262
|
+
Thread.new(tag) do |t|
|
263
|
+
e = Example.new(t)
|
264
|
+
a = AnotherExample.new(t)
|
265
|
+
puts e, e, a, a
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
threads.each(&:join)
|
270
|
+
```
|
271
|
+
|
272
|
+
This results in:
|
273
|
+
|
274
|
+
```
|
275
|
+
[Example: a] Count is: 1
|
276
|
+
[Example: b] Count is: 1
|
277
|
+
[Example: b] Count is: 2
|
278
|
+
[Example: a] Count is: 2
|
279
|
+
[AnotherExample: b] Count is: 7
|
280
|
+
[AnotherExample: a] Count is: 7
|
281
|
+
[AnotherExample: b] Count is: 12
|
282
|
+
[AnotherExample: a] Count is: 12
|
283
|
+
=> [#<Thread:0x000000010d703c68 (irb):177 dead>, #<Thread:0x000000010d703b50 (irb):177 dead>]
|
284
|
+
```
|
285
|
+
|
286
|
+
|
161
287
|
### Renaming the Declaration API
|
162
288
|
The `inoculate_with` API is named to avoid immediate collisions with other modules
|
163
289
|
and code you may have. You can use it as-is, or rename it to something you see fit
|
data/lib/inoculate/configurer.rb
CHANGED
@@ -27,6 +27,24 @@ module Inoculate
|
|
27
27
|
nil
|
28
28
|
end
|
29
29
|
|
30
|
+
# Register a singleton dependency.
|
31
|
+
# @see Manufacturer#singleton
|
32
|
+
#
|
33
|
+
# @since 0.4.0
|
34
|
+
def singleton(name, &block)
|
35
|
+
manufacturer.singleton(name, &block)
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
# Register a thread singleton dependency.
|
40
|
+
# @see Manufacturer#thread_singleton
|
41
|
+
#
|
42
|
+
# @since 0.5.0
|
43
|
+
def thread_singleton(name, &block)
|
44
|
+
manufacturer.thread_singleton(name, &block)
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
|
30
48
|
private
|
31
49
|
|
32
50
|
attr_reader :manufacturer
|
@@ -5,7 +5,6 @@ require "digest"
|
|
5
5
|
module Inoculate
|
6
6
|
# Registers and builds dependency injection modules.
|
7
7
|
# @todo singleton life cycle
|
8
|
-
# @todo instance life cycle
|
9
8
|
# @todo thread singleton life cycle
|
10
9
|
#
|
11
10
|
# @since 0.1.0
|
@@ -40,16 +39,7 @@ module Inoculate
|
|
40
39
|
#
|
41
40
|
# @since 0.1.0
|
42
41
|
def transient(name, &block)
|
43
|
-
|
44
|
-
raise Errors::AlreadyRegistered if @registered_blueprints.has_key? name
|
45
|
-
raise Errors::RequiresCallable if block.nil?
|
46
|
-
|
47
|
-
blueprint_name = name.to_sym
|
48
|
-
@registered_blueprints[blueprint_name] = {
|
49
|
-
lifecycle: :transient,
|
50
|
-
factory: block,
|
51
|
-
accessor_module: build_module(blueprint_name, :transient, block)
|
52
|
-
}
|
42
|
+
register_blueprint(name, :transient, &block)
|
53
43
|
end
|
54
44
|
|
55
45
|
# Register an instance dependency.
|
@@ -72,26 +62,78 @@ module Inoculate
|
|
72
62
|
#
|
73
63
|
# @since 0.3.0
|
74
64
|
def instance(name, &block)
|
65
|
+
register_blueprint(name, :instance, &block)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Register a singleton dependency.
|
69
|
+
#
|
70
|
+
# A singleton dependency gets created once.
|
71
|
+
#
|
72
|
+
# @example With a block
|
73
|
+
# manufacturer.singleton(:sha1_hasher) { Digest::SHA1.new }
|
74
|
+
#
|
75
|
+
# @example With a Proc
|
76
|
+
# manufacturer.singleton(:sha1_hasher, &-> { Digest::SHA1.new })
|
77
|
+
#
|
78
|
+
# @param name [Symbol, #to_sym] the dependency name which will be used to access it
|
79
|
+
# @param block [Block, Proc] a factory method to build the dependency
|
80
|
+
#
|
81
|
+
# @raise [Errors::RequiresCallable] if no block is provided
|
82
|
+
# @raise [Errors::InvalidName] if the name is not a symbol, cannot be converted to a symbol,
|
83
|
+
# or is not a valid attribute name
|
84
|
+
# @raise [Errors::AlreadyRegistered] if the name has been registered previously
|
85
|
+
#
|
86
|
+
# @since 0.4.0
|
87
|
+
def singleton(name, &block)
|
88
|
+
register_blueprint(name, :singleton, &block)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Register a thread singleton dependency.
|
92
|
+
#
|
93
|
+
# A thread singleton dependency gets created once per thread or fiber.
|
94
|
+
#
|
95
|
+
# @example With a block
|
96
|
+
# manufacturer.thread_singleton(:sha1_hasher) { Digest::SHA1.new }
|
97
|
+
#
|
98
|
+
# @example With a Proc
|
99
|
+
# manufacturer.thread_singleton(:sha1_hasher, &-> { Digest::SHA1.new })
|
100
|
+
#
|
101
|
+
# @param name [Symbol, #to_sym] the dependency name which will be used to access it
|
102
|
+
# @param block [Block, Proc] a factory method to build the dependency
|
103
|
+
#
|
104
|
+
# @raise [Errors::RequiresCallable] if no block is provided
|
105
|
+
# @raise [Errors::InvalidName] if the name is not a symbol, cannot be converted to a symbol,
|
106
|
+
# or is not a valid attribute name
|
107
|
+
# @raise [Errors::AlreadyRegistered] if the name has been registered previously
|
108
|
+
#
|
109
|
+
# @since 0.5.0
|
110
|
+
def thread_singleton(name, &block)
|
111
|
+
register_blueprint(name, :thread_singleton, &block)
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def register_blueprint(name, lifecycle, &block)
|
75
117
|
validate_dependency_name name
|
76
118
|
raise Errors::AlreadyRegistered if @registered_blueprints.has_key? name
|
77
119
|
raise Errors::RequiresCallable if block.nil?
|
78
120
|
|
79
121
|
blueprint_name = name.to_sym
|
80
122
|
@registered_blueprints[blueprint_name] = {
|
81
|
-
lifecycle:
|
123
|
+
lifecycle: lifecycle,
|
82
124
|
factory: block,
|
83
|
-
accessor_module: build_module(blueprint_name,
|
125
|
+
accessor_module: build_module(blueprint_name, lifecycle, block)
|
84
126
|
}
|
85
127
|
end
|
86
128
|
|
87
|
-
private
|
88
|
-
|
89
129
|
def build_module(name, lifecycle, factory)
|
90
|
-
module_name = "I#{
|
130
|
+
module_name = "I#{hash_name(name)}"
|
91
131
|
module_body =
|
92
132
|
case lifecycle
|
93
133
|
when :transient then build_transient(name, factory)
|
94
134
|
when :instance then build_instance(name, factory)
|
135
|
+
when :singleton then build_singleton(name, factory)
|
136
|
+
when :thread_singleton then build_thread_singleton(name, factory)
|
95
137
|
else raise ArgumentError, "Life cycle #{lifecycle} is not valid. Something has gone very wrong."
|
96
138
|
end
|
97
139
|
|
@@ -105,7 +147,7 @@ module Inoculate
|
|
105
147
|
end
|
106
148
|
|
107
149
|
def build_instance(name, factory)
|
108
|
-
cache_variable_name = "@icache_#{
|
150
|
+
cache_variable_name = "@icache_#{hash_name(name)}"
|
109
151
|
Module.new do
|
110
152
|
define_method(name) do
|
111
153
|
instance_variable_set(cache_variable_name, factory.call) unless instance_variable_defined?(cache_variable_name)
|
@@ -115,6 +157,24 @@ module Inoculate
|
|
115
157
|
end
|
116
158
|
end
|
117
159
|
|
160
|
+
def build_singleton(name, factory)
|
161
|
+
cache_variable_name = "@@icache_#{hash_name(name)}"
|
162
|
+
Module.new do |mod|
|
163
|
+
define_method(name) do
|
164
|
+
mod.class_variable_set(cache_variable_name, factory.call) unless mod.class_variable_defined?(cache_variable_name)
|
165
|
+
mod.class_variable_get(cache_variable_name)
|
166
|
+
end
|
167
|
+
private name
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def build_thread_singleton(name, factory)
|
172
|
+
thread_variable_name = "icache_#{hash_name(name)}"
|
173
|
+
Module.new do
|
174
|
+
private define_method(name) { Thread.current[thread_variable_name] ||= factory.call }
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
118
178
|
def validate_dependency_name(name)
|
119
179
|
raise Errors::InvalidName, "name must be a symbol or convert to one" unless name.respond_to? :to_sym
|
120
180
|
begin
|
@@ -123,5 +183,9 @@ module Inoculate
|
|
123
183
|
raise Errors::InvalidName, "name must be a valid attr_reader"
|
124
184
|
end
|
125
185
|
end
|
186
|
+
|
187
|
+
def hash_name(name)
|
188
|
+
Digest::SHA1.hexdigest(name.to_s)
|
189
|
+
end
|
126
190
|
end
|
127
191
|
end
|
data/lib/inoculate/version.rb
CHANGED
data/sig/inoculate.rbs
CHANGED
@@ -34,6 +34,8 @@ module Inoculate
|
|
34
34
|
attr_reader registered_blueprints: Hash[builder_name, blueprint]
|
35
35
|
def transient: (builder_name | _ToSymbol) { () -> void } -> void
|
36
36
|
def instance: (builder_name | _ToSymbol) { () -> void } -> void
|
37
|
+
def singleton: (builder_name | _ToSymbol) { () -> void } -> void
|
38
|
+
def thread_singleton: (builder_name | _ToSymbol) { () -> void } -> void
|
37
39
|
end
|
38
40
|
|
39
41
|
class Configurer
|
@@ -41,6 +43,8 @@ module Inoculate
|
|
41
43
|
|
42
44
|
def transient: (builder_name | _ToSymbol) { () -> void } -> nil
|
43
45
|
def instance: (builder_name | _ToSymbol) { () -> void } -> nil
|
46
|
+
def singleton: (builder_name | _ToSymbol) { () -> void } -> nil
|
47
|
+
def thread_singleton: (builder_name | _ToSymbol) { () -> void } -> nil
|
44
48
|
|
45
49
|
private
|
46
50
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inoculate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephan Tarulli
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-10-
|
11
|
+
date: 2022-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |
|
14
14
|
Inoculate is a small, thread-safe dependency injection library configured entirely with Ruby.
|