inoculate 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7bd8610b9bbde1304734505a64fdf22c8665961e3b02d93171ac4fc6ccf313aa
4
- data.tar.gz: 66ee174fa4bd02dfd671a39a946a8343da1d63bb88f9be05ee94816d033a5e4e
3
+ metadata.gz: 7f86297ac65acf1b0193d5afc05cda7cd6970b6ffcd3699f0003256f2fc06b8b
4
+ data.tar.gz: ce435f6820bd36596f50fa23d5bfbd1a018bca2f2926f5d404f04ac0f54a1296
5
5
  SHA512:
6
- metadata.gz: bbc2655324e68cd2ad977192b81f9cbef0d63b72da91f05521c59eb91f1d58993da68dbcd8d4f6b69be5e5d93708e847f567955f80cb042761e187b64c1bcdbc
7
- data.tar.gz: ef316f9b6e6dfa9cd42811f8af5082f402d0c03478a280a2728b61ce9c706a9e0b1cd0249921d18818cfa85b28ccf81969a1330744c3d8d19e95e5fd4f73a81c
6
+ metadata.gz: 885671688aa9dc16c6bde1f677326049d8c5d53fbaf8300e77c4c86d5ded34719ba1dbee542f3baa29a1d5d6c35e75773564142aab6e159448a564a8f49ca9f0
7
+ data.tar.gz: 2b709302a5cba52f5eff6d8faae9e0d1fbfefe893c20763d6ea4865f113af010993bbf89b15cdfe6c6090a52b36e3fb9d8ff513d16d24a0741b323f7c5aff0c5
data/CHANGELOG.md CHANGED
@@ -1,4 +1,8 @@
1
- ## v0.4.0 \[Unreleased]
1
+ ## v0.5.0 \[2022-10-13]
2
+
3
+ * Add thread singleton dependency registration.
4
+
5
+ ## v0.4.0 \[2022-10-13]
2
6
 
3
7
  * Add singleton dependency registration.
4
8
 
data/README.md CHANGED
@@ -210,6 +210,80 @@ This results in:
210
210
  => nil
211
211
  ```
212
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
+
213
287
  ### Renaming the Declaration API
214
288
  The `inoculate_with` API is named to avoid immediate collisions with other modules
215
289
  and code you may have. You can use it as-is, or rename it to something you see fit
@@ -36,6 +36,15 @@ module Inoculate
36
36
  nil
37
37
  end
38
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
+
39
48
  private
40
49
 
41
50
  attr_reader :manufacturer
@@ -88,6 +88,29 @@ module Inoculate
88
88
  register_blueprint(name, :singleton, &block)
89
89
  end
90
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
+
91
114
  private
92
115
 
93
116
  def register_blueprint(name, lifecycle, &block)
@@ -110,6 +133,7 @@ module Inoculate
110
133
  when :transient then build_transient(name, factory)
111
134
  when :instance then build_instance(name, factory)
112
135
  when :singleton then build_singleton(name, factory)
136
+ when :thread_singleton then build_thread_singleton(name, factory)
113
137
  else raise ArgumentError, "Life cycle #{lifecycle} is not valid. Something has gone very wrong."
114
138
  end
115
139
 
@@ -144,6 +168,13 @@ module Inoculate
144
168
  end
145
169
  end
146
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
+
147
178
  def validate_dependency_name(name)
148
179
  raise Errors::InvalidName, "name must be a symbol or convert to one" unless name.respond_to? :to_sym
149
180
  begin
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Inoculate
4
4
  # The library version.
5
- VERSION = "0.4.0"
5
+ VERSION = "0.5.0"
6
6
  end
data/sig/inoculate.rbs CHANGED
@@ -35,6 +35,7 @@ module Inoculate
35
35
  def transient: (builder_name | _ToSymbol) { () -> void } -> void
36
36
  def instance: (builder_name | _ToSymbol) { () -> void } -> void
37
37
  def singleton: (builder_name | _ToSymbol) { () -> void } -> void
38
+ def thread_singleton: (builder_name | _ToSymbol) { () -> void } -> void
38
39
  end
39
40
 
40
41
  class Configurer
@@ -43,6 +44,7 @@ module Inoculate
43
44
  def transient: (builder_name | _ToSymbol) { () -> void } -> nil
44
45
  def instance: (builder_name | _ToSymbol) { () -> void } -> nil
45
46
  def singleton: (builder_name | _ToSymbol) { () -> void } -> nil
47
+ def thread_singleton: (builder_name | _ToSymbol) { () -> void } -> nil
46
48
 
47
49
  private
48
50
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inoculate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephan Tarulli