needle 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/benchmarks/instantiation.rb +39 -0
- data/doc/faq/faq.yml +137 -102
- data/doc/manual-html/chapter-1.html +15 -19
- data/doc/manual-html/chapter-2.html +52 -23
- data/doc/manual-html/chapter-3.html +7 -9
- data/doc/manual-html/chapter-4.html +6 -8
- data/doc/manual-html/chapter-5.html +16 -19
- data/doc/manual-html/chapter-6.html +31 -8
- data/doc/manual-html/chapter-7.html +19 -8
- data/doc/manual-html/chapter-8.html +8 -11
- data/doc/manual-html/chapter-9.html +7 -6
- data/doc/manual-html/index.html +5 -3
- data/doc/manual/manual.yml +2 -1
- data/doc/manual/parts/01_alternatives.txt +2 -1
- data/doc/manual/parts/02_services.txt +33 -0
- data/doc/manual/parts/logging_logfactory.txt +9 -0
- data/doc/manual/parts/models_models.txt +4 -0
- data/doc/manual/parts/models_pipelines.txt +1 -0
- data/lib/needle/container.rb +64 -19
- data/lib/needle/definition-context.rb +34 -6
- data/lib/needle/lifecycle/multiton.rb +64 -0
- data/lib/needle/lifecycle/singleton.rb +2 -2
- data/lib/needle/lifecycle/threaded.rb +4 -3
- data/lib/needle/pipeline/collection.rb +10 -1
- data/lib/needle/pipeline/element.rb +6 -0
- data/lib/needle/registry.rb +18 -5
- data/lib/needle/service-point.rb +21 -10
- data/lib/needle/version.rb +1 -1
- data/test/lifecycle/tc_multiton.rb +43 -0
- data/test/lifecycle/tc_singleton.rb +18 -2
- data/test/lifecycle/tc_threaded.rb +12 -6
- data/test/pipeline/tc_collection.rb +26 -2
- data/test/services.rb +8 -0
- data/test/tc_container.rb +82 -0
- data/test/tc_definition_context.rb +63 -7
- data/test/tc_registry.rb +13 -1
- data/test/tc_service_point.rb +49 -0
- metadata +4 -2
@@ -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.2.0</strong><br />
|
18
|
+
Manual Last Updated: <strong>2004-11-18 15:36 GMT</strong>
|
19
19
|
</div>
|
20
20
|
</td></tr>
|
21
21
|
</table>
|
@@ -226,6 +226,7 @@
|
|
226
226
|
|
227
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
228
|
|
229
|
+
|
229
230
|
<pre>
|
230
231
|
class TimeTrackerNamespace < Needle::Container
|
231
232
|
attr_reader :birth_date
|
@@ -242,9 +243,7 @@
|
|
242
243
|
reg.namespace :hello
|
243
244
|
p reg.hello.birth_date
|
244
245
|
</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>
|
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.</p>
|
248
247
|
</div>
|
249
248
|
|
250
249
|
|
@@ -261,12 +260,13 @@
|
|
261
260
|
|
262
261
|
<p>It is this wrapper object that allows interceptor definitions to be done using method chaining:</p>
|
263
262
|
|
263
|
+
|
264
264
|
<pre>
|
265
265
|
reg.intercept( :foo ).with { ... }.with_options(...)
|
266
266
|
</pre>
|
267
|
-
|
268
267
|
<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
268
|
|
269
|
+
|
270
270
|
<pre>
|
271
271
|
class OnlyIfInterceptor < Needle::Interceptor
|
272
272
|
def only_if( &block )
|
@@ -313,6 +313,7 @@
|
|
313
313
|
|
314
314
|
<p>Consider the following contrived example, where you want to provide a convenient way to register services of type Hash.</p>
|
315
315
|
|
316
|
+
|
316
317
|
<pre>
|
317
318
|
class MyDefinitionContext < Needle::DefinitionContext
|
318
319
|
def register_hash( name, opts={} )
|
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.2.0</strong><br />
|
18
|
+
Manual Last Updated: <strong>2004-11-18 15:36 GMT</strong>
|
19
19
|
</div>
|
20
20
|
</td></tr>
|
21
21
|
</table>
|
@@ -234,7 +234,9 @@
|
|
234
234
|
<table border='0' cellpadding='0' cellspacing='0' align='center'><tr><td>
|
235
235
|
<ul>
|
236
236
|
|
237
|
-
<li>
|
237
|
+
<li>Described parameterized services</li>
|
238
|
+
|
239
|
+
<li>Added multiton service models to list of available models</li>
|
238
240
|
|
239
241
|
</ul>
|
240
242
|
</table>
|
data/doc/manual/manual.yml
CHANGED
@@ -23,7 +23,8 @@ product: !^product
|
|
23
23
|
- Needle Wiki: http://needle.rubyforge.org/wiki/wiki.pl
|
24
24
|
|
25
25
|
recent_updates:
|
26
|
-
-
|
26
|
+
- Described parameterized services
|
27
|
+
- Added multiton service models to list of available models
|
27
28
|
|
28
29
|
chapters:
|
29
30
|
|
@@ -3,8 +3,9 @@ Needle is not the only fish in the dependency-injection pond, even when it comes
|
|
3
3
|
* "Copland":http://copland.rubyforge.org. Copland aims to be an "application framework", taking something of a heavy-weight approach to DI. In so doing, it provides functionality that Needle does not, but at the cost of performance. It also uses external (YAML) configuration files. It is inspired by a Java framework ("HiveMind":http://jakarta.apache.org/hivemind), and so has a vaguely Java-ish flavor to it.
|
4
4
|
* "Rico":http://www.picocontainer.org/Rico. Rico is another project inspired by a Java project ("PicoContainer":http://www.picocontainer.org). It is very lean, and appears to be experimental.
|
5
5
|
* "Tudura":http://sourceforge.jp/projects/nihohi/. I do not have any information on this project, as the information is all in Japanese. If someone with more information about Tudura would like to step forward, I'd be happy to post a summary here.
|
6
|
+
* "MinDI":http://redshift.sourceforge.net/mindi/ is a recent contender that takes a very novel approach to dependency injection. Instead of instantiating a container and adding services to it, you declare a class, mixin a DI module, and add services to the class as methods, thereby making the class the container.
|
6
7
|
|
7
|
-
There is, at the time of this writing, at least one other project on RubyForge devoted to DI, although it has no public releases yet.
|
8
|
+
There is, at the time of this writing, at least one other project on RubyForge devoted to DI ("Pith":http://rubyforge.org/projects/pith), although it has no public releases yet.
|
8
9
|
|
9
10
|
So, which one should you choose? It comes down to an issue of personal preference, mostly, but also one of what you are wanting to accomplish. Needle excels at providing an unobtrusive, light-weight container for managing your dependencies. The cost of it being light-weight is that there is functionality it does not provide, which other containers may. If you really need that missing functionality, you are required to either implement it yourself, or select a different container.
|
10
11
|
|
@@ -54,3 +54,36 @@ By default, a service is only instantiated once per registry. This means that (u
|
|
54
54
|
</pre>
|
55
55
|
|
56
56
|
You can change this behavior, with _service models_. See the chapter on Service Models for more information.
|
57
|
+
|
58
|
+
h3. Parameterized Services
|
59
|
+
|
60
|
+
Needle also supports _parameterized services_. These are services that, when requested, require contextual information to be passed as a parameter so that the service can be correctly initialized.
|
61
|
+
|
62
|
+
Consider the following example, in which some hypothetical @Printer@ class represents any of a number of printers, based on a parameter given to its constructor.
|
63
|
+
|
64
|
+
<pre>
|
65
|
+
registry.register( :printer, :model => :multiton ) do |c,p,name|
|
66
|
+
Printer.new( c.log_for( p ), name )
|
67
|
+
end
|
68
|
+
|
69
|
+
mono = registry.printer( :monochrome )
|
70
|
+
color = registry.printer( :color )
|
71
|
+
</pre>
|
72
|
+
|
73
|
+
There are a few things to note about the above example:
|
74
|
+
|
75
|
+
# The @:multiton@ model is explicitly requested. This is necessary because the default service model (@:singleton@) does not allow parameterized services. Most of the time, you'll use the multiton service model with parameterized services, but you don't have to. You could also use any of the prototype models as well.
|
76
|
+
|
77
|
+
# The constructor block for the @:printer@ service takes three parameters, @c@, @p@, and @name@. The first two parameters, @c@ and @p@, represent the container and the service point, respectively. Any parameters after those two are the contextual parameters given when the service is requested. In this case, there is only one contextual parameter: the name of the printer.
|
78
|
+
|
79
|
+
# Notice the first parameter to the Printer constructor: @c.log_for(p)@. This is itself invoking a parameterized service, named @:log_for@, and passing @p@ as the contextual information. This will return a new logger handle for the service point @p@ (i.e., the current service point).
|
80
|
+
|
81
|
+
# See how the printer service is requested on the last two lines. In this case, the @#printer@ message is sent to the registry with a single parameter. You can also request the service in two other ways:
|
82
|
+
|
83
|
+
<pre>
|
84
|
+
dot_matrix = registry[ :printer, :dot_matrix ]
|
85
|
+
ink_jet = registry.get( :printer, :ink_jet )
|
86
|
+
</pre>
|
87
|
+
|
88
|
+
Choose the style that works best for you.
|
89
|
+
|
@@ -29,3 +29,12 @@ As a convenience, if the value passed to @#get@ responds to either @fullname@ or
|
|
29
29
|
Foo.new( c.logs.get( p ) )
|
30
30
|
end
|
31
31
|
</pre>
|
32
|
+
|
33
|
+
As a further convenience, there is a @:log_for@ service that is parameterized. Just pass the name of the log to retreive (or the service point instance) and it will return the log handle directly:
|
34
|
+
|
35
|
+
<pre>
|
36
|
+
reg.register( :foo ) do |c,p|
|
37
|
+
Foo.new( c.log_for( p ) )
|
38
|
+
end
|
39
|
+
</pre>
|
40
|
+
|
@@ -4,6 +4,10 @@ h3. Standard Service Models:
|
|
4
4
|
|
5
5
|
table{border: 1px solid black}.
|
6
6
|
|_<. Name|_<. Pipeline|_<. Effect|
|
7
|
+
|^. @:multiton@|^. @:multiton@|The returned value will be unique for each unique parameter set given to the service.|
|
8
|
+
|^. @:multiton_deferred@|^. @:multiton@, @:deferred@|As @:multiton@, but a proxy is returned, deferring the instantiation of the service itself until a method is invoked on it.|
|
9
|
+
|^. @:multiton_initialize@|^. @:multiton@, @:initialize@|As @:multiton@, but invoke an initialization method on every service instance as soon as they are created.|
|
10
|
+
|^. @:multiton_deferred_initialize@|^. @:multiton@, @:deferred@, @:initialize@|As @:multiton@, but a proxy is returned, deferring the instantiation of the service itself until a method is invoked on it. When the service is instantiated, an initialization method will be invoked on it.|
|
7
11
|
|^. @:prototype@|^. (empty)|Immediately instantiate the service, at every request.|
|
8
12
|
|^. @:prorotype_deferred@|^. @:deferred@|Return a proxy, that will instantiate the service the first time a method is invoked on the proxy. A new proxy instance will be returned for each request.|
|
9
13
|
|^. @:prototype_initialize@|^. @:initialize@|Immediately instantiate the service, and invoke an initialization method, at every request.|
|
@@ -9,6 +9,7 @@ There are five standard pipeline elements available in Needle (although you may
|
|
9
9
|
* @deferred@: this will always return a proxy that wraps subsequent pipeline elements, causing the subsequent elements to be executed only when a method is invoked on the proxy (at which point the method is then delegated to the resulting service).
|
10
10
|
* @initialize@: this will invoke a method on the resulting service (defaults to @initialize_service@, though it can be changed). It is used for doing final initialization of services (for services that need it).
|
11
11
|
* @interceptor@: this element is used to implement the proxy that wraps the interceptors around the service. It is only attached to the pipeline when an interceptor is attached to a service.
|
12
|
+
* @multiton@: this element enforces a multiton guard on the service. This means that the service will only be instantiated once for each unique set of parameters given to the service.
|
12
13
|
* @singleton@: this is a multiplicity guard that ensures a service is instantiated only once per process.
|
13
14
|
* @threaded@: this is like the @singleton@ element, but it ensures that a service is instantiated no more than once _per thread_.
|
14
15
|
|
data/lib/needle/container.rb
CHANGED
@@ -36,7 +36,15 @@ module Needle
|
|
36
36
|
# The name of this container. May be +nil+.
|
37
37
|
attr_reader :name
|
38
38
|
|
39
|
-
#
|
39
|
+
# A hash of default options to use when registering services. These
|
40
|
+
# defaults also apply to namespaces, so when specifying a new default
|
41
|
+
# service model (for instance) there may be unexpected side-effects with
|
42
|
+
# the namespaces that are created.
|
43
|
+
attr_reader :defaults
|
44
|
+
|
45
|
+
# Create a new empty container with the given parent and name. If a parent
|
46
|
+
# is given, this container will inherit the defaults of the parent at the
|
47
|
+
# time the container was created.
|
40
48
|
def initialize( parent=nil, name=nil )
|
41
49
|
@root = nil
|
42
50
|
@builder = nil
|
@@ -44,6 +52,8 @@ module Needle
|
|
44
52
|
@name = name
|
45
53
|
@parent = parent
|
46
54
|
@service_points = Hash.new
|
55
|
+
|
56
|
+
@defaults = ( parent.nil? ? Hash.new : parent.defaults.dup )
|
47
57
|
end
|
48
58
|
|
49
59
|
# Returns the root of the current hierarchy. If the container is the
|
@@ -55,11 +65,19 @@ module Needle
|
|
55
65
|
@root = parent.root
|
56
66
|
end
|
57
67
|
|
68
|
+
# Returns +true+ if this container either is the given container or is
|
69
|
+
# descended from the given container, and +false+ otherwise.
|
70
|
+
def descended_from?( container )
|
71
|
+
return true if self == container
|
72
|
+
return false unless parent
|
73
|
+
parent.descended_from? container
|
74
|
+
end
|
75
|
+
|
58
76
|
# Return the fully qualified name of this container, which is the
|
59
77
|
# container's name and all parent's names up to the root container,
|
60
78
|
# catenated together with dot characters, i.e., "one.two.three".
|
61
79
|
def fullname
|
62
|
-
parent_name = (
|
80
|
+
parent_name = ( parent ? parent.fullname : nil )
|
63
81
|
return @name.to_s unless parent_name
|
64
82
|
"#{parent_name}.#{@name}"
|
65
83
|
end
|
@@ -120,7 +138,7 @@ module Needle
|
|
120
138
|
|
121
139
|
name = name.to_s.intern unless name.is_a?( Symbol )
|
122
140
|
@service_points[ name ] =
|
123
|
-
ServicePoint.new( self, name, opts, &callback )
|
141
|
+
ServicePoint.new( self, name, @defaults.merge( opts ), &callback )
|
124
142
|
|
125
143
|
self
|
126
144
|
end
|
@@ -270,7 +288,7 @@ module Needle
|
|
270
288
|
# otherwise +nil+ is returned.
|
271
289
|
def find_definition( name )
|
272
290
|
point = @service_points[ name ]
|
273
|
-
point =
|
291
|
+
point = parent.find_definition( name ) if parent unless point
|
274
292
|
point
|
275
293
|
end
|
276
294
|
|
@@ -279,13 +297,19 @@ module Needle
|
|
279
297
|
# If the named service does not exist, ServiceNotFound is raised.
|
280
298
|
#
|
281
299
|
# Note that this returns the instantiated service, not the service point.
|
282
|
-
|
300
|
+
#
|
301
|
+
# Also, if any pipeline element in the instantiation pipeline does not
|
302
|
+
# support extra parameters when extra parameters have been given, then an
|
303
|
+
# error will be raised.
|
304
|
+
def get( name, *args )
|
283
305
|
point = find_definition( name )
|
284
306
|
raise ServiceNotFound, "#{fullname}.#{name}" unless point
|
285
307
|
|
286
|
-
point.instance
|
308
|
+
point.instance( *args )
|
287
309
|
end
|
288
310
|
|
311
|
+
alias :[] :get
|
312
|
+
|
289
313
|
# Returns +true+ if this container includes a service point with the given
|
290
314
|
# name. Returns +false+ otherwise.
|
291
315
|
def has_key?( name )
|
@@ -296,7 +320,7 @@ module Needle
|
|
296
320
|
# service point with the given name. Returns +false+ otherwise.
|
297
321
|
def knows_key?( name )
|
298
322
|
return true if has_key?( name )
|
299
|
-
return
|
323
|
+
return parent.knows_key?( name ) if parent
|
300
324
|
false
|
301
325
|
end
|
302
326
|
|
@@ -351,17 +375,9 @@ module Needle
|
|
351
375
|
#
|
352
376
|
# container.register( :add ) { Adder.new }
|
353
377
|
# p container.add == container[:add] # => true
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
#
|
358
|
-
# Usage:
|
359
|
-
#
|
360
|
-
# container.foo { Bar.new }
|
361
|
-
# p container.foo
|
362
|
-
def method_missing( sym, *args, &block )
|
363
|
-
if block.nil? && args.empty? && knows_key?( sym )
|
364
|
-
self[sym]
|
378
|
+
def method_missing( sym, *args )
|
379
|
+
if knows_key?( sym )
|
380
|
+
get( sym, *args )
|
365
381
|
else
|
366
382
|
super
|
367
383
|
end
|
@@ -372,7 +388,36 @@ module Needle
|
|
372
388
|
# this case, #has_key? is used instead of #knows_key? so that subcontainers
|
373
389
|
# may be used as proper hashes by their parents.
|
374
390
|
def respond_to?( sym )
|
375
|
-
|
391
|
+
has_key?( sym ) || super
|
392
|
+
end
|
393
|
+
|
394
|
+
# Specifies a set of default options to use temporarily. The options are
|
395
|
+
# merged with the current set of defaults for the container. The original
|
396
|
+
# options are returned, and may be restored by invoking #use again with
|
397
|
+
# the hash that is returned. If a block is given, the registry will be
|
398
|
+
# yielded to it and the options automatically restored when the block
|
399
|
+
# returns.
|
400
|
+
def use( opts, &block ) # :yield: self
|
401
|
+
use! @defaults.merge( opts ), &block
|
402
|
+
end
|
403
|
+
|
404
|
+
# Specifies a set of default options to use temporarily. The original
|
405
|
+
# options are returned. This differs from #use in that it will completely
|
406
|
+
# replace the original options, instead of merging the parameters with
|
407
|
+
# the originals.
|
408
|
+
def use!( opts )
|
409
|
+
original = @defaults
|
410
|
+
@defaults = opts
|
411
|
+
|
412
|
+
if block_given?
|
413
|
+
begin
|
414
|
+
yield self
|
415
|
+
ensure
|
416
|
+
use! original
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
return original
|
376
421
|
end
|
377
422
|
|
378
423
|
end
|
@@ -30,7 +30,7 @@ module Needle
|
|
30
30
|
protected_instance_methods +
|
31
31
|
public_instance_methods -
|
32
32
|
[ "instance_eval", "__id__", "__send__", "initialize", "remove_const",
|
33
|
-
"method_missing", "method", "class", "inspect", "to_s", "instance_variables" ]
|
33
|
+
"method_missing", "method", "class", "inspect", "to_s", "instance_variables", "block_given?" ]
|
34
34
|
).
|
35
35
|
each { |m| undef_method m }
|
36
36
|
|
@@ -76,6 +76,38 @@ module Needle
|
|
76
76
|
@container.__send__( :require, *parms )
|
77
77
|
end
|
78
78
|
|
79
|
+
# Delegate to Container#use on the current container, but yields the
|
80
|
+
# definition context instead of the container.
|
81
|
+
def use( opts, &block )
|
82
|
+
use! @container.defaults.merge( opts ), &block
|
83
|
+
end
|
84
|
+
|
85
|
+
# Delegate to Container#use! on the current container, but yields the
|
86
|
+
# definition context instead of the container.
|
87
|
+
def use!( opts )
|
88
|
+
original = @container.use!( opts )
|
89
|
+
|
90
|
+
if block_given?
|
91
|
+
begin
|
92
|
+
yield self
|
93
|
+
ensure
|
94
|
+
@container.use! original
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
original
|
99
|
+
end
|
100
|
+
|
101
|
+
# Delegates to Container#has_key?.
|
102
|
+
def has_key?( name )
|
103
|
+
@container.has_key?( name )
|
104
|
+
end
|
105
|
+
|
106
|
+
# Delegates to Container#knows_key?.
|
107
|
+
def knows_key?( name )
|
108
|
+
@container.knows_key?( name )
|
109
|
+
end
|
110
|
+
|
79
111
|
# Any method invocation with no block and no parameters is interpreted to
|
80
112
|
# be a service reference on the wrapped container, and delegates to
|
81
113
|
# Container#[]. If the block is not given but the args are not empty, a
|
@@ -85,11 +117,7 @@ module Needle
|
|
85
117
|
# all parameters in place.
|
86
118
|
def method_missing( sym, *args, &block )
|
87
119
|
if block.nil?
|
88
|
-
|
89
|
-
@container[sym]
|
90
|
-
else
|
91
|
-
super
|
92
|
-
end
|
120
|
+
@container.get( sym, *args )
|
93
121
|
else
|
94
122
|
@container.register( sym, *args, &block )
|
95
123
|
end
|
@@ -0,0 +1,64 @@
|
|
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
|
+
require 'needle/thread'
|
19
|
+
|
20
|
+
module Needle
|
21
|
+
module Lifecycle
|
22
|
+
|
23
|
+
# The instantiation pipeline element that enforces the multiton
|
24
|
+
# multiplicity. "Multiton" multiplicity is like singleton multiplicity,
|
25
|
+
# except that the guarded instance is unique for each unique set of
|
26
|
+
# arguments passed to the multiton.
|
27
|
+
class Multiton < Needle::Pipeline::Element
|
28
|
+
set_default_priority 100
|
29
|
+
|
30
|
+
# Creates the mutex to use and initializes the cache.
|
31
|
+
def initialize_element
|
32
|
+
@mutex = QueryableMutex.new
|
33
|
+
@cached = Hash.new
|
34
|
+
@is_cached = Hash.new( false )
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns the cached reference for the given arguments, if it has been
|
38
|
+
# previously cached. Otherwise, invokes the next element in the pipeline
|
39
|
+
# and caches the result. The cached reference is returned.
|
40
|
+
def call( container, point, *args )
|
41
|
+
unless @is_cached[ args ]
|
42
|
+
@mutex.synchronize do
|
43
|
+
unless @is_cached[ args ]
|
44
|
+
@cached[ args ] = succ.call( container, point, *args )
|
45
|
+
@is_cached[ args ] = true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
@cached[ args ]
|
51
|
+
end
|
52
|
+
|
53
|
+
# Resets the caches for this multiton object.
|
54
|
+
def reset!
|
55
|
+
@mutex.synchronize do
|
56
|
+
@cached = Hash.new
|
57
|
+
@is_cached = Hash.new( false )
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|