containable 2.2.0 → 2.3.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: 49be2a5c56c8ad8cd53ce41a6498bdb8aa47ab042cb123660dae6936ff4daade
4
- data.tar.gz: dc75daed9d1f05881d54164e76e0e57a505a9d0699dc49bd1fde20f698dc6f45
3
+ metadata.gz: 7c5be0b5f9d940c754f77ded0f06992cb9d1dc13e45ec055ccde1a877fbe1782
4
+ data.tar.gz: 156d209e8b763fe0f995fa19bb709282537c2122c1a54e05c167e082f052055d
5
5
  SHA512:
6
- metadata.gz: 4a67b2f11f343e08f9e351605e9721dbd302db977c4cd0ba6146235cca362e8b348be834f274c665669de8102659445519eb08f19ddaa54d948e09613d816b18
7
- data.tar.gz: 3cf88215110cc8e90f84280df6bbcc764744454b8b58a4a9cbe53bd115a4d78824cff6a5dcb08927cacafa33fc062298fa8ccd38efacdc4d8a17e23c4d10fe45
6
+ metadata.gz: 8c6fa5e47492849f7d67121a0725dae9466f0bb79fdc4e014f8ba80daa02d68507b6efb3dc56a3d75fc87b0130d2b001127a78c709eb8a54b345bd8f09d14d53
7
+ data.tar.gz: 9a43d1dfce8ea6768240a48c4292b10b26abd984fa51c969dd68b6bd4a5170edaabac3a7dc716b9995559b592b844bb3bea4ab0a01b7aa5535a298c67dd2f757
checksums.yaml.gz.sig CHANGED
Binary file
data/README.adoc CHANGED
@@ -110,7 +110,7 @@ module Container
110
110
  end
111
111
  ----
112
112
 
113
- With the above, `1` (literal) will be associated with the `:demo` key. This is perfect for registering literals, constants, or any objects you immediately want evaluated or have a reference to. To lazily register a dependency, use a block with parameters:
113
+ With the above, `1` (literal) will be associated with the `:demo` key. This is perfect for registering literals, constants, or any objects you immediately want evaluated or have a reference to. To lazy register a dependency, use a block with parameters:
114
114
 
115
115
  [source,ruby]
116
116
  ----
@@ -155,7 +155,7 @@ end
155
155
  Use `:cache` and `:fresh` to direct how your closures will be resolved. Here's what each does:
156
156
 
157
157
  * `+:cache+`: Ensures the same object is answered each time the key is resolved. In the above example, this means the `one` dependency will always answer the _same_ instance of an `Object` when resolved. This is default behavior so you don't need to define this key and is only shown for explicit illustration purposes.
158
- * `+:fresh+`: Ensures a new object is answered each time the key is resolved. In the above example, this means that the `two` dependency will always answer a _different_ instance of an `Object`. You want to use this when you want to lazily resolve a dependency while still wanting a new instance each time.
158
+ * `+:fresh+`: Ensures a new object is answered each time the key is resolved. In the above example, this means that the `two` dependency will always answer a _different_ instance of an `Object`. You want to use this when you want to lazy resolve a dependency while still wanting a new instance each time.
159
159
 
160
160
  💡 The `as` key is only applied when using a closure with no parameters and is ignored otherwise. This means you don't need to supply this key when using literals.
161
161
 
@@ -188,8 +188,15 @@ module Container
188
188
  register :one, 1
189
189
  end
190
190
 
191
+ # Single (preferred)
191
192
  Container.register :two, 2
193
+
194
+ # Single (setter)
192
195
  Container[:three] = 3
196
+
197
+ # Multiples.
198
+ Container.register(:four, 4)
199
+ .register(:five, 5)
193
200
  ----
194
201
 
195
202
  With the above, a combination of `.register` and `.[]=` (setter) messages are used. While the latter is handy, the former is preferred for improved readability.
@@ -246,7 +253,7 @@ Container[:two] # #<Proc:0x000000012e9f8718 /demo:23>
246
253
  Container[:three] # #<Proc:0x000000012e9f8628 /demo:24 (lambda)>
247
254
  ----
248
255
 
249
- With the above, you can see `:one` was immediately resolved to the value of `1` even though it was wrapped in a closure to begin with. This happened because the closure had no parameters so was safe to resolve. Again, this allows you to lazily resolve a dependency until you need it.
256
+ With the above, you can see `:one` was immediately resolved to the value of `1` even though it was wrapped in a closure to begin with. This happened because the closure had no parameters so was safe to resolve. Again, this allows you to lazy resolve a dependency until you need it.
250
257
 
251
258
  For keys `:two` and `:three`, we have a closure that has at least one parameter so remains a closure. This allows you to supply required arguments later. Here's a closer look of using the `:two` and `:three` dependencies:
252
259
 
@@ -277,7 +284,65 @@ Container[:two] # #<Object:0x000000012d237728>
277
284
  Container[:two] # #<Object:0x000000012d2de550>
278
285
  ----
279
286
 
280
- Notice `one` always answers the same instance of an `Object` while `two` always answers a new instance of `Object`. By using `:fresh`, this allows you to lazily evaluate your closure while disabling default caching support.
287
+ Notice `one` always answers the same instance of an `Object` while `two` always answers a new instance of `Object`. By using `:fresh`, this allows you to lazy evaluate your closure while disabling default caching support.
288
+
289
+ === Merges
290
+
291
+ You can merge dependencies from another container into your current container. Consider the following containers:
292
+
293
+ [source,ruby]
294
+ ----
295
+ module Primary
296
+ extend Containable
297
+
298
+ register :zero, 0
299
+ end
300
+
301
+ module Secondary
302
+ extend Containable
303
+
304
+ namespace :one do
305
+ register :two, 2
306
+ end
307
+
308
+ register :three, 3
309
+ end
310
+
311
+ module Other
312
+ extend Containable
313
+
314
+ register :four, 4
315
+ end
316
+ ----
317
+
318
+ With the above, this means you can do the following:
319
+
320
+ [source,ruby]
321
+ ----
322
+ # Merge a single dependency.
323
+ Primary.merge Secondary, :three
324
+ Primary[:three] # 3
325
+
326
+ # Merge multiple dependencies.
327
+ Primary.merge Secondary, "one.two", :three
328
+ Primary["one.two"] # 2
329
+ Primary[:three] # 3
330
+
331
+ # Merge with a namespace.
332
+ Primary.merge Secondary, "one.two", :three, namespace: :top
333
+ Primary["top.one.two"] # 2
334
+ Primary["top.three"] # 3
335
+
336
+ # Merge as fresh (normally, the default is cache).
337
+ Primary.merge Secondary, "one.two", as: :fresh
338
+
339
+ # Merge with repeated messages since it answers itself.
340
+ Primary.merge(Secondary, :three)
341
+ .merge(Other, :four)
342
+
343
+ Primary[:three] # 3
344
+ Primary[:four] # 4
345
+ ----
281
346
 
282
347
  === Namespaces
283
348
 
@@ -316,6 +381,16 @@ Container["two.green"] # "green"
316
381
  Container["three.silver"] # "silver"
317
382
  ----
318
383
 
384
+ You can also send multiple messages to create a namespace register a dependency:
385
+
386
+ [source,]
387
+ ----
388
+ Container.namespace(:one)
389
+ .register(:two, 2)
390
+ ----
391
+
392
+ This is a convenience that should only be used in rare situations because defining your namespaces and associated dependencies using block syntax improves readability.
393
+
319
394
  === Enumeration
320
395
 
321
396
  Enumeration is possible but limited. Given the following:
@@ -407,7 +482,7 @@ Container.clone.frozen? # true
407
482
 
408
483
  === Customization
409
484
 
410
- You can customize how the container registers and resolves dependencies by creating your own register and resolver. Internally, both of these objects have access to and use `dependencies` (i.e. `Concurrent::Hash`) which stores the registered key and tuple. Example:
485
+ You can customize how the container registers and resolves dependencies by creating your own register and resolver classes. Both classes should inherit from either `Containable::Register` or `Containable::Resolver`. Internally, each subclass has access to the `dependencies` (i.e. `Concurrent::Hash`) object which stores the registered key and associated value (tuple). Example:
411
486
 
412
487
  [source,ruby]
413
488
  ----
@@ -417,9 +492,13 @@ You can customize how the container registers and resolves dependencies by creat
417
492
  }
418
493
  ----
419
494
 
420
- Each tuple captures the dependency (value) and directive (i.e. `:cache` or `:fresh`). This allows you to have access to all information captured at registration. Below are a few examples on how to use and customize this information for your own purposes.
495
+ Each tuple captures the dependency (value) and directive (i.e. `:cache` or `:fresh`). This allows you to have access to all information captured at registration. Definitely read the source code of each class to learn more.
496
+
497
+ The following provides a few examples on how you can customize further.
421
498
 
422
- Here's how to use a custom register that doesn't care if you override an existing key.
499
+ ==== Registers
500
+
501
+ Here's an example of a custom register that doesn't care if you override an existing key.
423
502
 
424
503
  [source,ruby]
425
504
  ----
@@ -441,6 +520,39 @@ end
441
520
  Container[:one] # "override"
442
521
  ----
443
522
 
523
+ Alternatively, you could specify which keys are allowed to be overwritten. Here's a modification to the above example that would make this possible:
524
+
525
+ [source,ruby]
526
+ ----
527
+ require "containable"
528
+ require "refinements/array"
529
+
530
+ class Register < Containable::Register
531
+ using Refinements::Array
532
+
533
+ def initialize(*, allowed_keys: %w[http logger], **)
534
+ super(*, **)
535
+ @allowed_keys = allowed_keys
536
+ end
537
+
538
+ protected
539
+
540
+ def check_duplicate key, namespaced_key
541
+ return if allowed_keys.include? namespaced_key
542
+
543
+ message = "Dependency is already registered: #{key.inspect}."
544
+
545
+ fail KeyError, message if dependencies.key? namespaced_key
546
+ end
547
+
548
+ private
549
+
550
+ attr_reader :allowed_keys
551
+ end
552
+ ----
553
+
554
+ ==== Resolvers
555
+
444
556
  Here's an example with a custom resolver that only allows specific keys to be resolved:
445
557
 
446
558
  [source,ruby]
@@ -477,8 +589,6 @@ Container[:two] # Only use these keys: [:one, :three] (KeyError)
477
589
  Container[:three] # 3
478
590
  ----
479
591
 
480
- In both cases, you only need to inject your custom register or resolver when extending your container with `Containable`. Both of these classes should inherit from either `Containable::Register` or `Containable::Resolver` to customize behavior as you like. Definitely read the source code of both these classes to learn more.
481
-
482
592
  === Infusible
483
593
 
484
594
  To fully leverage the power of this gem, check out {infusible_link}. You can get far with simple containers but if you want to supercharge your containers and make your architecture truly come alive then make sure to couple this gem with the {infusible_link} gem. 🚀
data/containable.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "containable"
5
- spec.version = "2.2.0"
5
+ spec.version = "2.3.0"
6
6
  spec.authors = ["Brooke Kuhlmann"]
7
7
  spec.email = ["brooke@alchemists.io"]
8
8
  spec.homepage = "https://alchemists.io/projects/containable"
@@ -40,11 +40,22 @@ module Containable
40
40
  fail FrozenError, "Can't modify frozen container." if dependencies.frozen?
41
41
 
42
42
  target.call key, value, as:, &block
43
+ self
44
+ end
45
+ end
46
+
47
+ def define_merge target: register
48
+ define_method :merge do |other, *keys, namespace: nil, as: :cache|
49
+ target.merge(other, *keys, namespace:, as:)
50
+ self
43
51
  end
44
52
  end
45
53
 
46
54
  def define_namespace target = register
47
- define_method(:namespace) { |name, &block| target.namespace name, &block }
55
+ define_method :namespace do |name, &block|
56
+ target.namespace name, &block
57
+ self
58
+ end
48
59
  end
49
60
 
50
61
  def define_resolve target = resolver
@@ -89,5 +100,17 @@ module Containable
89
100
  def define_frozen?
90
101
  define_method(:frozen?) { dependencies.frozen? }
91
102
  end
103
+
104
+ def define_stub
105
+ define_method :stub! do |**keywords|
106
+ require "containable/test"
107
+
108
+ extend Test
109
+
110
+ stub(**keywords)
111
+ end
112
+ end
113
+
114
+ def define_restore = define_method(:restore) { false }
92
115
  end
93
116
  end
@@ -25,22 +25,31 @@ module Containable
25
25
  check_duplicate key, namespaced_key
26
26
  check_directive as
27
27
  dependencies[namespaced_key] = [block || value, as]
28
+ self
28
29
  end
29
30
 
30
31
  alias register call
31
32
 
33
+ def merge other, *keys, namespace: nil, as: :cache
34
+ keys.each { call [namespace, it].compact.join("."), other[it], as: }
35
+ self
36
+ end
37
+
32
38
  def namespace(name, &)
33
39
  keys.clear if depth.zero?
34
40
  keys.append name
35
41
  visit(&)
42
+ self
36
43
  end
37
44
 
38
- private
45
+ protected
39
46
 
40
47
  attr_reader :dependencies, :separator, :directives
41
48
 
42
49
  attr_accessor :keys, :depth
43
50
 
51
+ def namespacify(key) = keys[..depth].append(key).join separator
52
+
44
53
  def check_duplicate key, namespaced_key
45
54
  message = "Dependency is already registered: #{key.inspect}."
46
55
 
@@ -54,6 +63,8 @@ module Containable
54
63
  %(Invalid directive: #{value.inspect}. Use #{directives.map(&:inspect).join " or "}.)
55
64
  end
56
65
 
66
+ private
67
+
57
68
  def visit &block
58
69
  increment
59
70
  instance_eval(&block) if block
@@ -64,7 +75,5 @@ module Containable
64
75
  def increment = self.depth += 1
65
76
 
66
77
  def decrement = self.depth -= 1
67
-
68
- def namespacify(key) = keys[..depth].append(key).join separator
69
78
  end
70
79
  end
@@ -18,7 +18,7 @@ module Containable
18
18
  process key, value, as
19
19
  end
20
20
 
21
- private
21
+ protected
22
22
 
23
23
  attr_reader :dependencies
24
24
 
data/lib/containable.rb CHANGED
@@ -12,14 +12,4 @@ module Containable
12
12
  end
13
13
 
14
14
  def self.[](register: Register, resolver: Resolver) = Builder.new(register:, resolver:)
15
-
16
- def stub!(**)
17
- require "containable/test"
18
-
19
- extend Test
20
-
21
- stub(**)
22
- end
23
-
24
- def restore = false
25
15
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: containable
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brooke Kuhlmann
@@ -90,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
90
  - !ruby/object:Gem::Version
91
91
  version: '0'
92
92
  requirements: []
93
- rubygems_version: 4.0.10
93
+ rubygems_version: 4.0.12
94
94
  specification_version: 4
95
95
  summary: A thread-safe dependency injection container.
96
96
  test_files: []
metadata.gz.sig CHANGED
Binary file