needle 0.5.0 → 0.6.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.
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