inoculate 0.1.0 → 0.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
- data/CHANGELOG.md +11 -2
- data/README.md +51 -0
- data/lib/inoculate/configurer.rb +12 -2
- data/lib/inoculate/initialization.rb +0 -2
- data/lib/inoculate/manufacturer.rb +66 -31
- data/lib/inoculate/porter.rb +4 -1
- data/lib/inoculate/version.rb +1 -1
- data/sig/inoculate.rbs +6 -9
- 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: 2649bf406a68234451cc147c069a8b9d58ce91d2a1538e728a532ff4abda2180
|
4
|
+
data.tar.gz: 3bf808d9e35467647432ed7d74cab98e669a41f7e887e889735c8d583844eaf7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ff27ab41b2f2da2cc6ebf22e434c88b626b9b0c2bf6dab505a9baa1a777faf82c60512a44bb258e19bf0ceb71315b0f0123ab724d6dcb65dfb0468d3d9cf7b35
|
7
|
+
data.tar.gz: 23212f8eeb9dc79c159651ebebc93d4ca13ad7623aa2d85622fe8de7b64f993880db5837e51a072f9116bffc169f02030b0676247f1befb254e86023443436ed
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,17 @@
|
|
1
|
-
## v0.
|
1
|
+
## v0.3.0 \[2022-10-11]
|
2
|
+
|
3
|
+
* Add instance dependency registration.
|
4
|
+
* Only allow blocks (and Procs) as dependency factories.
|
5
|
+
|
6
|
+
## v0.2.0 \[2022-10-11]
|
7
|
+
|
8
|
+
* Build modules at initialization time so that usage is always thread-safe.
|
9
|
+
|
10
|
+
## v0.1.0 \[2022-10-10]
|
2
11
|
|
3
12
|
* Add transient dependency registration.
|
4
13
|
* Add initialization process for gem.
|
5
14
|
|
6
|
-
## v0.0.0
|
15
|
+
## v0.0.0 \[2022-10-05]
|
7
16
|
|
8
17
|
* Initial gem setup.
|
data/README.md
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
Inoculate is a small, thread-safe dependency injection library configured entirely with Ruby.
|
4
4
|
It provides several life-cycles and provides dependency access through private accessors.
|
5
5
|
|
6
|
+
[](https://badge.fury.io/rb/inoculate)
|
7
|
+
|
6
8
|
---
|
7
9
|
|
8
10
|
## What's in the box?
|
@@ -22,6 +24,7 @@ It provides several life-cycles and provides dependency access through private a
|
|
22
24
|
2. [Usage](#usage)
|
23
25
|
1. [Dependency Life Cycles](#dependency-life-cycles)
|
24
26
|
1. [Transient](#transient)
|
27
|
+
2. [Instance](#instance)
|
25
28
|
2. [Renaming the Declaration API](#renaming-the-declaration-api)
|
26
29
|
3. [Hide Your Dependency on Inoculate](#hide-your-dependency-on-inoculate)
|
27
30
|
3. [Installation](#installation)
|
@@ -107,6 +110,54 @@ Count is: 0
|
|
107
110
|
=> nil
|
108
111
|
```
|
109
112
|
|
113
|
+
#### Instance
|
114
|
+
Instance dependencies are constructed once for each instance of a dependent class.
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
class Counter
|
118
|
+
attr_reader :count
|
119
|
+
|
120
|
+
def initialize
|
121
|
+
@count = 0
|
122
|
+
end
|
123
|
+
|
124
|
+
def inc
|
125
|
+
@count += 1
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
Inoculate.initialize do |config|
|
130
|
+
config.instance(:counter) { Counter.new }
|
131
|
+
end
|
132
|
+
|
133
|
+
class Example
|
134
|
+
include Inoculate::Porter
|
135
|
+
inoculate_with :counter
|
136
|
+
|
137
|
+
def initialize(name)
|
138
|
+
@name = name
|
139
|
+
end
|
140
|
+
|
141
|
+
def to_s
|
142
|
+
counter.inc
|
143
|
+
"[#{@name}] Count is: #{counter.count}"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
a = Example.new("a")
|
148
|
+
b = Example.new("b")
|
149
|
+
puts a, a, b
|
150
|
+
```
|
151
|
+
|
152
|
+
This results in:
|
153
|
+
|
154
|
+
```
|
155
|
+
[a] Count is: 1
|
156
|
+
[a] Count is: 2
|
157
|
+
[b] Count is: 1
|
158
|
+
=> nil
|
159
|
+
```
|
160
|
+
|
110
161
|
### Renaming the Declaration API
|
111
162
|
The `inoculate_with` API is named to avoid immediate collisions with other modules
|
112
163
|
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
@@ -13,8 +13,18 @@ module Inoculate
|
|
13
13
|
# @see Manufacturer#transient
|
14
14
|
#
|
15
15
|
# @since 0.1.0
|
16
|
-
def transient(name,
|
17
|
-
manufacturer.transient(name,
|
16
|
+
def transient(name, &block)
|
17
|
+
manufacturer.transient(name, &block)
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
# Register an instance dependency.
|
22
|
+
# @see Manufacturer#instance
|
23
|
+
#
|
24
|
+
# @since 0.3.0
|
25
|
+
def instance(name, &block)
|
26
|
+
manufacturer.instance(name, &block)
|
27
|
+
nil
|
18
28
|
end
|
19
29
|
|
20
30
|
private
|
@@ -7,8 +7,6 @@
|
|
7
7
|
module Inoculate
|
8
8
|
# The main configuration entrypoint for Inoculate. Use this to set up named dependencies.
|
9
9
|
#
|
10
|
-
# @note This method is not thread safe.
|
11
|
-
#
|
12
10
|
# @example A simple dependency configuration
|
13
11
|
# Inoculate.initialize do |config|
|
14
12
|
# config.transient(:http) { Faraday }
|
@@ -4,7 +4,6 @@ require "digest"
|
|
4
4
|
|
5
5
|
module Inoculate
|
6
6
|
# Registers and builds dependency injection modules.
|
7
|
-
# @todo building needs to be thread-safe
|
8
7
|
# @todo singleton life cycle
|
9
8
|
# @todo instance life cycle
|
10
9
|
# @todo thread singleton life cycle
|
@@ -29,58 +28,94 @@ module Inoculate
|
|
29
28
|
# manufacturer.transient(:sha1_hasher) { Digest::SHA1.new }
|
30
29
|
#
|
31
30
|
# @example With a Proc
|
32
|
-
# manufacturer.transient(:sha1_hasher,
|
33
|
-
#
|
34
|
-
# @example With anything Callable
|
35
|
-
# class HashingBuilder
|
36
|
-
# def call = Digest::SHA1.new
|
37
|
-
# end
|
38
|
-
# manufacturer.transient(:sha1_hasher, HashingBuilder.new)
|
31
|
+
# manufacturer.transient(:sha1_hasher, &-> { Digest::SHA1.new })
|
39
32
|
#
|
40
33
|
# @param name [Symbol, #to_sym] the dependency name which will be used to access it
|
41
|
-
# @param
|
42
|
-
# @param block [Proc, nil] an alternative builder callable
|
34
|
+
# @param block [Block, Proc] a factory method to build the dependency
|
43
35
|
#
|
44
|
-
# @raise [Errors::RequiresCallable] if no
|
36
|
+
# @raise [Errors::RequiresCallable] if no block is provided
|
45
37
|
# @raise [Errors::InvalidName] if the name is not a symbol, cannot be converted to a symbol,
|
46
38
|
# or is not a valid attribute name
|
47
39
|
# @raise [Errors::AlreadyRegistered] if the name has been registered previously
|
48
40
|
#
|
49
41
|
# @since 0.1.0
|
50
|
-
def transient(name,
|
51
|
-
|
42
|
+
def transient(name, &block)
|
43
|
+
validate_dependency_name name
|
52
44
|
raise Errors::AlreadyRegistered if @registered_blueprints.has_key? name
|
53
|
-
raise Errors::RequiresCallable
|
45
|
+
raise Errors::RequiresCallable if block.nil?
|
54
46
|
|
55
|
-
|
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
|
+
}
|
56
53
|
end
|
57
54
|
|
58
|
-
#
|
55
|
+
# Register an instance dependency.
|
56
|
+
#
|
57
|
+
# An instance dependency gets created once for each instance of a dependent class.
|
59
58
|
#
|
60
|
-
# @
|
59
|
+
# @example With a block
|
60
|
+
# manufacturer.instance(:sha1_hasher) { Digest::SHA1.new }
|
61
61
|
#
|
62
|
-
# @
|
62
|
+
# @example With a Proc
|
63
|
+
# manufacturer.instance(:sha1_hasher, &-> { Digest::SHA1.new })
|
63
64
|
#
|
64
|
-
# @
|
65
|
+
# @param name [Symbol, #to_sym] the dependency name which will be used to access it
|
66
|
+
# @param block [Block, Proc] a factory method to build the dependency
|
65
67
|
#
|
66
|
-
# @
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
68
|
+
# @raise [Errors::RequiresCallable] if no block is provided
|
69
|
+
# @raise [Errors::InvalidName] if the name is not a symbol, cannot be converted to a symbol,
|
70
|
+
# or is not a valid attribute name
|
71
|
+
# @raise [Errors::AlreadyRegistered] if the name has been registered previously
|
72
|
+
#
|
73
|
+
# @since 0.3.0
|
74
|
+
def instance(name, &block)
|
75
|
+
validate_dependency_name name
|
76
|
+
raise Errors::AlreadyRegistered if @registered_blueprints.has_key? name
|
77
|
+
raise Errors::RequiresCallable if block.nil?
|
71
78
|
|
79
|
+
blueprint_name = name.to_sym
|
80
|
+
@registered_blueprints[blueprint_name] = {
|
81
|
+
lifecycle: :instance,
|
82
|
+
factory: block,
|
83
|
+
accessor_module: build_module(blueprint_name, :instance, block)
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def build_module(name, lifecycle, factory)
|
72
90
|
module_name = "I#{Digest::SHA1.hexdigest(name.to_s)}"
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
91
|
+
module_body =
|
92
|
+
case lifecycle
|
93
|
+
when :transient then build_transient(name, factory)
|
94
|
+
when :instance then build_instance(name, factory)
|
95
|
+
else raise ArgumentError, "Life cycle #{lifecycle} is not valid. Something has gone very wrong."
|
96
|
+
end
|
97
|
+
|
98
|
+
Providers.module_eval { const_set(module_name, module_body) }
|
99
|
+
end
|
100
|
+
|
101
|
+
def build_transient(name, factory)
|
102
|
+
Module.new do
|
103
|
+
private define_method(name) { factory.call }
|
78
104
|
end
|
79
105
|
end
|
80
106
|
|
81
|
-
|
107
|
+
def build_instance(name, factory)
|
108
|
+
cache_variable_name = "@icache_#{Digest::SHA1.hexdigest(name.to_s)}"
|
109
|
+
Module.new do
|
110
|
+
define_method(name) do
|
111
|
+
instance_variable_set(cache_variable_name, factory.call) unless instance_variable_defined?(cache_variable_name)
|
112
|
+
instance_variable_get(cache_variable_name)
|
113
|
+
end
|
114
|
+
private name
|
115
|
+
end
|
116
|
+
end
|
82
117
|
|
83
|
-
def
|
118
|
+
def validate_dependency_name(name)
|
84
119
|
raise Errors::InvalidName, "name must be a symbol or convert to one" unless name.respond_to? :to_sym
|
85
120
|
begin
|
86
121
|
Module.new { attr_reader name }
|
data/lib/inoculate/porter.rb
CHANGED
@@ -37,7 +37,10 @@ module Inoculate
|
|
37
37
|
m = Module.new do
|
38
38
|
define_method(method_name) do |*names|
|
39
39
|
names.each do |name|
|
40
|
-
|
40
|
+
mod = Inoculate.manufacturer.registered_blueprints.dig(name, :accessor_module)
|
41
|
+
raise Errors::UnknownName if mod.nil?
|
42
|
+
|
43
|
+
include(mod)
|
41
44
|
end
|
42
45
|
end
|
43
46
|
end
|
data/lib/inoculate/version.rb
CHANGED
data/sig/inoculate.rbs
CHANGED
@@ -6,9 +6,9 @@ module Inoculate
|
|
6
6
|
end
|
7
7
|
|
8
8
|
type callable = ^() -> untyped
|
9
|
-
type lifecycle_name = :transient
|
9
|
+
type lifecycle_name = :transient | :instance
|
10
10
|
type builder_name = Symbol
|
11
|
-
type blueprint = { lifecycle: lifecycle_name,
|
11
|
+
type blueprint = { lifecycle: lifecycle_name, factory: callable, accessor_module: Module? }
|
12
12
|
|
13
13
|
module Errors
|
14
14
|
class Error < StandardError
|
@@ -32,18 +32,15 @@ module Inoculate
|
|
32
32
|
|
33
33
|
class Manufacturer
|
34
34
|
attr_reader registered_blueprints: Hash[builder_name, blueprint]
|
35
|
-
def transient: (builder_name | _ToSymbol
|
36
|
-
def
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def validate_builder_name: (builder_name) -> void
|
35
|
+
def transient: (builder_name | _ToSymbol) { () -> void } -> void
|
36
|
+
def instance: (builder_name | _ToSymbol) { () -> void } -> void
|
41
37
|
end
|
42
38
|
|
43
39
|
class Configurer
|
44
40
|
def initialize: (Manufacturer) -> void
|
45
41
|
|
46
|
-
def transient: (builder_name | _ToSymbol
|
42
|
+
def transient: (builder_name | _ToSymbol) { () -> void } -> nil
|
43
|
+
def instance: (builder_name | _ToSymbol) { () -> void } -> nil
|
47
44
|
|
48
45
|
private
|
49
46
|
|
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.3.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-12 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.
|