wirer 0.4.7 → 0.5.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.
- data/lib/wirer/construction_session.rb +123 -0
- data/lib/wirer/container.rb +9 -118
- data/lib/wirer/factory/curried_dependencies.rb +8 -3
- data/lib/wirer/factory/interface.rb +2 -2
- data/lib/wirer/version.rb +1 -1
- data/lib/wirer.rb +1 -0
- metadata +14 -11
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'thread'
|
2
|
+
module Wirer
|
3
|
+
class ConstructionSession
|
4
|
+
attr_reader :container
|
5
|
+
|
6
|
+
def initialize(container)
|
7
|
+
@construction_mutex = Mutex.new
|
8
|
+
@container = container
|
9
|
+
end
|
10
|
+
|
11
|
+
# N.B. all calls to the private construct_ methods below must be wrapped with
|
12
|
+
# this at the top-level entry point.
|
13
|
+
# It wraps with a mutex, to avoid race conditions with concurrent
|
14
|
+
# attempts to construct a singleton factory, and also ensures that everything
|
15
|
+
# constructed in this session gets post_initialized at the end.
|
16
|
+
def construction_session
|
17
|
+
@construction_mutex.synchronize do
|
18
|
+
begin
|
19
|
+
@phase_1_in_progress = []
|
20
|
+
@queued_for_phase_2 = []
|
21
|
+
@queued_for_post_initialize = []
|
22
|
+
|
23
|
+
result = yield
|
24
|
+
|
25
|
+
until @queued_for_phase_2.empty?
|
26
|
+
factory_instance = @queued_for_phase_2.pop
|
27
|
+
construct_and_inject_setter_dependencies(*factory_instance)
|
28
|
+
@queued_for_post_initialize.push(factory_instance)
|
29
|
+
end
|
30
|
+
|
31
|
+
result
|
32
|
+
ensure
|
33
|
+
post_initialize(@queued_for_post_initialize)
|
34
|
+
remove_instance_variable(:@phase_1_in_progress)
|
35
|
+
remove_instance_variable(:@queued_for_post_initialize)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def construct_dependencies(dependencies)
|
41
|
+
result = {}
|
42
|
+
dependencies.each do |arg_name, dependency|
|
43
|
+
result[arg_name] = construct_dependency(dependency)
|
44
|
+
end
|
45
|
+
result
|
46
|
+
end
|
47
|
+
|
48
|
+
def construct_dependency(dependency)
|
49
|
+
dependency.match_factories(@container.factories) do |factory|
|
50
|
+
if dependency.factory?
|
51
|
+
if @container.singleton_factories_instances.has_key?(factory)
|
52
|
+
raise Error, "Problem with :factory => true dependency: #{factory} was added to the container as a singleton, so only a singleton instance can be supplied, not a wrapped factory"
|
53
|
+
end
|
54
|
+
curry_factory_with_constructed_dependencies(factory)
|
55
|
+
else
|
56
|
+
construct_factory_without_args(factory)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def construct_factory(factory, *args, &block_arg)
|
62
|
+
if args.empty? && !block_arg
|
63
|
+
construct_factory_without_args(factory)
|
64
|
+
else
|
65
|
+
construct_factory_with_args(factory, *args, &block_arg)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def construct_factory_without_args(factory)
|
70
|
+
instance = @container.singleton_factories_instances[factory] and return instance
|
71
|
+
|
72
|
+
if @phase_1_in_progress.include?(factory)
|
73
|
+
cycle = @phase_1_in_progress[@phase_1_in_progress.index(factory)..-1] + [factory]
|
74
|
+
raise CyclicDependencyError, "Cyclic constructor dependencies. Break the cycle by changing some into setter dependencies:\n#{cycle.map(&:inspect).join("\n")}"
|
75
|
+
end
|
76
|
+
@phase_1_in_progress.push(factory)
|
77
|
+
result = construct_with_constructor_dependencies(factory)
|
78
|
+
@phase_1_in_progress.pop
|
79
|
+
|
80
|
+
if @container.singleton_factories_instances.has_key?(factory)
|
81
|
+
@container.singleton_factories_instances[factory] = result
|
82
|
+
end
|
83
|
+
|
84
|
+
@queued_for_phase_2.push([factory, result])
|
85
|
+
result
|
86
|
+
end
|
87
|
+
|
88
|
+
def construct_factory_with_args(factory, *args, &block_arg)
|
89
|
+
result = construct_with_constructor_dependencies(factory, *args, &block_arg)
|
90
|
+
@queued_for_phase_2.push([factory, result])
|
91
|
+
result
|
92
|
+
end
|
93
|
+
|
94
|
+
def construct_with_constructor_dependencies(factory, *args, &block_arg)
|
95
|
+
deps = construct_dependencies(factory.constructor_dependencies)
|
96
|
+
begin
|
97
|
+
factory.new_from_dependencies(deps, *args, &block_arg)
|
98
|
+
rescue Wirer::Error
|
99
|
+
raise
|
100
|
+
rescue => e
|
101
|
+
wrapped = DependencyConstructionError.new("Unable to construct factory: #{factory.inspect}", e)
|
102
|
+
raise wrapped
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def curry_factory_with_constructed_dependencies(factory)
|
107
|
+
deps = construct_dependencies(factory.constructor_dependencies)
|
108
|
+
factory.curry_with_dependencies(deps, self)
|
109
|
+
end
|
110
|
+
|
111
|
+
def construct_and_inject_setter_dependencies(factory, instance)
|
112
|
+
setter_deps = construct_dependencies(factory.setter_dependencies(instance))
|
113
|
+
setter_deps.each do |dep_name, dep|
|
114
|
+
factory.inject_dependency(instance, dep_name, dep)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def post_initialize(factories_instances)
|
119
|
+
factories_instances.each {|factory, instance| factory.post_initialize(instance)}
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
data/lib/wirer/container.rb
CHANGED
@@ -11,12 +11,14 @@ module Wirer
|
|
11
11
|
# Alternatively if don't want this, specify :singleton => false when adding it.
|
12
12
|
class Container
|
13
13
|
attr_reader :factories
|
14
|
+
attr_reader :factories_by_method_name
|
15
|
+
attr_reader :singleton_factories_instances
|
14
16
|
|
15
17
|
def initialize
|
16
18
|
@singleton_factories_instances = {}
|
17
19
|
@factories = []
|
18
20
|
@factories_by_method_name = {}
|
19
|
-
@
|
21
|
+
@construction_session = ConstructionSession.new(self)
|
20
22
|
yield self if block_given?
|
21
23
|
end
|
22
24
|
|
@@ -131,17 +133,18 @@ module Wirer
|
|
131
133
|
def construct_factory_by_method_name(method_name, *args, &block_arg)
|
132
134
|
factory = @factories_by_method_name[method_name]
|
133
135
|
begin
|
134
|
-
construction_session do
|
135
|
-
construct_factory(factory, *args, &block_arg)
|
136
|
+
@construction_session.construction_session do
|
137
|
+
@construction_session.construct_factory(factory, *args, &block_arg)
|
136
138
|
end
|
137
|
-
rescue
|
139
|
+
rescue => exception
|
140
|
+
exception.backtrace
|
138
141
|
raise DependencyConstructionError.new("Unable to construct factory with name #{method_name}", $!)
|
139
142
|
end
|
140
143
|
end
|
141
144
|
|
142
145
|
def [](*dep_args)
|
143
|
-
construction_session do
|
144
|
-
construct_dependency(Dependency.new_from_args(*dep_args))
|
146
|
+
@construction_session.construction_session do
|
147
|
+
@construction_session.construct_dependency(Dependency.new_from_args(*dep_args))
|
145
148
|
end
|
146
149
|
end
|
147
150
|
|
@@ -167,117 +170,5 @@ module Wirer
|
|
167
170
|
end
|
168
171
|
end
|
169
172
|
|
170
|
-
private
|
171
|
-
|
172
|
-
# N.B. all calls to the private construct_ methods below must be wrapped with
|
173
|
-
# this at the top-level entry point.
|
174
|
-
# It wraps with a mutex, to avoid race conditions with concurrent
|
175
|
-
# attempts to construct a singleton factory, and also ensures that everything
|
176
|
-
# constructed in this session gets post_initialized at the end.
|
177
|
-
def construction_session
|
178
|
-
@construction_mutex.synchronize do
|
179
|
-
begin
|
180
|
-
@phase_1_in_progress = []
|
181
|
-
@queued_for_phase_2 = []
|
182
|
-
@queued_for_post_initialize = []
|
183
|
-
|
184
|
-
result = yield
|
185
|
-
|
186
|
-
until @queued_for_phase_2.empty?
|
187
|
-
factory_instance = @queued_for_phase_2.pop
|
188
|
-
construct_and_inject_setter_dependencies(*factory_instance)
|
189
|
-
@queued_for_post_initialize.push(factory_instance)
|
190
|
-
end
|
191
|
-
|
192
|
-
result
|
193
|
-
ensure
|
194
|
-
post_initialize(@queued_for_post_initialize)
|
195
|
-
remove_instance_variable(:@phase_1_in_progress)
|
196
|
-
remove_instance_variable(:@queued_for_post_initialize)
|
197
|
-
end
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
def construct_dependencies(dependencies)
|
202
|
-
result = {}
|
203
|
-
dependencies.each do |arg_name, dependency|
|
204
|
-
result[arg_name] = construct_dependency(dependency)
|
205
|
-
end
|
206
|
-
result
|
207
|
-
end
|
208
|
-
|
209
|
-
def construct_dependency(dependency)
|
210
|
-
dependency.match_factories(@factories) do |factory|
|
211
|
-
if dependency.factory?
|
212
|
-
if @singleton_factories_instances.has_key?(factory)
|
213
|
-
raise Error, "Problem with :factory => true dependency: #{factory} was added to the container as a singleton, so only a singleton instance can be supplied, not a wrapped factory"
|
214
|
-
end
|
215
|
-
curry_factory_with_constructed_dependencies(factory)
|
216
|
-
else
|
217
|
-
construct_factory_without_args(factory)
|
218
|
-
end
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
def construct_factory(factory, *args, &block_arg)
|
223
|
-
if args.empty? && !block_arg
|
224
|
-
construct_factory_without_args(factory)
|
225
|
-
else
|
226
|
-
construct_factory_with_args(factory, *args, &block_arg)
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
def construct_factory_without_args(factory)
|
231
|
-
instance = @singleton_factories_instances[factory] and return instance
|
232
|
-
|
233
|
-
if @phase_1_in_progress.include?(factory)
|
234
|
-
cycle = @phase_1_in_progress[@phase_1_in_progress.index(factory)..-1] + [factory]
|
235
|
-
raise CyclicDependencyError, "Cyclic constructor dependencies. Break the cycle by changing some into setter dependencies:\n#{cycle.map(&:inspect).join("\n")}"
|
236
|
-
end
|
237
|
-
@phase_1_in_progress.push(factory)
|
238
|
-
result = construct_with_constructor_dependencies(factory)
|
239
|
-
@phase_1_in_progress.pop
|
240
|
-
|
241
|
-
if @singleton_factories_instances.has_key?(factory)
|
242
|
-
@singleton_factories_instances[factory] = result
|
243
|
-
end
|
244
|
-
|
245
|
-
@queued_for_phase_2.push([factory, result])
|
246
|
-
result
|
247
|
-
end
|
248
|
-
|
249
|
-
def construct_factory_with_args(factory, *args, &block_arg)
|
250
|
-
result = construct_with_constructor_dependencies(factory, *args, &block_arg)
|
251
|
-
@queued_for_phase_2.push([factory, result])
|
252
|
-
result
|
253
|
-
end
|
254
|
-
|
255
|
-
def construct_with_constructor_dependencies(factory, *args, &block_arg)
|
256
|
-
deps = construct_dependencies(factory.constructor_dependencies)
|
257
|
-
begin
|
258
|
-
factory.new_from_dependencies(deps, *args, &block_arg)
|
259
|
-
rescue Wirer::Error
|
260
|
-
raise
|
261
|
-
rescue => e
|
262
|
-
wrapped = DependencyConstructionError.new("Unable to construct factory: #{factory.inspect}", e)
|
263
|
-
raise wrapped
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
def curry_factory_with_constructed_dependencies(factory)
|
268
|
-
deps = construct_dependencies(factory.constructor_dependencies)
|
269
|
-
factory.curry_with_dependencies(deps)
|
270
|
-
end
|
271
|
-
|
272
|
-
def construct_and_inject_setter_dependencies(factory, instance)
|
273
|
-
setter_deps = construct_dependencies(factory.setter_dependencies(instance))
|
274
|
-
setter_deps.each do |dep_name, dep|
|
275
|
-
factory.inject_dependency(instance, dep_name, dep)
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
def post_initialize(factories_instances)
|
280
|
-
factories_instances.each {|factory, instance| factory.post_initialize(instance)}
|
281
|
-
end
|
282
173
|
end
|
283
174
|
end
|
@@ -12,15 +12,20 @@ module Wirer
|
|
12
12
|
# the container will then give you a curried factory from which you can construct
|
13
13
|
# your own instances, rather than supplying a single pre-constructed instance.
|
14
14
|
#
|
15
|
-
#
|
15
|
+
# Setter dependencies are curried in a very unoptimised way.
|
16
16
|
class Factory::CurriedDependencies
|
17
|
-
def initialize(factory, dependencies)
|
17
|
+
def initialize(construction_session, factory, dependencies)
|
18
18
|
@factory = factory
|
19
19
|
@dependencies = dependencies
|
20
|
+
@construction_session = construction_session
|
20
21
|
end
|
21
22
|
|
22
23
|
def new(*args, &block_arg)
|
23
|
-
@factory.
|
24
|
+
setter_dependencies = @factory.setter_dependencies(nil).dup || {}
|
25
|
+
|
26
|
+
@construction_session.construction_session do
|
27
|
+
@construction_session.construct_factory(@factory, *args, &block_arg)
|
28
|
+
end
|
24
29
|
end
|
25
30
|
|
26
31
|
alias :call :new
|
@@ -94,8 +94,8 @@ module Wirer
|
|
94
94
|
# Supplies a wrapper around the factory with a set of pre-supplied dependencies.
|
95
95
|
# The wrapper can then be used to construct instances.
|
96
96
|
# See Factory::CurriedDependencies
|
97
|
-
def curry_with_dependencies(dependencies)
|
98
|
-
Factory::CurriedDependencies.new(self, dependencies)
|
97
|
+
def curry_with_dependencies(dependencies, construction_session)
|
98
|
+
Factory::CurriedDependencies.new(construction_session, self, dependencies)
|
99
99
|
end
|
100
100
|
|
101
101
|
def inject_dependency(instance, attr_name, dependency)
|
data/lib/wirer/version.rb
CHANGED
data/lib/wirer.rb
CHANGED
metadata
CHANGED
@@ -1,21 +1,22 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wirer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 11
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 5
|
9
|
+
- 0
|
10
|
+
version: 0.5.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Matthew Willson
|
14
|
+
- Tom Chipchase
|
14
15
|
autorequire:
|
15
16
|
bindir: bin
|
16
17
|
cert_chain: []
|
17
18
|
|
18
|
-
date:
|
19
|
+
date: 2012-07-27 00:00:00 Z
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
22
|
name: rake
|
@@ -82,6 +83,7 @@ dependencies:
|
|
82
83
|
description:
|
83
84
|
email:
|
84
85
|
- matthew@playlouder.com
|
86
|
+
- tom.chipchase@gmail.com
|
85
87
|
executables: []
|
86
88
|
|
87
89
|
extensions: []
|
@@ -90,17 +92,18 @@ extra_rdoc_files: []
|
|
90
92
|
|
91
93
|
files:
|
92
94
|
- lib/wirer.rb
|
95
|
+
- lib/wirer/construction_session.rb
|
93
96
|
- lib/wirer/version.rb
|
94
|
-
- lib/wirer/container.rb
|
95
|
-
- lib/wirer/dependency.rb
|
96
|
-
- lib/wirer/errors.rb
|
97
97
|
- lib/wirer/service.rb
|
98
|
-
- lib/wirer/
|
98
|
+
- lib/wirer/errors.rb
|
99
|
+
- lib/wirer/container.rb
|
100
|
+
- lib/wirer/factory/class_mixins.rb
|
99
101
|
- lib/wirer/factory/curried_dependencies.rb
|
100
102
|
- lib/wirer/factory/wrapped.rb
|
103
|
+
- lib/wirer/factory/from_instance.rb
|
101
104
|
- lib/wirer/factory/interface.rb
|
102
|
-
- lib/wirer/factory/class_mixins.rb
|
103
105
|
- lib/wirer/factory/from_args.rb
|
106
|
+
- lib/wirer/dependency.rb
|
104
107
|
- README.rb
|
105
108
|
homepage:
|
106
109
|
licenses: []
|
@@ -131,7 +134,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
134
|
requirements: []
|
132
135
|
|
133
136
|
rubyforge_project:
|
134
|
-
rubygems_version: 1.8.
|
137
|
+
rubygems_version: 1.8.24
|
135
138
|
signing_key:
|
136
139
|
specification_version: 3
|
137
140
|
summary: A lightweight dependency injection framework to help wire up objects in Ruby
|