wirer 0.4.7 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
- @construction_mutex = Mutex.new
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
- # Note: at present only constructor dependencies can be curried in this way.
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.new_from_dependencies(@dependencies, *args, &block_arg)
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
@@ -1,3 +1,3 @@
1
1
  module Wirer
2
- VERSION = '0.4.7'
2
+ VERSION = '0.5.0'
3
3
  end
data/lib/wirer.rb CHANGED
@@ -9,6 +9,7 @@ require 'wirer/factory/class_mixins'
9
9
  require 'wirer/factory/curried_dependencies'
10
10
  require 'wirer/service'
11
11
  require 'wirer/dependency'
12
+ require 'wirer/construction_session'
12
13
  require 'wirer/container'
13
14
  require 'wirer/errors'
14
15
 
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: 1
4
+ hash: 11
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 4
9
- - 7
10
- version: 0.4.7
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: 2011-11-12 00:00:00 Z
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/factory/from_instance.rb
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.10
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