needle 1.2.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/doc/manual-html/chapter-1.html +138 -78
- data/doc/manual-html/chapter-2.html +180 -99
- data/doc/manual-html/chapter-3.html +111 -75
- data/doc/manual-html/chapter-4.html +80 -48
- data/doc/manual-html/chapter-5.html +106 -56
- data/doc/manual-html/chapter-6.html +82 -34
- data/doc/manual-html/chapter-7.html +74 -38
- data/doc/manual-html/chapter-8.html +70 -41
- data/doc/manual-html/chapter-9.html +88 -63
- data/doc/manual-html/index.html +6 -6
- data/doc/manual-html/needle.png +0 -0
- data/doc/manual-html/{manual.css → stylesheets/manual.css} +83 -10
- data/doc/manual-html/stylesheets/ruby.css +17 -0
- data/doc/manual/chapter.erb +20 -0
- data/doc/manual/img/Needle.ai +0 -0
- data/doc/manual/img/needle.png +0 -0
- data/doc/manual/manual.rb +80 -5
- data/doc/manual/manual.yml +3 -3
- data/doc/manual/page.erb +1 -1
- data/doc/manual/parts/01_use_cases.txt +70 -70
- data/doc/manual/parts/02_creating.txt +19 -19
- data/doc/manual/parts/02_namespaces.txt +29 -29
- data/doc/manual/parts/02_services.txt +40 -41
- data/doc/manual/parts/03_conventional.txt +20 -20
- data/doc/manual/parts/03_locator.txt +44 -44
- data/doc/manual/parts/04_overview.txt +1 -1
- data/doc/manual/parts/04_setup.txt +32 -32
- data/doc/manual/parts/customizing_contexts.txt +14 -14
- data/doc/manual/parts/customizing_interceptors.txt +25 -25
- data/doc/manual/parts/customizing_namespaces.txt +12 -12
- data/doc/manual/parts/interceptors_attaching.txt +29 -30
- data/doc/manual/parts/interceptors_custom.txt +16 -16
- data/doc/manual/parts/interceptors_ordering.txt +5 -5
- data/doc/manual/parts/libraries_creating.txt +18 -18
- data/doc/manual/parts/libraries_using.txt +19 -19
- data/doc/manual/parts/logging_configuration.txt +13 -13
- data/doc/manual/parts/logging_logfactory.txt +21 -22
- data/doc/manual/parts/models_models.txt +8 -8
- data/doc/manual/parts/models_overview.txt +1 -1
- data/doc/manual/parts/models_pipelines.txt +22 -22
- data/doc/manual/{manual.css → stylesheets/manual.css} +83 -10
- data/doc/manual/stylesheets/ruby.css +17 -0
- data/lib/needle/definition-context.rb +3 -2
- data/lib/needle/lifecycle/proxy.rb +1 -1
- data/lib/needle/version.rb +1 -1
- metadata +94 -85
@@ -2,24 +2,24 @@ Creating your own interceptors is very easy. As was demonstrated earlier, you ca
|
|
2
2
|
|
3
3
|
An interceptor factory can be any object, as long as it implements the method @#new@ with two parameters, the _service point_ (service "definition") of the service that the interceptor will be bound to, and a hash of the options that were passed to the interceptor when it was attached to the service. This method should then return a new interceptor instance, which must implement the @#process@ method. The @#process@ method should accept two parameters: an object representing the _chain_ of interceptors, and the invocation _context_.
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
{{{lang=ruby,number=true,caption=Custom interceptor example
|
6
|
+
class MyInterceptorFactory
|
7
|
+
def initialize( point, options )
|
8
|
+
...
|
9
|
+
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
11
|
+
def process( chain, context )
|
12
|
+
# context.sym : the name of the method that was invoked
|
13
|
+
# context.args : the array of arguments passed to the method
|
14
|
+
# context.block : the block passed to the method, if any
|
15
|
+
# context.data : a hash that may be used to share data between interceptors
|
16
|
+
return context.process_next( context )
|
18
17
|
end
|
19
|
-
|
18
|
+
end
|
19
|
+
}}}
|
20
20
|
|
21
21
|
Once you've created your factory, you can attach it to a service:
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
{{{lang=ruby,caption=Attaching a custom interceptor
|
24
|
+
reg.intercept( :foo ).with { MyInterceptorFactory }
|
25
|
+
}}}
|
@@ -4,10 +4,10 @@ You can specify a different ordering of the interceptors by giving each one a _p
|
|
4
4
|
|
5
5
|
You can specify the priority as an option when attaching an interceptor:
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
{{{lang=ruby,number=true,caption=Setting interceptor priorities
|
8
|
+
reg.register( :foo ) { ... }
|
9
|
+
reg.intercept( :foo ).with { Something }.with_options( :priority => 100 )
|
10
|
+
reg.intercept( :foo ).with { SomethingElse }.with_options( :priority => 50 )
|
11
|
+
}}}
|
12
12
|
|
13
13
|
Without the priorities, when a method of @:foo@ was invoked, Something would be called first, and then SomethingElse. _With_ the priorities (as specified), SomethingElse would be called before Something (since SomethingElse has a lower priority).
|
@@ -2,28 +2,28 @@ This centralization is implemented by creating a module for each library you wan
|
|
2
2
|
|
3
3
|
For example, if I had a library of cryptographic routines and I wanted to make them accessible as Needle services, I would create a module to contain the service definitions. Typically, this module will be defined in a file called "services.rb", although you can certainly name it whatever you like.
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
b.des do
|
16
|
-
require 'crypto/des'
|
17
|
-
DES.new
|
18
|
-
end
|
5
|
+
{{{lang=ruby,number=true,caption=Needle-enabled library example
|
6
|
+
module Crypto
|
7
|
+
|
8
|
+
def register_services( container )
|
9
|
+
container.namespace_define( :crypto ) do |b|
|
10
|
+
b.prng do
|
11
|
+
require 'crypto/prng'
|
12
|
+
PRNG.new
|
13
|
+
end
|
19
14
|
|
20
|
-
|
15
|
+
b.des do
|
16
|
+
require 'crypto/des'
|
17
|
+
DES.new
|
21
18
|
end
|
22
|
-
end
|
23
|
-
module_function :register_services
|
24
19
|
|
20
|
+
...
|
21
|
+
end
|
25
22
|
end
|
26
|
-
|
23
|
+
module_function :register_services
|
24
|
+
|
25
|
+
end
|
26
|
+
}}}
|
27
27
|
|
28
28
|
Notice that there are no explicit dependencies on Needle, only on the interfaces Needle publishes. Thus, third-parties can add service configuration modules to their libraries without introducing dependencies on Needle.
|
29
29
|
|
@@ -1,31 +1,31 @@
|
|
1
1
|
Using the libraries is as simple as requiring the file that has the service definitions, and then invoking the @#register_services@ module function:
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
{{{lang=ruby,number=true,caption=Example of using a Needle-enabled library
|
4
|
+
require 'needle'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
reg = Needle::Registry.new
|
7
|
+
reg.define do |b|
|
8
|
+
b.foo { Foo.new }
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
require 'crypto/services'
|
11
|
+
Crypto.register_services( reg )
|
12
|
+
end
|
13
13
|
|
14
|
-
|
15
|
-
|
14
|
+
prng = reg.crypto.prng
|
15
|
+
}}}
|
16
16
|
|
17
17
|
To make this easier, the Container class has a convenience method named @#require@:
|
18
18
|
|
19
|
-
|
20
|
-
|
19
|
+
{{{lang=ruby,number=true,caption=Example of using Container#require
|
20
|
+
require 'needle'
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
reg = Needle::Registry.new
|
23
|
+
reg.define do |b|
|
24
|
+
b.foo { Foo.new }
|
25
|
+
b.require 'crypto/services', "Crypto"
|
26
|
+
end
|
27
27
|
|
28
|
-
|
29
|
-
|
28
|
+
prng = reg.crypto.prng
|
29
|
+
}}}
|
30
30
|
|
31
31
|
The @Container#require@ method will require the file, and then look for a @#register_services@ method of the named module. It will execute that method, passing the container as an argument.
|
@@ -12,19 +12,19 @@ You can configure the LogFactory when you create the registry by passing a hash
|
|
12
12
|
|
13
13
|
By default, the filename is "./needle.log", and the roll_size is one megabyte. The default level is @:debug@. If you wanted to specify a different filename and default level of @:warn@, you could do:
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
15
|
+
{{{lang=ruby,number=true,caption=Configuring the loggers
|
16
|
+
reg = Needle::Registry.new(
|
17
|
+
:logs => {
|
18
|
+
:filename => "./my-app.log",
|
19
|
+
:default_level => :warn }
|
20
|
+
)
|
21
|
+
}}}
|
22
22
|
|
23
23
|
Alternatively, you can easily put the logging configuration in a YAML file and read it in, like so:
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
{{{lang=ruby,number=true,caption=Configuring the loggers from a YAML file
|
26
|
+
require 'yaml'
|
27
|
+
reg = Needle::Registry.new(
|
28
|
+
:logs => YAML.load( File.read( "log-conf.yml" ) )
|
29
|
+
)
|
30
|
+
}}}
|
@@ -1,14 +1,14 @@
|
|
1
1
|
The LogFactory is available as a service, so that any component that needs a logger instance can gain easy access to one. The factory's service name is @:logs@.
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
{{{lang=ruby,caption=Accessing the LogFactory service
|
4
|
+
factory = reg.logs
|
5
|
+
}}}
|
6
6
|
|
7
7
|
You obtain logger instances from the factory by passing a logger name to @#get@:
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
{{{lang=ruby,caption=Getting a named logger instance
|
10
|
+
logger = reg.logs.get "a logger name"
|
11
|
+
}}}
|
12
12
|
|
13
13
|
Subsequent calls to @#get@ will return the same logger instance for the given logger name.
|
14
14
|
|
@@ -16,25 +16,24 @@ h3. Loggers for Services
|
|
16
16
|
|
17
17
|
Typically, you'll use this to assign a logger instance to a service when it is constructed. In that case, the name of the logger is the fully-qualified name of the service:
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
{{{lang=ruby,number=true,caption=Getting a logger for a service
|
20
|
+
reg.register( :foo ) do |c,p|
|
21
|
+
Foo.new( c.logs.get( p.fullname ) )
|
22
|
+
end
|
23
|
+
}}}
|
24
24
|
|
25
25
|
As a convenience, if the value passed to @#get@ responds to either @fullname@ or @name@, the return value of that message will be used as the name. Thus, you can do the following:
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
{{{lang=ruby,number=true,caption=Getting a logger for a service (simplified)
|
28
|
+
reg.register( :foo ) do |c,p|
|
29
|
+
Foo.new( c.logs.get( p ) )
|
30
|
+
end
|
31
|
+
}}}
|
32
32
|
|
33
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
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
35
|
+
{{{lang=ruby,number=true,caption=Getting a logger for a service (simplified further)
|
36
|
+
reg.register( :foo ) do |c,p|
|
37
|
+
Foo.new( c.log_for( p ) )
|
38
|
+
end
|
39
|
+
}}}
|
@@ -2,7 +2,7 @@ Specifying an entire pipeline for every service point can be tedious. For that r
|
|
2
2
|
|
3
3
|
h3. Standard Service Models:
|
4
4
|
|
5
|
-
table
|
5
|
+
table(list).
|
6
6
|
|_<. Name|_<. Pipeline|_<. Effect|
|
7
7
|
|^. @:multiton@|^. @:multiton@|The returned value will be unique for each unique parameter set given to the service.|
|
8
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.|
|
@@ -25,15 +25,15 @@ h3. Specifying a Service Model
|
|
25
25
|
|
26
26
|
You specify the service model by passing the @:model@ option when you register a service. (You must only specify _either_ the model, _or_ the pipeline, but not both.)
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
{{{lang=ruby,caption=Specifying a service model
|
29
|
+
reg.register( :foo, :model => :singleton_deferred ) {...}
|
30
|
+
}}}
|
31
31
|
|
32
32
|
h3. Defining New Models
|
33
33
|
|
34
34
|
You can create your own service models by adding the corresponding pipelines to the @:service_models@ service:
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
{{{lang=ruby,number=true,caption=Defining a custom model
|
37
|
+
reg.service_models[ :my_service_model ] = [ :singleton, :my_pipeline_element ]
|
38
|
+
reg.register( :foo, :model => :my_service_model ) {...}
|
39
|
+
}}}
|
@@ -1,3 +1,3 @@
|
|
1
|
-
Service models are the mechanism by which a client can specify how the
|
1
|
+
Service models are the mechanism by which a client can specify how the lifestyle of a particular service should be managed. By default, all services are managed as _singletons_, but it is a simple matter to choose a different behavior when a service is registered.
|
2
2
|
|
3
3
|
Underneath, service models are implemented using an _instantiation pipeline_.
|
@@ -1,10 +1,10 @@
|
|
1
1
|
An _instantiation pipeline_ is a sequence of elements, each of which knows how to perform some aspect of the instantiation of a service.
|
2
2
|
|
3
|
-
Every service consists of at least one instantiation element--the block that was given when the service was registered. Other elements may be combined with this block to enforce various aspects of
|
3
|
+
Every service consists of at least one instantiation element--the block that was given when the service was registered. Other elements may be combined with this block to enforce various aspects of lifestyle management, such as _multiplicity_ (singleton vs. prototype) and _laziness_ (deferred vs. immediate instantiation).
|
4
4
|
|
5
5
|
h3. Standard Pipeline Elements
|
6
6
|
|
7
|
-
There are
|
7
|
+
There are six standard pipeline elements available in Needle (although you may certainly create your own):
|
8
8
|
|
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).
|
@@ -23,20 +23,20 @@ h3. Custom Pipeline Elements
|
|
23
23
|
|
24
24
|
Creating new pipeline elements simple. Just create a new class that extends @Needle::Pipeline::Element@. Set the default pipeline priority (using the @#set_default_priority@ class method), and then implement the @#call@ method (accepting at least two parameters: the container and the service point).
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
end
|
26
|
+
{{{lang=ruby,number=true,caption=Custom pipeline element example
|
27
|
+
require 'needle/pipeline/element'
|
28
|
+
|
29
|
+
class MyPipelineElement < Needle::Pipeline::Element
|
30
|
+
set_default_priority 50
|
31
|
+
|
32
|
+
def call( container, point, *args )
|
33
|
+
...
|
34
|
+
result = succ.call( container, point, *args )
|
35
|
+
...
|
36
|
+
return result
|
38
37
|
end
|
39
|
-
|
38
|
+
end
|
39
|
+
}}}
|
40
40
|
|
41
41
|
To invoke the next element of the pipeline, just invoke @#succ.call(...)@.
|
42
42
|
|
@@ -48,9 +48,9 @@ h3. Added Pipelines Elements to Services
|
|
48
48
|
|
49
49
|
You can specify the pipeline elements to use for a service via the @:pipeline@ option. This must refer to an array, each element of which must be either a symbol (in which case it references an element of the @:pipeline_elements@ service), or a class (in which case it must implement the interface required by @Needle::Pipeline::Element).
|
50
50
|
|
51
|
-
|
52
|
-
|
53
|
-
|
51
|
+
{{{lang=ruby,caption=Adding pipelines to services
|
52
|
+
reg.register( :foo, :pipeline => [ :singleton, MyPipelineElement ] ) { ... }
|
53
|
+
}}}
|
54
54
|
|
55
55
|
The elements will be sorted based on their priorities, with lower priorities sorting closer to the instantiation block, and higher priorities sorting closer to the client.
|
56
56
|
|
@@ -58,7 +58,7 @@ h3. Making Custom Pipeline Elements Available
|
|
58
58
|
|
59
59
|
You can make your custom pipeline elements available (so they can be referenced by symbol, instead of class name) by adding them to the @:pipeline_elements@ service:
|
60
60
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
61
|
+
{{{lang=ruby,number=true,caption=Publishing custom pipeline elements
|
62
|
+
reg.pipeline_elements[ :my_pipeline_element ] = MyPipelineElement
|
63
|
+
reg.register( :foo, :pipeline => [ :singleton, :my_pipeline_element ] ) { ... }
|
64
|
+
}}}
|
@@ -87,8 +87,8 @@ a:hover {
|
|
87
87
|
}
|
88
88
|
|
89
89
|
#navigation ul, #navigation ol {
|
90
|
-
margin-left: 1.
|
91
|
-
padding-left: 1.
|
90
|
+
margin-left: 1.2em;
|
91
|
+
padding-left: 1.2em;
|
92
92
|
}
|
93
93
|
|
94
94
|
#navigation .license {
|
@@ -107,12 +107,6 @@ a:hover {
|
|
107
107
|
text-align: justify;
|
108
108
|
}
|
109
109
|
|
110
|
-
#content pre {
|
111
|
-
background: #FFE;
|
112
|
-
border: 1px dotted #AAA;
|
113
|
-
padding: 1em;
|
114
|
-
}
|
115
|
-
|
116
110
|
#content h1 {
|
117
111
|
background: #005;
|
118
112
|
color: #FFF;
|
@@ -186,7 +180,86 @@ a:hover {
|
|
186
180
|
text-align: center;
|
187
181
|
}
|
188
182
|
|
189
|
-
table.list
|
190
|
-
|
183
|
+
table.list {
|
184
|
+
margin: 2em;
|
185
|
+
border: 1px solid black;
|
186
|
+
background: #FFD;
|
187
|
+
padding: 0px;
|
188
|
+
border-spacing: 0px;
|
189
|
+
}
|
190
|
+
|
191
|
+
table.list th {
|
192
|
+
border-bottom: 1px solid #005;
|
191
193
|
padding-bottom: 5px;
|
194
|
+
background: #008;
|
195
|
+
color: white;
|
196
|
+
padding: 0.5em;
|
197
|
+
text-align: left;
|
198
|
+
}
|
199
|
+
|
200
|
+
table.list td {
|
201
|
+
padding: 0.2em;
|
202
|
+
text-align: left;
|
203
|
+
vertical-align: top;
|
204
|
+
border-bottom: 1px solid;
|
205
|
+
}
|
206
|
+
|
207
|
+
.prevnext {
|
208
|
+
padding: 0.5em 1em 0.5em 1em;
|
209
|
+
background: #557;
|
210
|
+
color: #FFF;
|
211
|
+
font-size: small;
|
212
|
+
font-weight: bold;
|
213
|
+
border: 1px solid #000;
|
214
|
+
}
|
215
|
+
|
216
|
+
.prevnext a {
|
217
|
+
color: #FF0;
|
218
|
+
}
|
219
|
+
|
220
|
+
.top .prevnext {
|
221
|
+
margin: 0 0 1em 0;
|
222
|
+
text-align: left;
|
223
|
+
}
|
224
|
+
|
225
|
+
.bottom .prevnext {
|
226
|
+
margin: 1em 0 0 0;
|
227
|
+
text-align: right;
|
228
|
+
}
|
229
|
+
|
230
|
+
.figure {
|
231
|
+
border: 1px solid black;
|
232
|
+
line-height: normal;
|
233
|
+
background: #FFD;
|
234
|
+
margin: 2em;
|
235
|
+
}
|
236
|
+
|
237
|
+
.figure .caption {
|
238
|
+
background: #008;
|
239
|
+
color: white;
|
240
|
+
font-weight: bold;
|
241
|
+
font-size: small;
|
242
|
+
padding: 4px 24px 4px 8px;
|
243
|
+
margin-left: -4px;
|
244
|
+
border: 1px dotted #77F;
|
245
|
+
}
|
246
|
+
|
247
|
+
.figure .body {
|
248
|
+
padding-left: 1em;
|
249
|
+
}
|
250
|
+
|
251
|
+
.figure pre {
|
252
|
+
padding: 0px;
|
253
|
+
background: transparent;
|
254
|
+
border: none;
|
255
|
+
font-size: small;
|
256
|
+
font-family: mono;
|
257
|
+
}
|
258
|
+
|
259
|
+
.figure .lineno {
|
260
|
+
text-align: right;
|
261
|
+
color: #B00;
|
262
|
+
font-family: mono;
|
263
|
+
font-size: small;
|
264
|
+
padding-right: 1em;
|
192
265
|
}
|