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.
@@ -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