needle 1.2.0 → 1.2.1
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.
- 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
|
}
|