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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/README.adoc +119 -9
- data/containable.gemspec +1 -1
- data/lib/containable/builder.rb +24 -1
- data/lib/containable/register.rb +12 -3
- data/lib/containable/resolver.rb +1 -1
- data/lib/containable.rb +0 -10
- data.tar.gz.sig +0 -0
- metadata +2 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7c5be0b5f9d940c754f77ded0f06992cb9d1dc13e45ec055ccde1a877fbe1782
|
|
4
|
+
data.tar.gz: 156d209e8b763fe0f995fa19bb709282537c2122c1a54e05c167e082f052055d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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.
|
|
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
|
-
|
|
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
data/lib/containable/builder.rb
CHANGED
|
@@ -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
|
|
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
|
data/lib/containable/register.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
data/lib/containable/resolver.rb
CHANGED
data/lib/containable.rb
CHANGED
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.
|
|
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.
|
|
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
|