needle 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/benchmarks/instantiability.rb +26 -0
  2. data/benchmarks/instantiation.rb +33 -0
  3. data/benchmarks/interceptors.rb +42 -0
  4. data/benchmarks/interceptors2.rb +70 -0
  5. data/doc/README +3 -1
  6. data/doc/di-in-ruby.rdoc +201 -0
  7. data/doc/images/di_classdiagram.jpg +0 -0
  8. data/doc/manual/manual.yml +4 -0
  9. data/doc/manual/parts/01_alternatives.txt +11 -0
  10. data/doc/manual/parts/02_creating.txt +20 -0
  11. data/doc/manual/parts/02_namespaces.txt +1 -1
  12. data/doc/manual/parts/02_services.txt +15 -3
  13. data/doc/manual-html/chapter-1.html +34 -7
  14. data/doc/manual-html/chapter-2.html +43 -9
  15. data/doc/manual-html/chapter-3.html +6 -4
  16. data/doc/manual-html/chapter-4.html +6 -4
  17. data/doc/manual-html/chapter-5.html +6 -4
  18. data/doc/manual-html/chapter-6.html +6 -4
  19. data/doc/manual-html/chapter-7.html +6 -4
  20. data/doc/manual-html/index.html +19 -4
  21. data/lib/needle/container.rb +104 -66
  22. data/{test/tc_models.rb → lib/needle/lifecycle/deferred.rb} +14 -20
  23. data/lib/needle/lifecycle/initialize.rb +49 -0
  24. data/lib/needle/{models → lifecycle}/proxy.rb +16 -8
  25. data/lib/needle/lifecycle/singleton.rb +63 -0
  26. data/lib/needle/lifecycle/threaded.rb +58 -0
  27. data/lib/needle/pipeline/collection.rb +133 -0
  28. data/lib/needle/pipeline/element.rb +85 -0
  29. data/lib/needle/pipeline/interceptor.rb +46 -0
  30. data/lib/needle/registry.rb +48 -8
  31. data/lib/needle/service-point.rb +36 -39
  32. data/lib/needle/thread.rb +87 -0
  33. data/lib/needle/version.rb +1 -1
  34. data/{lib/needle/models/prototype.rb → test/lifecycle/tc_deferred.rb} +15 -17
  35. data/test/lifecycle/tc_initialize.rb +62 -0
  36. data/test/{models → lifecycle}/tc_proxy.rb +5 -5
  37. data/test/lifecycle/tc_singleton.rb +32 -0
  38. data/{lib/needle/models/prototype-deferred.rb → test/lifecycle/tc_threaded.rb} +24 -18
  39. data/test/models/model_test.rb +131 -0
  40. data/test/models/tc_prototype.rb +9 -30
  41. data/test/models/tc_prototype_deferred.rb +9 -31
  42. data/test/models/tc_prototype_deferred_initialize.rb +32 -0
  43. data/test/models/tc_prototype_initialize.rb +32 -0
  44. data/test/models/tc_singleton.rb +8 -29
  45. data/test/models/tc_singleton_deferred.rb +8 -30
  46. data/test/models/tc_singleton_deferred_initialize.rb +32 -0
  47. data/test/models/tc_singleton_initialize.rb +32 -0
  48. data/test/models/tc_threaded.rb +32 -0
  49. data/test/models/tc_threaded_deferred.rb +32 -0
  50. data/test/models/tc_threaded_deferred_initialize.rb +32 -0
  51. data/test/models/tc_threaded_initialize.rb +32 -0
  52. data/test/pipeline/tc_collection.rb +116 -0
  53. data/test/pipeline/tc_element.rb +72 -0
  54. data/test/tc_container.rb +77 -36
  55. data/test/tc_logger.rb +5 -0
  56. data/test/tc_registry.rb +39 -1
  57. data/test/tc_service_point.rb +43 -7
  58. metadata +39 -12
  59. data/lib/needle/models/singleton-deferred.rb +0 -57
  60. data/lib/needle/models/singleton.rb +0 -56
  61. data/lib/needle/models.rb +0 -44
@@ -14,8 +14,8 @@
14
14
  </div>
15
15
  </td><td valign='middle' align='right'>
16
16
  <div class="info">
17
- Needle Version: <strong>0.5.0</strong><br />
18
- Manual Last Updated: <strong>2004-10-15 03:41 GMT</strong>
17
+ Needle Version: <strong>0.6.0</strong><br />
18
+ Manual Last Updated: <strong>2004-10-21 16:17 GMT</strong>
19
19
  </div>
20
20
  </td></tr>
21
21
  </table>
@@ -41,9 +41,11 @@
41
41
 
42
42
  <li><a href="chapter-1.html#s2">How Can It Help Me?</a></li>
43
43
 
44
- <li><a href="chapter-1.html#s3">License Information</a></li>
44
+ <li><a href="chapter-1.html#s3">Alternatives</a></li>
45
45
 
46
- <li><a href="chapter-1.html#s4">Support</a></li>
46
+ <li><a href="chapter-1.html#s4">License Information</a></li>
47
+
48
+ <li><a href="chapter-1.html#s5">Support</a></li>
47
49
 
48
50
  </ol>
49
51
  </li>
@@ -177,7 +179,27 @@
177
179
  registry = Needle::Registry.new
178
180
  </pre>
179
181
 
180
- <p>Once you have the reference to the registry, you can register services with it, create new namespaces in it, and so forth.<br />
182
+ <p>Once you have the reference to the registry, you can register services with it, create new namespaces in it, and so forth.</p>
183
+
184
+ <p>Alternatively, you can pass a block to <code>#new</code>:</p>
185
+
186
+ <pre>
187
+ registry = Needle::Registry.new do |r|
188
+ ...
189
+ end
190
+ </pre>
191
+
192
+ <p>The parameter to the block will be a reference to the registry. This allows you to register services with the registry as soon as it is created.</p>
193
+
194
+ <p>One other convenience method is <code>#new!</code>:</p>
195
+
196
+ <pre>
197
+ registry = Needle::Registry.new! do
198
+ ...
199
+ end
200
+ </pre>
201
+
202
+ <p>This block accepts no parameters, and evaluates the block as if it were passed to <code>Registry#define!</code> (see below).<br />
181
203
  </p>
182
204
  </div>
183
205
 
@@ -211,17 +233,29 @@
211
233
 
212
234
  <h3>Convenience Methods</h3>
213
235
 
214
- <p>Because you will often need to register many services with a registry at once, a convenience method has been provided to make this use case lean and mean. Just call <code>registry!</code>, passing a block that accepts no parameters. This block will be evaluated in a new context, with any unrecognized method call being interpreted as a new service registration of that name:</p>
236
+ <p>Because you will often need to register many services with a registry at once, two convenience methods have been provided to make this use case lean and mean.</p>
237
+
238
+ <p>The first is <code>define</code>. Just pass a block to define that accepts one parameter. This parameter will be a &#8220;builder&#8221; object that allows you to define services just by sending them as messages to the builder:</p>
239
+
240
+ <pre>
241
+ registry.define do |b|
242
+ b.foo { Bar.new }
243
+ b.bar { Foo.new }
244
+ ...
245
+ end
246
+ </pre>
247
+
248
+ <p>Alternative, you can call <code>define!</code>, passing a block that accepts no parameters. This block will be evaluated in the &#8220;builder&#8221; object&#8217;s context, with any unrecognized method call being interpreted as a new service registration of that name:</p>
215
249
 
216
250
  <pre>
217
- registry.register! do
251
+ registry.define! do
218
252
  foo { Bar.new }
219
253
  bar { Foo.new }
220
254
  ...
221
255
  end
222
256
  </pre>
223
257
 
224
- <p>The above will register two new services with the registry, <code>:foo</code> and <code>:bar</code>.</p>
258
+ <p>Both of the above will register two new services with the registry, <code>:foo</code> and <code>:bar</code>.</p>
225
259
 
226
260
  <h3>Default Lifecycle</h3>
227
261
 
@@ -287,7 +321,7 @@
287
321
  end
288
322
  </pre>
289
323
 
290
- <p>And, to mirror the <code>register!</code> method, there is also a <code>namespace!</code> method. This method creates a new namespace and then does a <code>register!</code> call on that namespace.</p>
324
+ <p>And, to mirror the <code>namespace</code> method, there is also a <code>namespace!</code> method. This method creates a new namespace and then does a <code>define!</code> call on that namespace.</p>
291
325
 
292
326
  <pre>
293
327
  registry.namespace! :stuff do
@@ -14,8 +14,8 @@
14
14
  </div>
15
15
  </td><td valign='middle' align='right'>
16
16
  <div class="info">
17
- Needle Version: <strong>0.5.0</strong><br />
18
- Manual Last Updated: <strong>2004-10-15 03:41 GMT</strong>
17
+ Needle Version: <strong>0.6.0</strong><br />
18
+ Manual Last Updated: <strong>2004-10-21 16:17 GMT</strong>
19
19
  </div>
20
20
  </td></tr>
21
21
  </table>
@@ -41,9 +41,11 @@
41
41
 
42
42
  <li><a href="chapter-1.html#s2">How Can It Help Me?</a></li>
43
43
 
44
- <li><a href="chapter-1.html#s3">License Information</a></li>
44
+ <li><a href="chapter-1.html#s3">Alternatives</a></li>
45
45
 
46
- <li><a href="chapter-1.html#s4">Support</a></li>
46
+ <li><a href="chapter-1.html#s4">License Information</a></li>
47
+
48
+ <li><a href="chapter-1.html#s5">Support</a></li>
47
49
 
48
50
  </ol>
49
51
  </li>
@@ -14,8 +14,8 @@
14
14
  </div>
15
15
  </td><td valign='middle' align='right'>
16
16
  <div class="info">
17
- Needle Version: <strong>0.5.0</strong><br />
18
- Manual Last Updated: <strong>2004-10-15 03:41 GMT</strong>
17
+ Needle Version: <strong>0.6.0</strong><br />
18
+ Manual Last Updated: <strong>2004-10-21 16:17 GMT</strong>
19
19
  </div>
20
20
  </td></tr>
21
21
  </table>
@@ -41,9 +41,11 @@
41
41
 
42
42
  <li><a href="chapter-1.html#s2">How Can It Help Me?</a></li>
43
43
 
44
- <li><a href="chapter-1.html#s3">License Information</a></li>
44
+ <li><a href="chapter-1.html#s3">Alternatives</a></li>
45
45
 
46
- <li><a href="chapter-1.html#s4">Support</a></li>
46
+ <li><a href="chapter-1.html#s4">License Information</a></li>
47
+
48
+ <li><a href="chapter-1.html#s5">Support</a></li>
47
49
 
48
50
  </ol>
49
51
  </li>
@@ -14,8 +14,8 @@
14
14
  </div>
15
15
  </td><td valign='middle' align='right'>
16
16
  <div class="info">
17
- Needle Version: <strong>0.5.0</strong><br />
18
- Manual Last Updated: <strong>2004-10-15 03:41 GMT</strong>
17
+ Needle Version: <strong>0.6.0</strong><br />
18
+ Manual Last Updated: <strong>2004-10-21 16:17 GMT</strong>
19
19
  </div>
20
20
  </td></tr>
21
21
  </table>
@@ -41,9 +41,11 @@
41
41
 
42
42
  <li><a href="chapter-1.html#s2">How Can It Help Me?</a></li>
43
43
 
44
- <li><a href="chapter-1.html#s3">License Information</a></li>
44
+ <li><a href="chapter-1.html#s3">Alternatives</a></li>
45
45
 
46
- <li><a href="chapter-1.html#s4">Support</a></li>
46
+ <li><a href="chapter-1.html#s4">License Information</a></li>
47
+
48
+ <li><a href="chapter-1.html#s5">Support</a></li>
47
49
 
48
50
  </ol>
49
51
  </li>
@@ -14,8 +14,8 @@
14
14
  </div>
15
15
  </td><td valign='middle' align='right'>
16
16
  <div class="info">
17
- Needle Version: <strong>0.5.0</strong><br />
18
- Manual Last Updated: <strong>2004-10-15 03:41 GMT</strong>
17
+ Needle Version: <strong>0.6.0</strong><br />
18
+ Manual Last Updated: <strong>2004-10-21 16:17 GMT</strong>
19
19
  </div>
20
20
  </td></tr>
21
21
  </table>
@@ -41,9 +41,11 @@
41
41
 
42
42
  <li><a href="chapter-1.html#s2">How Can It Help Me?</a></li>
43
43
 
44
- <li><a href="chapter-1.html#s3">License Information</a></li>
44
+ <li><a href="chapter-1.html#s3">Alternatives</a></li>
45
45
 
46
- <li><a href="chapter-1.html#s4">Support</a></li>
46
+ <li><a href="chapter-1.html#s4">License Information</a></li>
47
+
48
+ <li><a href="chapter-1.html#s5">Support</a></li>
47
49
 
48
50
  </ol>
49
51
  </li>
@@ -14,8 +14,8 @@
14
14
  </div>
15
15
  </td><td valign='middle' align='right'>
16
16
  <div class="info">
17
- Needle Version: <strong>0.5.0</strong><br />
18
- Manual Last Updated: <strong>2004-10-15 03:41 GMT</strong>
17
+ Needle Version: <strong>0.6.0</strong><br />
18
+ Manual Last Updated: <strong>2004-10-21 16:17 GMT</strong>
19
19
  </div>
20
20
  </td></tr>
21
21
  </table>
@@ -41,9 +41,11 @@
41
41
 
42
42
  <li><a href="chapter-1.html#s2">How Can It Help Me?</a></li>
43
43
 
44
- <li><a href="chapter-1.html#s3">License Information</a></li>
44
+ <li><a href="chapter-1.html#s3">Alternatives</a></li>
45
45
 
46
- <li><a href="chapter-1.html#s4">Support</a></li>
46
+ <li><a href="chapter-1.html#s4">License Information</a></li>
47
+
48
+ <li><a href="chapter-1.html#s5">Support</a></li>
47
49
 
48
50
  </ol>
49
51
  </li>
@@ -14,8 +14,8 @@
14
14
  </div>
15
15
  </td><td valign='middle' align='right'>
16
16
  <div class="info">
17
- Needle Version: <strong>0.5.0</strong><br />
18
- Manual Last Updated: <strong>2004-10-15 03:41 GMT</strong>
17
+ Needle Version: <strong>0.6.0</strong><br />
18
+ Manual Last Updated: <strong>2004-10-21 16:17 GMT</strong>
19
19
  </div>
20
20
  </td></tr>
21
21
  </table>
@@ -41,9 +41,11 @@
41
41
 
42
42
  <li><a href="chapter-1.html#s2">How Can It Help Me?</a></li>
43
43
 
44
- <li><a href="chapter-1.html#s3">License Information</a></li>
44
+ <li><a href="chapter-1.html#s3">Alternatives</a></li>
45
45
 
46
- <li><a href="chapter-1.html#s4">Support</a></li>
46
+ <li><a href="chapter-1.html#s4">License Information</a></li>
47
+
48
+ <li><a href="chapter-1.html#s5">Support</a></li>
47
49
 
48
50
  </ol>
49
51
  </li>
@@ -161,6 +163,19 @@
161
163
  </p>
162
164
 
163
165
 
166
+ <p align="center"><strong>Recent Updates to This Manual</strong></p>
167
+ <table border='0' cellpadding='0' cellspacing='0' align='center'><tr><td>
168
+ <ul>
169
+
170
+ <li>added Container#define</li>
171
+
172
+ <li>renamed Container#register! to Container#define!</li>
173
+
174
+ <li>added documentation of Container#new!</li>
175
+
176
+ </ul>
177
+ </table>
178
+
164
179
 
165
180
  <p class="copyright">
166
181
  Copyright &copy; 2004
@@ -30,13 +30,13 @@ module Needle
30
30
  # Container#namespace method to create new containers.
31
31
  class Container
32
32
 
33
- # This class is used by the #register! and #namespace! methods to allow
33
+ # This class is used by the #define! and #namespace! methods to allow
34
34
  # an +instance_eval+'d block to create new service points simply by
35
35
  # invoking imaginary methods. It is basically an empty shell, with almost
36
36
  # all of the builtin methods removed from it. (This allows services like
37
37
  # "hash" and "print" to be defined, where they would normally conflict
38
38
  # with the Kernel methods of the same name.)
39
- class RegistrationContext
39
+ class DefinitionContext
40
40
  ( private_instance_methods +
41
41
  protected_instance_methods +
42
42
  public_instance_methods -
@@ -45,24 +45,33 @@ module Needle
45
45
  ).
46
46
  each { |m| undef_method m }
47
47
 
48
- # Create a new RegistrationContext that wraps the given container. All
48
+ # Create a new DefinitionContext that wraps the given container. All
49
49
  # operations performed on this context will be delegated to the
50
50
  # container.
51
51
  def initialize( container )
52
52
  @container = container
53
53
  end
54
54
 
55
+ # A way to access the container reference being operated on from within
56
+ # the context.
57
+ def this_container
58
+ @container
59
+ end
60
+
55
61
  # Delegate to Container#intercept.
56
62
  def intercept( name )
57
63
  @container.intercept( name )
58
64
  end
59
65
 
60
- # Delegate to Container#namespace!. Note that this is an exception to the
61
- # general rule regarding bang methods, since this (non-bang) method
62
- # delegates to a bang-method. However, because this is typically called
63
- # within the context of a bang method (like Container#register!), it felt
64
- # redundant to have the bang here as well. Disagree? Let me know.
66
+ # Delegate to Container#namespace. Instead of passing the container to
67
+ # the new namespace, however, pass the definition context for the new
68
+ # container.
65
69
  def namespace( *parms, &block )
70
+ @container.namespace( *parms ) { |ns| block.call( ns.builder ) }
71
+ end
72
+
73
+ # Delegate to Container#namespace!.
74
+ def namespace!( *parms, &block )
66
75
  @container.namespace!( *parms, &block )
67
76
  end
68
77
 
@@ -96,6 +105,7 @@ module Needle
96
105
  # Create a new empty container with the given parent and name.
97
106
  def initialize( parent=nil, name=nil )
98
107
  @root = nil
108
+ @builder = nil
99
109
 
100
110
  @name = name
101
111
  @parent = parent
@@ -119,49 +129,66 @@ module Needle
119
129
  "#{@parent.fullname}.#{@name}"
120
130
  end
121
131
 
122
- # Register the named service with the container. When the service is
123
- # requested (with Container#[]), the associated callback will be used
124
- # to construct it.
132
+ # Returns the DefinitionContext instance that can be used to "build"
133
+ # this container.
134
+ def builder
135
+ @builder ||= DefinitionContext.new( self )
136
+ end
137
+
138
+ # If a block is given, yields the container's builder instance to the
139
+ # block. Otherwise, simply returns the builder instance.
125
140
  #
126
141
  # Usage:
127
142
  #
128
- # container.register( :calc, :model=>:prototype ) do |c|
129
- # Calc.new( c.operations )
143
+ # container.define do |b|
144
+ # b.foo { Bar.new }
145
+ # b.baz { Baz.new }
146
+ # ...
130
147
  # end
131
- def register( name, opts={}, &callback )
132
- raise ArgumentError, "expect block" unless callback
133
- name = name.to_s.intern unless name.is_a?( Symbol )
134
- @service_points[ name ] =
135
- ServicePoint.new( self, name, opts, &callback )
148
+ #
149
+ # Or:
150
+ #
151
+ # container.define.foo { Bar.new }
152
+ # container.define.baz { Baz.new }
153
+ def define
154
+ yield builder if block_given?
155
+ builder
136
156
  end
137
157
 
138
- # Create a new RegistrationContext around the container, and then evaluate
158
+ # Create a new DefinitionContext around the container, and then evaluate
139
159
  # the block within the new context instance (via +instance_eval+).
140
160
  #
141
161
  # Usage:
142
162
  #
143
- # container.register! do
163
+ # container.define! do
144
164
  # calc( :model => :prototype ) { Calc.new( operations ) }
145
165
  # end
146
- def register!( &block )
166
+ def define!( &block )
147
167
  raise ArgumentError, "block expected" unless block
148
- ctx = RegistrationContext.new( self )
149
- ctx.instance_eval( &block )
168
+ builder.instance_eval( &block )
150
169
  self
151
170
  end
152
171
 
153
- # Create a new namespace within the container. Each parameter ought to
154
- # be the name of a namespace. If more than one parameter is given,
155
- # each one represents the name of a new namespace to create inside of
156
- # the last-created namespace, unless that namespace already exists.
157
- # This makes it work analogously to FileUtils#mkdir_p (creating new
158
- # namespaces along a path of namespaces, as needed).
172
+ # Register the named service with the container. When the service is
173
+ # requested (with Container#[]), the associated callback will be used
174
+ # to construct it.
159
175
  #
160
- # If a block is given, the latest namespace created is yielded to the
161
- # block.
176
+ # Usage:
162
177
  #
163
- # The last parameter may be a Hash, in which case it is used to specify
164
- # options describing how the namespace should be created.
178
+ # container.register( :calc, :model=>:prototype ) do |c|
179
+ # Calc.new( c.operations )
180
+ # end
181
+ def register( name, opts={}, &callback )
182
+ raise ArgumentError, "expect block" unless callback
183
+
184
+ name = name.to_s.intern unless name.is_a?( Symbol )
185
+ @service_points[ name ] =
186
+ ServicePoint.new( self, name, opts, &callback )
187
+ end
188
+
189
+ # Create a new namespace within the container, with the given name. If a
190
+ # block is provided, it will be invoked when the namespace is created,
191
+ # with the new namespace passed to it.
165
192
  #
166
193
  # For the curious, namespaces are simply services that are implemented
167
194
  # by Container. The two statements are really identical:
@@ -169,46 +196,30 @@ module Needle
169
196
  # container.namespace( :calc )
170
197
  # container.register( :calc ) { |c| Needle::Container.new( c, :calc ) }
171
198
  #
199
+ # Note that this means that namespaces may be singletons or prototypes, or
200
+ # have immediate or deferred instantiation, and so forth. (The default of
201
+ # immediately, singleton instantiation is sufficient for 99% of the things
202
+ # you'll use namespaces for.)
203
+ #
172
204
  # Usage:
173
205
  #
174
- # container.namespace( :calc, :operations ) do |op|
206
+ # container.namespace( :operations ) do |op|
175
207
  # op.register( :add ) { Adder.new }
176
208
  # ...
177
209
  # end
178
210
  #
179
211
  # adder = container.calc.operations.add
180
- def namespace( *parms )
181
- opts = {}
182
- opts = parms.pop if parms.last.is_a?( Hash )
183
-
184
- if parms.length < 1
185
- raise ArgumentError, "you must specify at least one name"
212
+ def namespace( name, opts={}, &block )
213
+ register( name, opts ) do |c,p|
214
+ ns = Container.new( c, name )
215
+ block.call ns if block
216
+ ns
186
217
  end
187
-
188
- container = self
189
- parms.each do |parm|
190
- unless container.has_key?( parm )
191
- container.register( parm, opts ) { |c| Container.new( c, parm ) }
192
- end
193
-
194
- container = container[parm]
195
- end
196
-
197
- yield container if block_given?
198
218
  end
199
219
 
200
- # Create a new namespace within the container. Each parameter ought to
201
- # be the name of a namespace. If more than one parameter is given,
202
- # each one represents the name of a new namespace to create inside of
203
- # the last-created namespace, unless that namespace already exists.
204
- # This makes it work analogously to FileUtils#mkdir_p (creating new
205
- # namespaces along a path of namespaces, as needed).
206
- #
207
- # The last parameter may be a Hash, in which case it is used to specify
208
- # options describing how the namespace should be created.
209
- #
210
- # The block is passed to the Container#register! method of the last
211
- # namespace created.
220
+ # Create a new namespace within the container, with the given name.
221
+ # The block (which is required) will be passed to Container#define! on
222
+ # the new namespace.
212
223
  #
213
224
  # For the curious, namespaces are simply services that are implemented
214
225
  # by Container. The two statements are really identical:
@@ -216,17 +227,22 @@ module Needle
216
227
  # container.namespace( :calc )
217
228
  # container.register( :calc ) { |c| Needle::Container.new( c, :calc ) }
218
229
  #
230
+ # Note that this means that namespaces may be singletons or prototypes, or
231
+ # have immediate or deferred instantiation, and so forth. (The default of
232
+ # immediately, singleton instantiation is sufficient for 99% of the things
233
+ # you'll use namespaces for.)
234
+ #
219
235
  # Usage:
220
236
  #
221
- # container.namespace!( :calc, :operations ) do
237
+ # container.namespace!( :operations ) do
222
238
  # add { Adder.new }
223
239
  # ...
224
240
  # end
225
241
  #
226
242
  # adder = container.calc.operations.add
227
- def namespace!( *parms, &block )
243
+ def namespace!( name, opts={}, &block )
228
244
  raise ArgumentError, "block expected" unless block
229
- namespace( *parms ) { |ns| ns.register!( &block ) }
245
+ namespace( name, opts ) { |ns| ns.define!( &block ) }
230
246
  end
231
247
 
232
248
  # Describe a new interceptor to use that will intercept method calls
@@ -246,6 +262,21 @@ module Needle
246
262
  interceptor
247
263
  end
248
264
 
265
+ # Returns the pipeline object for the named service, which allows clients
266
+ # to explicitly manipulate the service's instantiation pipeline.
267
+ #
268
+ # Usage:
269
+ #
270
+ # container.pipeline( :calc ).
271
+ # add( :initialize ).
272
+ # add( :custom ) { |me,*args| me.succ.call( *args ) }
273
+ def pipeline( name )
274
+ point = find_definition( name )
275
+ raise ServiceNotFound, "#{fullname}.#{name}" unless point
276
+
277
+ point.pipeline
278
+ end
279
+
249
280
  # Searches the current container and its ancestors for the named service.
250
281
  # If found, the service point (the definition of that service) is returned,
251
282
  # otherwise +nil+ is returned.
@@ -297,6 +328,13 @@ module Needle
297
328
  # container.register( :add ) { Adder.new }
298
329
  # p container.add == container[:add] # => true
299
330
  #
331
+ # This also allows you to register new services in the container by
332
+ # sending the container a message with an attached block.
333
+ #
334
+ # Usage:
335
+ #
336
+ # container.foo { Bar.new }
337
+ # p container.foo
300
338
  def method_missing( sym, *args, &block )
301
339
  if block.nil? && args.empty? && knows_key?( sym )
302
340
  self[sym]
@@ -14,31 +14,25 @@
14
14
  # =============================================================================
15
15
  #++
16
16
 
17
- $:.unshift "../lib"
17
+ require 'needle/pipeline/element'
18
+ require 'needle/lifecycle/proxy'
18
19
 
19
- require 'needle/models'
20
- require 'test/unit'
20
+ module Needle
21
+ module Lifecycle
21
22
 
22
- class TC_Models < Test::Unit::TestCase
23
+ # The instantiation pipeline element that implements deferred
24
+ # instantiation.
25
+ class Deferred < Needle::Pipeline::Element
23
26
 
24
- class Registry < Hash
25
- def register( name, opts={}, &callback )
26
- self[name] = callback.call( self )
27
- end
28
- end
29
-
30
- def test_register
31
- registry = Registry.new
27
+ set_default_priority 50
32
28
 
33
- Needle::Models.register( registry )
34
- assert_equal 1, registry.length
35
- assert registry.has_key?( :service_models )
29
+ # Returns a new proxy instance that wraps the next element of the
30
+ # instantiation pipeline.
31
+ def call( *args )
32
+ Proxy.new( succ, *args )
33
+ end
36
34
 
37
- models = registry[:service_models]
35
+ end
38
36
 
39
- assert_equal 4, models.length
40
- assert_equal [:prototype, :prototype_deferred, :singleton, :singleton_deferred],
41
- models.keys.sort {|a,b| a.to_s <=> b.to_s}
42
37
  end
43
-
44
38
  end
@@ -0,0 +1,49 @@
1
+ #--
2
+ # =============================================================================
3
+ # Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
4
+ # All rights reserved.
5
+ #
6
+ # This source file is distributed as part of the Needle dependency injection
7
+ # library for Ruby. This file (and the library as a whole) may be used only as
8
+ # allowed by either the BSD license, or the Ruby license (or, by association
9
+ # with the Ruby license, the GPL). See the "doc" subdirectory of the Needle
10
+ # distribution for the texts of these licenses.
11
+ # -----------------------------------------------------------------------------
12
+ # needle website : http://needle.rubyforge.org
13
+ # project website: http://rubyforge.org/projects/needle
14
+ # =============================================================================
15
+ #++
16
+
17
+ require 'needle/pipeline/element'
18
+
19
+ module Needle
20
+ module Lifecycle
21
+
22
+ # The instantiation pipeline element that implements calling a separate
23
+ # initialization method on the instantiated service. This should always be
24
+ # placed in the pipeline as close to the service implementation as possible
25
+ # (hence, the low default priority).
26
+ class Initialize < Needle::Pipeline::Element
27
+
28
+ set_default_priority -100
29
+
30
+ # Initialize the element. Looks at the options hash to determine the
31
+ # name of the initialization method to call, defaulting to
32
+ # <tt>:initialize_service</tt>.
33
+ def initialize_element
34
+ @init_method = options[:init_method] || :initialize_service
35
+ end
36
+
37
+ # Invokes the next element of the chain. If the result responds to the
38
+ # requested initialization method, that method is invoked on the
39
+ # result, and the result is returned.
40
+ def call( *args )
41
+ service = succ.call( *args )
42
+ service.send @init_method if service.respond_to?( @init_method )
43
+ service
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+ end