inoculate 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e63449f9ee05019e70acf85faafd0f2adf6554704f560de31b66b12c01e54aeb
4
- data.tar.gz: e6975710a5802d37bd4b84de1345dbd10a10a5cf8538096cd8ffea8459997af9
3
+ metadata.gz: 2649bf406a68234451cc147c069a8b9d58ce91d2a1538e728a532ff4abda2180
4
+ data.tar.gz: 3bf808d9e35467647432ed7d74cab98e669a41f7e887e889735c8d583844eaf7
5
5
  SHA512:
6
- metadata.gz: 95e7e8d09278118af12987096c2919419a66b6ea298d6556a3e7df113403e0fa4edd363a1d19172eef8d4739ffdf10b4e8180b19151cdd19361d3c2ccd861200
7
- data.tar.gz: 9dd927feda03995336037f58b86e9c97be8e2bf83f4ac8a101ee67b10e0d40106bdf0f62a138ef046135ef24f759074c289ef61153c8044d02db08b63e4b20be
6
+ metadata.gz: ff27ab41b2f2da2cc6ebf22e434c88b626b9b0c2bf6dab505a9baa1a777faf82c60512a44bb258e19bf0ceb71315b0f0123ab724d6dcb65dfb0468d3d9cf7b35
7
+ data.tar.gz: 23212f8eeb9dc79c159651ebebc93d4ca13ad7623aa2d85622fe8de7b64f993880db5837e51a072f9116bffc169f02030b0676247f1befb254e86023443436ed
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## v0.3.0 \[2022-10-11]
2
+
3
+ * Add instance dependency registration.
4
+ * Only allow blocks (and Procs) as dependency factories.
5
+
1
6
  ## v0.2.0 \[2022-10-11]
2
7
 
3
8
  * Build modules at initialization time so that usage is always thread-safe.
data/README.md CHANGED
@@ -24,6 +24,7 @@ It provides several life-cycles and provides dependency access through private a
24
24
  2. [Usage](#usage)
25
25
  1. [Dependency Life Cycles](#dependency-life-cycles)
26
26
  1. [Transient](#transient)
27
+ 2. [Instance](#instance)
27
28
  2. [Renaming the Declaration API](#renaming-the-declaration-api)
28
29
  3. [Hide Your Dependency on Inoculate](#hide-your-dependency-on-inoculate)
29
30
  3. [Installation](#installation)
@@ -109,6 +110,54 @@ Count is: 0
109
110
  => nil
110
111
  ```
111
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
+
112
161
  ### Renaming the Declaration API
113
162
  The `inoculate_with` API is named to avoid immediate collisions with other modules
114
163
  and code you may have. You can use it as-is, or rename it to something you see fit
@@ -13,8 +13,18 @@ module Inoculate
13
13
  # @see Manufacturer#transient
14
14
  #
15
15
  # @since 0.1.0
16
- def transient(name, builder = nil, &block)
17
- manufacturer.transient(name, builder, &block)
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
@@ -28,49 +28,94 @@ module Inoculate
28
28
  # manufacturer.transient(:sha1_hasher) { Digest::SHA1.new }
29
29
  #
30
30
  # @example With a Proc
31
- # manufacturer.transient(:sha1_hasher, -> { Digest::SHA1.new })
32
- #
33
- # @example With anything Callable
34
- # class HashingBuilder
35
- # def call = Digest::SHA1.new
36
- # end
37
- # manufacturer.transient(:sha1_hasher, HashingBuilder.new)
31
+ # manufacturer.transient(:sha1_hasher, &-> { Digest::SHA1.new })
38
32
  #
39
33
  # @param name [Symbol, #to_sym] the dependency name which will be used to access it
40
- # @param builder [#call] the callable to build the dependency
41
- # @param block [Proc, nil] an alternative builder callable
34
+ # @param block [Block, Proc] a factory method to build the dependency
42
35
  #
43
- # @raise [Errors::RequiresCallable] if no builder or block is provided
36
+ # @raise [Errors::RequiresCallable] if no block is provided
44
37
  # @raise [Errors::InvalidName] if the name is not a symbol, cannot be converted to a symbol,
45
38
  # or is not a valid attribute name
46
39
  # @raise [Errors::AlreadyRegistered] if the name has been registered previously
47
40
  #
48
41
  # @since 0.1.0
49
- def transient(name, builder = nil, &block)
50
- validate_builder_name name
42
+ def transient(name, &block)
43
+ validate_dependency_name name
51
44
  raise Errors::AlreadyRegistered if @registered_blueprints.has_key? name
52
- raise Errors::RequiresCallable unless builder.respond_to?(:call) || block
45
+ raise Errors::RequiresCallable if block.nil?
53
46
 
54
47
  blueprint_name = name.to_sym
55
48
  @registered_blueprints[blueprint_name] = {
56
49
  lifecycle: :transient,
57
- builder: builder || block,
58
- accessor_module: build_module(blueprint_name, :transient, builder || block)
50
+ factory: block,
51
+ accessor_module: build_module(blueprint_name, :transient, block)
52
+ }
53
+ end
54
+
55
+ # Register an instance dependency.
56
+ #
57
+ # An instance dependency gets created once for each instance of a dependent class.
58
+ #
59
+ # @example With a block
60
+ # manufacturer.instance(:sha1_hasher) { Digest::SHA1.new }
61
+ #
62
+ # @example With a Proc
63
+ # manufacturer.instance(:sha1_hasher, &-> { Digest::SHA1.new })
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
67
+ #
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?
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)
59
84
  }
60
85
  end
61
86
 
62
87
  private
63
88
 
64
- def build_module(name, lifecycle, builder)
89
+ def build_module(name, lifecycle, factory)
65
90
  module_name = "I#{Digest::SHA1.hexdigest(name.to_s)}"
66
- Providers.module_eval do
67
- const_set(module_name, Module.new do
68
- private define_method(name) { builder.call }
69
- end)
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 }
104
+ end
105
+ end
106
+
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
70
115
  end
71
116
  end
72
117
 
73
- def validate_builder_name(name)
118
+ def validate_dependency_name(name)
74
119
  raise Errors::InvalidName, "name must be a symbol or convert to one" unless name.respond_to? :to_sym
75
120
  begin
76
121
  Module.new { attr_reader name }
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Inoculate
4
4
  # The library version.
5
- VERSION = "0.2.0"
5
+ VERSION = "0.3.0"
6
6
  end
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, builder: callable, accessor_module: Module? }
11
+ type blueprint = { lifecycle: lifecycle_name, factory: callable, accessor_module: Module? }
12
12
 
13
13
  module Errors
14
14
  class Error < StandardError
@@ -32,13 +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, callable?) ?{ () -> void } -> void
35
+ def transient: (builder_name | _ToSymbol) { () -> void } -> void
36
+ def instance: (builder_name | _ToSymbol) { () -> void } -> void
36
37
  end
37
38
 
38
39
  class Configurer
39
40
  def initialize: (Manufacturer) -> void
40
41
 
41
- def transient: (builder_name | _ToSymbol, callable?) -> void
42
+ def transient: (builder_name | _ToSymbol) { () -> void } -> nil
43
+ def instance: (builder_name | _ToSymbol) { () -> void } -> nil
42
44
 
43
45
  private
44
46
 
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.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephan Tarulli