wirer 0.4.7 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|