needle 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/doc/manual/manual.yml +6 -1
- data/doc/manual/parts/customizing_contexts.txt +24 -0
- data/doc/manual/parts/customizing_interceptors.txt +38 -0
- data/doc/manual/parts/customizing_namespaces.txt +24 -0
- data/doc/manual-html/chapter-1.html +18 -2
- data/doc/manual-html/chapter-2.html +18 -2
- data/doc/manual-html/chapter-3.html +18 -2
- data/doc/manual-html/chapter-4.html +18 -2
- data/doc/manual-html/chapter-5.html +18 -2
- data/doc/manual-html/chapter-6.html +18 -2
- data/doc/manual-html/chapter-7.html +18 -2
- data/doc/manual-html/chapter-8.html +18 -2
- data/doc/manual-html/chapter-9.html +344 -0
- data/doc/manual-html/index.html +19 -3
- data/lib/needle/container.rb +4 -82
- data/lib/needle/definition-context.rb +100 -0
- data/lib/needle/registry.rb +19 -8
- data/lib/needle/version.rb +1 -1
- data/test/tc_container.rb +127 -86
- data/test/tc_definition_context.rb +75 -0
- data/test/tc_registry.rb +23 -0
- metadata +8 -2
@@ -0,0 +1,344 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>Needle Manual :: Chapter 9: Customizing Needle</title>
|
4
|
+
<link type="text/css" rel="stylesheet" href="manual.css" />
|
5
|
+
</head>
|
6
|
+
|
7
|
+
<body>
|
8
|
+
<div id="banner">
|
9
|
+
<table border='0' cellpadding='0' cellspacing='0' width='100%'>
|
10
|
+
<tr><td valign='top' align='left'>
|
11
|
+
<div class="title">
|
12
|
+
<span class="product">Needle—</span><br />
|
13
|
+
<span class="tagline">to the point --></span>
|
14
|
+
</div>
|
15
|
+
</td><td valign='middle' align='right'>
|
16
|
+
<div class="info">
|
17
|
+
Needle Version: <strong>1.1.0</strong><br />
|
18
|
+
Manual Last Updated: <strong>2004-11-11 17:31 GMT</strong>
|
19
|
+
</div>
|
20
|
+
</td></tr>
|
21
|
+
</table>
|
22
|
+
</div>
|
23
|
+
|
24
|
+
<table border='0' width='100%' cellpadding='0' cellspacing='0'>
|
25
|
+
<tr><td valign='top'>
|
26
|
+
|
27
|
+
<div id="navigation">
|
28
|
+
<h1>Needle Manual</h1>
|
29
|
+
|
30
|
+
<h2>Chapters</h2>
|
31
|
+
<ol type="I">
|
32
|
+
|
33
|
+
<li>
|
34
|
+
<a href="chapter-1.html">
|
35
|
+
Introduction
|
36
|
+
</a>
|
37
|
+
|
38
|
+
<ol type="1">
|
39
|
+
|
40
|
+
<li><a href="chapter-1.html#s1">What is Needle?</a></li>
|
41
|
+
|
42
|
+
<li><a href="chapter-1.html#s2">How Can It Help Me?</a></li>
|
43
|
+
|
44
|
+
<li><a href="chapter-1.html#s3">Alternatives</a></li>
|
45
|
+
|
46
|
+
<li><a href="chapter-1.html#s4">License Information</a></li>
|
47
|
+
|
48
|
+
<li><a href="chapter-1.html#s5">Support</a></li>
|
49
|
+
|
50
|
+
</ol>
|
51
|
+
</li>
|
52
|
+
|
53
|
+
<li>
|
54
|
+
<a href="chapter-2.html">
|
55
|
+
Registry
|
56
|
+
</a>
|
57
|
+
|
58
|
+
<ol type="1">
|
59
|
+
|
60
|
+
<li><a href="chapter-2.html#s1">Overview</a></li>
|
61
|
+
|
62
|
+
<li><a href="chapter-2.html#s2">Creating</a></li>
|
63
|
+
|
64
|
+
<li><a href="chapter-2.html#s3">Services</a></li>
|
65
|
+
|
66
|
+
<li><a href="chapter-2.html#s4">Namespaces</a></li>
|
67
|
+
|
68
|
+
</ol>
|
69
|
+
</li>
|
70
|
+
|
71
|
+
<li>
|
72
|
+
<a href="chapter-3.html">
|
73
|
+
Service Locator
|
74
|
+
</a>
|
75
|
+
|
76
|
+
<ol type="1">
|
77
|
+
|
78
|
+
<li><a href="chapter-3.html#s1">Overview</a></li>
|
79
|
+
|
80
|
+
<li><a href="chapter-3.html#s2">Conventional Architecture</a></li>
|
81
|
+
|
82
|
+
<li><a href="chapter-3.html#s3">Locator Pattern</a></li>
|
83
|
+
|
84
|
+
</ol>
|
85
|
+
</li>
|
86
|
+
|
87
|
+
<li>
|
88
|
+
<a href="chapter-4.html">
|
89
|
+
Dependency Injection
|
90
|
+
</a>
|
91
|
+
|
92
|
+
<ol type="1">
|
93
|
+
|
94
|
+
<li><a href="chapter-4.html#s1">Overview</a></li>
|
95
|
+
|
96
|
+
<li><a href="chapter-4.html#s2">Setup</a></li>
|
97
|
+
|
98
|
+
</ol>
|
99
|
+
</li>
|
100
|
+
|
101
|
+
<li>
|
102
|
+
<a href="chapter-5.html">
|
103
|
+
Interceptors
|
104
|
+
</a>
|
105
|
+
|
106
|
+
<ol type="1">
|
107
|
+
|
108
|
+
<li><a href="chapter-5.html#s1">Overview</a></li>
|
109
|
+
|
110
|
+
<li><a href="chapter-5.html#s2">Architecture</a></li>
|
111
|
+
|
112
|
+
<li><a href="chapter-5.html#s3">Attaching</a></li>
|
113
|
+
|
114
|
+
<li><a href="chapter-5.html#s4">Ordering</a></li>
|
115
|
+
|
116
|
+
<li><a href="chapter-5.html#s5">Custom</a></li>
|
117
|
+
|
118
|
+
</ol>
|
119
|
+
</li>
|
120
|
+
|
121
|
+
<li>
|
122
|
+
<a href="chapter-6.html">
|
123
|
+
Service Models
|
124
|
+
</a>
|
125
|
+
|
126
|
+
<ol type="1">
|
127
|
+
|
128
|
+
<li><a href="chapter-6.html#s1">Overview</a></li>
|
129
|
+
|
130
|
+
<li><a href="chapter-6.html#s2">Pipelines</a></li>
|
131
|
+
|
132
|
+
<li><a href="chapter-6.html#s3">Models</a></li>
|
133
|
+
|
134
|
+
</ol>
|
135
|
+
</li>
|
136
|
+
|
137
|
+
<li>
|
138
|
+
<a href="chapter-7.html">
|
139
|
+
Logging
|
140
|
+
</a>
|
141
|
+
|
142
|
+
<ol type="1">
|
143
|
+
|
144
|
+
<li><a href="chapter-7.html#s1">Overview</a></li>
|
145
|
+
|
146
|
+
<li><a href="chapter-7.html#s2">LogFactory</a></li>
|
147
|
+
|
148
|
+
<li><a href="chapter-7.html#s3">Configuration</a></li>
|
149
|
+
|
150
|
+
</ol>
|
151
|
+
</li>
|
152
|
+
|
153
|
+
<li>
|
154
|
+
<a href="chapter-8.html">
|
155
|
+
Service Libraries
|
156
|
+
</a>
|
157
|
+
|
158
|
+
<ol type="1">
|
159
|
+
|
160
|
+
<li><a href="chapter-8.html#s1">Overview</a></li>
|
161
|
+
|
162
|
+
<li><a href="chapter-8.html#s2">Creating Libraries</a></li>
|
163
|
+
|
164
|
+
<li><a href="chapter-8.html#s3">Using Libraries</a></li>
|
165
|
+
|
166
|
+
</ol>
|
167
|
+
</li>
|
168
|
+
|
169
|
+
<li><strong>
|
170
|
+
<a href="chapter-9.html">
|
171
|
+
Customizing Needle
|
172
|
+
</a>
|
173
|
+
</strong> <big>←</big>
|
174
|
+
<ol type="1">
|
175
|
+
|
176
|
+
<li><a href="chapter-9.html#s1">Namespaces</a></li>
|
177
|
+
|
178
|
+
<li><a href="chapter-9.html#s2">Interceptors</a></li>
|
179
|
+
|
180
|
+
<li><a href="chapter-9.html#s3">Contexts</a></li>
|
181
|
+
|
182
|
+
</ol>
|
183
|
+
</li>
|
184
|
+
|
185
|
+
</ol>
|
186
|
+
|
187
|
+
<h2>Other Documentation</h2>
|
188
|
+
|
189
|
+
<ul>
|
190
|
+
<li><a href="http://needle.rubyforge.org/api/index.html">Needle API</a></li>
|
191
|
+
<li><a href="http://needle.rubyforge.org/faq.html">Needle FAQ</a></li>
|
192
|
+
</ul>
|
193
|
+
|
194
|
+
<h2>Tutorials</h2>
|
195
|
+
<ol>
|
196
|
+
|
197
|
+
</ol>
|
198
|
+
|
199
|
+
<p align="center"><strong>More To Come...</strong></p>
|
200
|
+
|
201
|
+
<div class="license">
|
202
|
+
<a href="http://creativecommons.org/licenses/by-sa/2.0/"><img alt="Creative Commons License" border="0" src="http://creativecommons.org/images/public/somerights" /></a><br />
|
203
|
+
This manual is licensed under a <a href="http://creativecommons.org/licenses/by-sa/2.0/">Creative Commons License</a>.
|
204
|
+
</div>
|
205
|
+
</div>
|
206
|
+
|
207
|
+
</td><td valign='top' width="100%">
|
208
|
+
|
209
|
+
<div id="content">
|
210
|
+
|
211
|
+
<h1>9. Customizing Needle</h1>
|
212
|
+
|
213
|
+
|
214
|
+
|
215
|
+
<h2>
|
216
|
+
<a name="s1"></a>
|
217
|
+
9.1. Namespaces
|
218
|
+
</h2>
|
219
|
+
|
220
|
+
|
221
|
+
|
222
|
+
<div class="section">
|
223
|
+
<p>By default, when you create a namespace in Needle, the namespace is registered as a service. The type of the service is determined by the <code>:namespace_impl_factory</code> service, which (by default) returns the <code>Needle::Container</code> class.</p>
|
224
|
+
|
225
|
+
<p>You can specify your own custom implementation for namespaces by registering your own <code>:namespace_impl_factory</code> service. In fact, each namespace can have its own implementation of subnamespaces—just register a <code>:namespace_impl_factory</code> in each one that you want to be specialized.</p>
|
226
|
+
|
227
|
+
<p>Here’s a contrived example. Suppose you want each namespace to keep track of the precise time that it was created.</p>
|
228
|
+
|
229
|
+
<pre>
|
230
|
+
class TimeTrackerNamespace < Needle::Container
|
231
|
+
attr_reader :birth_date
|
232
|
+
|
233
|
+
def initialize( *args )
|
234
|
+
super
|
235
|
+
@birth_date = Time.now
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
reg = Needle::Registry.new
|
240
|
+
reg.register( :namespace_impl_factory ) { TimeTrackerNamespace }
|
241
|
+
|
242
|
+
reg.namespace :hello
|
243
|
+
p reg.hello.birth_date
|
244
|
+
</pre>
|
245
|
+
|
246
|
+
<p>In general, you’ll be better off having your custom implementation extend <code>Needle::Container</code>, although the only <em>real</em> requirement is that your implementation publish the same interface as the default namespace implementation.<br />
|
247
|
+
</p>
|
248
|
+
</div>
|
249
|
+
|
250
|
+
|
251
|
+
|
252
|
+
<h2>
|
253
|
+
<a name="s2"></a>
|
254
|
+
9.2. Interceptors
|
255
|
+
</h2>
|
256
|
+
|
257
|
+
|
258
|
+
|
259
|
+
<div class="section">
|
260
|
+
<p>When you attach an interceptor to a service, that new interceptor is wrapped in a definition object that includes various metadata about the interceptor, including its implementation, its priority, its name, and so forth. The implementation of this interceptor definition is determined by the value of the <code>:interceptor_impl_factory</code> service, which by default returns <code>Needle::Interceptor</code>.</p>
|
261
|
+
|
262
|
+
<p>It is this wrapper object that allows interceptor definitions to be done using method chaining:</p>
|
263
|
+
|
264
|
+
<pre>
|
265
|
+
reg.intercept( :foo ).with { ... }.with_options(...)
|
266
|
+
</pre>
|
267
|
+
|
268
|
+
<p>If you wish to add custom, domain-specific functionality to the interceptor wrapper, you can register your own implementation of the <code>:interceptor_impl_factory</code>. Consider the following contrived example, where an “only_if” clause is given to determine when the interceptor should be invoked.</p>
|
269
|
+
|
270
|
+
<pre>
|
271
|
+
class OnlyIfInterceptor < Needle::Interceptor
|
272
|
+
def only_if( &block )
|
273
|
+
@only_if = block
|
274
|
+
self
|
275
|
+
end
|
276
|
+
|
277
|
+
def action
|
278
|
+
action_proc = super
|
279
|
+
lambda do |chain,ctx|
|
280
|
+
if @only_if.call( chain, ctx )
|
281
|
+
action_proc.call( chain, ctx )
|
282
|
+
else
|
283
|
+
chain.process_next( ctx )
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
reg = Needle::Registry.new
|
290
|
+
reg.register( :interceptor_impl_factory ) { OnlyIfInterceptor }
|
291
|
+
reg.register( :foo ) { Bar.new }
|
292
|
+
|
293
|
+
reg.intercept( :foo ).
|
294
|
+
with { |c| c.logging_interceptor }.
|
295
|
+
only_if { |ch,ctx| something_is_true( ch, ctx ) }.
|
296
|
+
with_options(...)
|
297
|
+
</pre>
|
298
|
+
</div>
|
299
|
+
|
300
|
+
|
301
|
+
|
302
|
+
<h2>
|
303
|
+
<a name="s3"></a>
|
304
|
+
9.3. Contexts
|
305
|
+
</h2>
|
306
|
+
|
307
|
+
|
308
|
+
|
309
|
+
<div class="section">
|
310
|
+
<p>A <em>definition context</em> is used when registering services using any of the <code>#define</code> interfaces. For example, <code>Container#define</code> yields an instance of a definition context to the given block, and <code>Container#define!</code> uses the block in an <code>instance_eval</code> on a definition context.</p>
|
311
|
+
|
312
|
+
<p>The default implementation used for definition contexts is defined by the <code>:definition_context_factory</code> service. By default, this service returns <code>Needle::DefinitionContext</code>, but you can specify your own definition context implementations by overriding this service. In fact, each namespace could have its own definition context implementation, if needed.</p>
|
313
|
+
|
314
|
+
<p>Consider the following contrived example, where you want to provide a convenient way to register services of type Hash.</p>
|
315
|
+
|
316
|
+
<pre>
|
317
|
+
class MyDefinitionContext < Needle::DefinitionContext
|
318
|
+
def register_hash( name, opts={} )
|
319
|
+
this_container.register( name, opts ) { Hash.new }
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
reg = Needle::Registry.new
|
324
|
+
reg.register( :definition_context_factory ) { MyDefinitionContext }
|
325
|
+
|
326
|
+
reg.define do |b|
|
327
|
+
b.register_hash( :test1 )
|
328
|
+
b.register_hash( :test2 )
|
329
|
+
end
|
330
|
+
|
331
|
+
reg.test1[:key] = "value"
|
332
|
+
reg.test2[:foo] = "bar"
|
333
|
+
</pre>
|
334
|
+
</div>
|
335
|
+
|
336
|
+
|
337
|
+
|
338
|
+
|
339
|
+
</div>
|
340
|
+
|
341
|
+
</td></tr>
|
342
|
+
</table>
|
343
|
+
</body>
|
344
|
+
</html>
|
data/doc/manual-html/index.html
CHANGED
@@ -14,8 +14,8 @@
|
|
14
14
|
</div>
|
15
15
|
</td><td valign='middle' align='right'>
|
16
16
|
<div class="info">
|
17
|
-
Needle Version: <strong>1.
|
18
|
-
Manual Last Updated: <strong>2004-11-
|
17
|
+
Needle Version: <strong>1.1.0</strong><br />
|
18
|
+
Manual Last Updated: <strong>2004-11-11 17:31 GMT</strong>
|
19
19
|
</div>
|
20
20
|
</td></tr>
|
21
21
|
</table>
|
@@ -166,6 +166,22 @@
|
|
166
166
|
</ol>
|
167
167
|
</li>
|
168
168
|
|
169
|
+
<li>
|
170
|
+
<a href="chapter-9.html">
|
171
|
+
Customizing Needle
|
172
|
+
</a>
|
173
|
+
|
174
|
+
<ol type="1">
|
175
|
+
|
176
|
+
<li><a href="chapter-9.html#s1">Namespaces</a></li>
|
177
|
+
|
178
|
+
<li><a href="chapter-9.html#s2">Interceptors</a></li>
|
179
|
+
|
180
|
+
<li><a href="chapter-9.html#s3">Contexts</a></li>
|
181
|
+
|
182
|
+
</ol>
|
183
|
+
</li>
|
184
|
+
|
169
185
|
</ol>
|
170
186
|
|
171
187
|
<h2>Other Documentation</h2>
|
@@ -218,7 +234,7 @@
|
|
218
234
|
<table border='0' cellpadding='0' cellspacing='0' align='center'><tr><td>
|
219
235
|
<ul>
|
220
236
|
|
221
|
-
<li>
|
237
|
+
<li>Added chapter on "Customizing Needle"</li>
|
222
238
|
|
223
239
|
</ul>
|
224
240
|
</table>
|
data/lib/needle/container.rb
CHANGED
@@ -15,7 +15,6 @@
|
|
15
15
|
#++
|
16
16
|
|
17
17
|
require 'needle/errors'
|
18
|
-
require 'needle/interceptor'
|
19
18
|
require 'needle/service-point'
|
20
19
|
|
21
20
|
module Needle
|
@@ -30,83 +29,6 @@ module Needle
|
|
30
29
|
# Container#namespace method to create new containers.
|
31
30
|
class Container
|
32
31
|
|
33
|
-
# This class is used by the #define! and #namespace! methods to allow
|
34
|
-
# an +instance_eval+'d block to create new service points simply by
|
35
|
-
# invoking imaginary methods. It is basically an empty shell, with almost
|
36
|
-
# all of the builtin methods removed from it. (This allows services like
|
37
|
-
# "hash" and "print" to be defined, where they would normally conflict
|
38
|
-
# with the Kernel methods of the same name.)
|
39
|
-
class DefinitionContext
|
40
|
-
( private_instance_methods +
|
41
|
-
protected_instance_methods +
|
42
|
-
public_instance_methods -
|
43
|
-
[ "instance_eval", "__id__", "__send__", "initialize", "remove_const",
|
44
|
-
"method_missing", "method", "class", "inspect", "to_s", "instance_variables" ]
|
45
|
-
).
|
46
|
-
each { |m| undef_method m }
|
47
|
-
|
48
|
-
# Create a new DefinitionContext that wraps the given container. All
|
49
|
-
# operations performed on this context will be delegated to the
|
50
|
-
# container.
|
51
|
-
def initialize( container )
|
52
|
-
@container = container
|
53
|
-
end
|
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
|
-
|
61
|
-
# Delegate to Container#intercept.
|
62
|
-
def intercept( name )
|
63
|
-
@container.intercept( name )
|
64
|
-
end
|
65
|
-
|
66
|
-
# Delegate to Container#namespace.
|
67
|
-
def namespace( *parms, &block )
|
68
|
-
@container.namespace( *parms, &block )
|
69
|
-
end
|
70
|
-
|
71
|
-
# Delegate to Container#namespace_define!.
|
72
|
-
def namespace_define!( *parms, &block )
|
73
|
-
@container.namespace_define!( *parms, &block )
|
74
|
-
end
|
75
|
-
|
76
|
-
alias :namespace! :namespace_define!
|
77
|
-
|
78
|
-
# Delegate to Container#define on the new namespace.
|
79
|
-
def namespace_define( *parms, &block )
|
80
|
-
@container.namespace( *parms ) { |ns| ns.define( &block ) }
|
81
|
-
end
|
82
|
-
|
83
|
-
# Delegate to Container#require on the current container.
|
84
|
-
def require( *parms )
|
85
|
-
# this is necessary to work around an rdoc bug...rdoc doesn't like
|
86
|
-
# calling require with a variable number of arguments.
|
87
|
-
@container.__send__( :require, *parms )
|
88
|
-
end
|
89
|
-
|
90
|
-
# Any method invocation with no block and no parameters is interpreted to
|
91
|
-
# be a service reference on the wrapped container, and delegates to
|
92
|
-
# Container#[]. If the block is not given but the args are not empty, a
|
93
|
-
# NoMethodError will be raised.
|
94
|
-
#
|
95
|
-
# If a block is given, this delegates to Container#register, leaving
|
96
|
-
# all parameters in place.
|
97
|
-
def method_missing( sym, *args, &block )
|
98
|
-
if block.nil?
|
99
|
-
if args.empty?
|
100
|
-
@container[sym]
|
101
|
-
else
|
102
|
-
super
|
103
|
-
end
|
104
|
-
else
|
105
|
-
@container.register( sym, *args, &block )
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
32
|
# The container that contains this container. This will be +nil+ for
|
111
33
|
# the root of a hierarchy (see Registry).
|
112
34
|
attr_reader :parent
|
@@ -145,7 +67,7 @@ module Needle
|
|
145
67
|
# Returns the DefinitionContext instance that can be used to "build"
|
146
68
|
# this container.
|
147
69
|
def builder
|
148
|
-
@builder ||=
|
70
|
+
@builder ||= self[ :definition_context_factory ].new( self )
|
149
71
|
end
|
150
72
|
|
151
73
|
# If a block is given, yields the container's builder instance to the
|
@@ -208,7 +130,7 @@ module Needle
|
|
208
130
|
# with the new namespace passed to it.
|
209
131
|
#
|
210
132
|
# For the curious, namespaces are simply services that are implemented
|
211
|
-
# by Container. The two statements are
|
133
|
+
# by Container. The two statements are conceptually identical:
|
212
134
|
#
|
213
135
|
# container.namespace( :calc )
|
214
136
|
# container.register( :calc ) { |c,p| Needle::Container.new( c, p.name ) }
|
@@ -233,7 +155,7 @@ module Needle
|
|
233
155
|
# namespace as soon as you've created it.
|
234
156
|
def namespace( name, opts={}, &block )
|
235
157
|
register( name, opts ) do |c,p|
|
236
|
-
ns =
|
158
|
+
ns = self[ :namespace_impl_factory ].new( c, name )
|
237
159
|
block.call ns if block
|
238
160
|
ns
|
239
161
|
end
|
@@ -322,7 +244,7 @@ module Needle
|
|
322
244
|
point = find_definition( name )
|
323
245
|
raise ServiceNotFound, "#{fullname}.#{name}" unless point
|
324
246
|
|
325
|
-
interceptor =
|
247
|
+
interceptor = self[ :interceptor_impl_factory ].new
|
326
248
|
point.interceptor interceptor
|
327
249
|
|
328
250
|
interceptor
|
@@ -0,0 +1,100 @@
|
|
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/errors'
|
18
|
+
require 'needle/service-point'
|
19
|
+
|
20
|
+
module Needle
|
21
|
+
|
22
|
+
# This class is used by the Container#define! and Container#namespace!
|
23
|
+
# methods to allow an +instance_eval+'d block to create new service points
|
24
|
+
# simply by invoking imaginary methods. It is basically an empty shell, with
|
25
|
+
# almost all of the builtin methods removed from it. (This allows services
|
26
|
+
# like "hash" and "print" to be defined, where they would normally conflict
|
27
|
+
# with the Kernel methods of the same name.)
|
28
|
+
class DefinitionContext
|
29
|
+
( private_instance_methods +
|
30
|
+
protected_instance_methods +
|
31
|
+
public_instance_methods -
|
32
|
+
[ "instance_eval", "__id__", "__send__", "initialize", "remove_const",
|
33
|
+
"method_missing", "method", "class", "inspect", "to_s", "instance_variables" ]
|
34
|
+
).
|
35
|
+
each { |m| undef_method m }
|
36
|
+
|
37
|
+
# Create a new DefinitionContext that wraps the given container. All
|
38
|
+
# operations performed on this context will be delegated to the
|
39
|
+
# container.
|
40
|
+
def initialize( container )
|
41
|
+
@container = container
|
42
|
+
end
|
43
|
+
|
44
|
+
# A way to access the container reference being operated on from within
|
45
|
+
# the context.
|
46
|
+
def this_container
|
47
|
+
@container
|
48
|
+
end
|
49
|
+
|
50
|
+
# Delegate to Container#intercept.
|
51
|
+
def intercept( name )
|
52
|
+
@container.intercept( name )
|
53
|
+
end
|
54
|
+
|
55
|
+
# Delegate to Container#namespace.
|
56
|
+
def namespace( *parms, &block )
|
57
|
+
@container.namespace( *parms, &block )
|
58
|
+
end
|
59
|
+
|
60
|
+
# Delegate to Container#namespace_define!.
|
61
|
+
def namespace_define!( *parms, &block )
|
62
|
+
@container.namespace_define!( *parms, &block )
|
63
|
+
end
|
64
|
+
|
65
|
+
alias :namespace! :namespace_define!
|
66
|
+
|
67
|
+
# Delegate to Container#define on the new namespace.
|
68
|
+
def namespace_define( *parms, &block )
|
69
|
+
@container.namespace( *parms ) { |ns| ns.define( &block ) }
|
70
|
+
end
|
71
|
+
|
72
|
+
# Delegate to Container#require on the current container.
|
73
|
+
def require( *parms )
|
74
|
+
# this is necessary to work around an rdoc bug...rdoc doesn't like
|
75
|
+
# calling require with a variable number of arguments.
|
76
|
+
@container.__send__( :require, *parms )
|
77
|
+
end
|
78
|
+
|
79
|
+
# Any method invocation with no block and no parameters is interpreted to
|
80
|
+
# be a service reference on the wrapped container, and delegates to
|
81
|
+
# Container#[]. If the block is not given but the args are not empty, a
|
82
|
+
# NoMethodError will be raised.
|
83
|
+
#
|
84
|
+
# If a block is given, this delegates to Container#register, leaving
|
85
|
+
# all parameters in place.
|
86
|
+
def method_missing( sym, *args, &block )
|
87
|
+
if block.nil?
|
88
|
+
if args.empty?
|
89
|
+
@container[sym]
|
90
|
+
else
|
91
|
+
super
|
92
|
+
end
|
93
|
+
else
|
94
|
+
@container.register( sym, *args, &block )
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
data/lib/needle/registry.rb
CHANGED
@@ -15,7 +15,9 @@
|
|
15
15
|
#++
|
16
16
|
|
17
17
|
require 'needle/container'
|
18
|
+
require 'needle/definition-context'
|
18
19
|
require 'needle/errors'
|
20
|
+
require 'needle/interceptor'
|
19
21
|
require 'needle/lifecycle/deferred'
|
20
22
|
require 'needle/lifecycle/initialize'
|
21
23
|
require 'needle/lifecycle/singleton'
|
@@ -73,9 +75,13 @@ module Needle
|
|
73
75
|
new( *parms ) { |reg| reg.define( &block ) }
|
74
76
|
end
|
75
77
|
|
76
|
-
# Instantiate a new Registry. The options hash may include
|
77
|
-
#
|
78
|
-
#
|
78
|
+
# Instantiate a new Registry. The options hash may include the following
|
79
|
+
# keys:
|
80
|
+
#
|
81
|
+
# <tt>:logs</tt>:: options used to initialize the logger factory. The
|
82
|
+
# value to this key should be another hash.
|
83
|
+
# <tt>:parent</tt>:: The parent container of this registry.
|
84
|
+
# <tt>:name</tt>:: The name of this registry.
|
79
85
|
#
|
80
86
|
# If a block is given, the constructed registry instance is yielded to it.
|
81
87
|
#
|
@@ -95,19 +101,20 @@ module Needle
|
|
95
101
|
# :logs => { :filename => "/dev/null" }
|
96
102
|
# )
|
97
103
|
def initialize( opts={} )
|
98
|
-
super(
|
99
|
-
bootstrap( opts )
|
104
|
+
super( opts[:parent], opts[:name] )
|
105
|
+
bootstrap( opts ) if parent.nil?
|
100
106
|
yield( self ) if block_given?
|
101
107
|
end
|
102
108
|
|
103
|
-
# Returns +nil
|
109
|
+
# Returns +nil+, unless the registry has a parent, in which case it acts
|
110
|
+
# like Container#fullname. Registries are usually unnamed containers.
|
104
111
|
def fullname
|
105
|
-
nil
|
112
|
+
parent ? super : nil
|
106
113
|
end
|
107
114
|
|
108
115
|
# Bootstraps the pipeline elements, service models, logger factory, and
|
109
116
|
# logging interceptor services into the current registry. This is only
|
110
|
-
# called when a new registry is created.
|
117
|
+
# called when a new, root (parentless) registry is created.
|
111
118
|
def bootstrap( opts )
|
112
119
|
register( :pipeline_elements, :pipeline=>[] ) { Hash.new }
|
113
120
|
pipeline( :pipeline_elements ).add( :singleton,
|
@@ -137,6 +144,10 @@ module Needle
|
|
137
144
|
:threaded_deferred_initialize => [ :threaded, :deferred, :initialize ]
|
138
145
|
)
|
139
146
|
|
147
|
+
register( :definition_context_factory ) { DefinitionContext }
|
148
|
+
register( :namespace_impl_factory ) { Container }
|
149
|
+
register( :interceptor_impl_factory ) { Interceptor }
|
150
|
+
|
140
151
|
register( :logs ) { LogFactory.new( opts[:logs] || {} ) }
|
141
152
|
register( :logging_interceptor ) { LoggingInterceptor }
|
142
153
|
end
|