inoculate 0.3.0 → 0.4.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: 2649bf406a68234451cc147c069a8b9d58ce91d2a1538e728a532ff4abda2180
4
- data.tar.gz: 3bf808d9e35467647432ed7d74cab98e669a41f7e887e889735c8d583844eaf7
3
+ metadata.gz: 7bd8610b9bbde1304734505a64fdf22c8665961e3b02d93171ac4fc6ccf313aa
4
+ data.tar.gz: 66ee174fa4bd02dfd671a39a946a8343da1d63bb88f9be05ee94816d033a5e4e
5
5
  SHA512:
6
- metadata.gz: ff27ab41b2f2da2cc6ebf22e434c88b626b9b0c2bf6dab505a9baa1a777faf82c60512a44bb258e19bf0ceb71315b0f0123ab724d6dcb65dfb0468d3d9cf7b35
7
- data.tar.gz: 23212f8eeb9dc79c159651ebebc93d4ca13ad7623aa2d85622fe8de7b64f993880db5837e51a072f9116bffc169f02030b0676247f1befb254e86023443436ed
6
+ metadata.gz: bbc2655324e68cd2ad977192b81f9cbef0d63b72da91f05521c59eb91f1d58993da68dbcd8d4f6b69be5e5d93708e847f567955f80cb042761e187b64c1bcdbc
7
+ data.tar.gz: ef316f9b6e6dfa9cd42811f8af5082f402d0c03478a280a2728b61ce9c706a9e0b1cd0249921d18818cfa85b28ccf81969a1330744c3d8d19e95e5fd4f73a81c
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## v0.4.0 \[Unreleased]
2
+
3
+ * Add singleton dependency registration.
4
+
1
5
  ## v0.3.0 \[2022-10-11]
2
6
 
3
7
  * Add instance dependency registration.
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
- puts Example.new
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,54 @@ 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
+
161
213
  ### Renaming the Declaration API
162
214
  The `inoculate_with` API is named to avoid immediate collisions with other modules
163
215
  and code you may have. You can use it as-is, or rename it to something you see fit
@@ -27,6 +27,15 @@ 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
+
30
39
  private
31
40
 
32
41
  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
- validate_dependency_name name
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,54 @@ 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
+ private
92
+
93
+ def register_blueprint(name, lifecycle, &block)
75
94
  validate_dependency_name name
76
95
  raise Errors::AlreadyRegistered if @registered_blueprints.has_key? name
77
96
  raise Errors::RequiresCallable if block.nil?
78
97
 
79
98
  blueprint_name = name.to_sym
80
99
  @registered_blueprints[blueprint_name] = {
81
- lifecycle: :instance,
100
+ lifecycle: lifecycle,
82
101
  factory: block,
83
- accessor_module: build_module(blueprint_name, :instance, block)
102
+ accessor_module: build_module(blueprint_name, lifecycle, block)
84
103
  }
85
104
  end
86
105
 
87
- private
88
-
89
106
  def build_module(name, lifecycle, factory)
90
- module_name = "I#{Digest::SHA1.hexdigest(name.to_s)}"
107
+ module_name = "I#{hash_name(name)}"
91
108
  module_body =
92
109
  case lifecycle
93
110
  when :transient then build_transient(name, factory)
94
111
  when :instance then build_instance(name, factory)
112
+ when :singleton then build_singleton(name, factory)
95
113
  else raise ArgumentError, "Life cycle #{lifecycle} is not valid. Something has gone very wrong."
96
114
  end
97
115
 
@@ -105,7 +123,7 @@ module Inoculate
105
123
  end
106
124
 
107
125
  def build_instance(name, factory)
108
- cache_variable_name = "@icache_#{Digest::SHA1.hexdigest(name.to_s)}"
126
+ cache_variable_name = "@icache_#{hash_name(name)}"
109
127
  Module.new do
110
128
  define_method(name) do
111
129
  instance_variable_set(cache_variable_name, factory.call) unless instance_variable_defined?(cache_variable_name)
@@ -115,6 +133,17 @@ module Inoculate
115
133
  end
116
134
  end
117
135
 
136
+ def build_singleton(name, factory)
137
+ cache_variable_name = "@@icache_#{hash_name(name)}"
138
+ Module.new do |mod|
139
+ define_method(name) do
140
+ mod.class_variable_set(cache_variable_name, factory.call) unless mod.class_variable_defined?(cache_variable_name)
141
+ mod.class_variable_get(cache_variable_name)
142
+ end
143
+ private name
144
+ end
145
+ end
146
+
118
147
  def validate_dependency_name(name)
119
148
  raise Errors::InvalidName, "name must be a symbol or convert to one" unless name.respond_to? :to_sym
120
149
  begin
@@ -123,5 +152,9 @@ module Inoculate
123
152
  raise Errors::InvalidName, "name must be a valid attr_reader"
124
153
  end
125
154
  end
155
+
156
+ def hash_name(name)
157
+ Digest::SHA1.hexdigest(name.to_s)
158
+ end
126
159
  end
127
160
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Inoculate
4
4
  # The library version.
5
- VERSION = "0.3.0"
5
+ VERSION = "0.4.0"
6
6
  end
data/sig/inoculate.rbs CHANGED
@@ -34,6 +34,7 @@ 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
37
38
  end
38
39
 
39
40
  class Configurer
@@ -41,6 +42,7 @@ module Inoculate
41
42
 
42
43
  def transient: (builder_name | _ToSymbol) { () -> void } -> nil
43
44
  def instance: (builder_name | _ToSymbol) { () -> void } -> nil
45
+ def singleton: (builder_name | _ToSymbol) { () -> void } -> nil
44
46
 
45
47
  private
46
48
 
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.3.0
4
+ version: 0.4.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-12 00:00:00.000000000 Z
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.