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
data/doc/manual/manual.yml
CHANGED
@@ -14,7 +14,7 @@ product: !^product
|
|
14
14
|
name: Needle
|
15
15
|
tagline: to the point -->
|
16
16
|
version: !!eval require "../../lib/needle/version"; Needle::Version::STRING
|
17
|
-
|
17
|
+
logo: needle.png
|
18
18
|
urls:
|
19
19
|
- Project Page: http://rubyforge.org/projects/needle
|
20
20
|
- User Manual: http://needle.rubyforge.org
|
@@ -23,8 +23,8 @@ product: !^product
|
|
23
23
|
- Needle Wiki: http://needle.rubyforge.org/wiki/wiki.pl
|
24
24
|
|
25
25
|
recent_updates:
|
26
|
-
-
|
27
|
-
- Added
|
26
|
+
- Added Needle graphic (thanks to Bruce Williams)
|
27
|
+
- Added syntax highlighting to code blocks
|
28
28
|
|
29
29
|
chapters:
|
30
30
|
|
data/doc/manual/page.erb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
<html>
|
2
2
|
<head>
|
3
3
|
<title><%= manual.product.name %> Manual<% if object %> :: <%= object.page_title %><% end %></title>
|
4
|
-
<link type="text/css" rel="stylesheet" href="manual.css" />
|
4
|
+
<link type="text/css" rel="stylesheet" href="stylesheets/manual.css" />
|
5
5
|
</head>
|
6
6
|
|
7
7
|
<body>
|
@@ -7,7 +7,7 @@ Try these on for size:
|
|
7
7
|
* "Log Method Execution":#logexec
|
8
8
|
* "Reference Another Service":#refsvc
|
9
9
|
* "Unit Testing":#unittest
|
10
|
-
* "
|
10
|
+
* "Lifestyle Management":#lifestyle
|
11
11
|
|
12
12
|
(Thanks to Howard Lewis Ship for his "HiveMind":http://jakarta.apache.org/hivemind documentation, from which some of the above bullet points were adapted.)
|
13
13
|
|
@@ -18,36 +18,36 @@ Needle has an integrated logging framework, and the ability to log execution tra
|
|
18
18
|
|
19
19
|
Consider the following code, demonstrating how this would be done without Needle:
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
21
|
+
{{{lang=ruby,number=true,caption=Logging method execution without Needle
|
22
|
+
def foo( arg1, arg2 )
|
23
|
+
@log.debug( "in foo with #{arg1} and #{arg2}" ) if @log.debug?
|
24
|
+
...
|
25
|
+
result = the_result_of_the_method
|
26
|
+
@log.debug( "finishing foo with #{result}" ) if @log.debug
|
27
|
+
return result
|
28
|
+
rescue Exception => e
|
29
|
+
@log.debug( "foo raised exception #{e.message} (#{e.class})" ) if @log.debug?
|
30
|
+
raise
|
31
|
+
end
|
32
|
+
}}}
|
33
33
|
|
34
34
|
Now, multiply that by the number of methods in your class... the logging messages quickly overpower the rest of the code, and detract from the flow of your program. This makes your program harder to debug, test, and maintain.
|
35
35
|
|
36
36
|
Now, consider the same method using Needle's integrated logging framework...
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
38
|
+
{{{lang=ruby,number=true,caption=Logging method execution with Needle
|
39
|
+
def foo( arg1, arg2 )
|
40
|
+
...
|
41
|
+
return the_result_of_the_method
|
42
|
+
end
|
43
|
+
}}}
|
44
44
|
|
45
45
|
Then, when you define the service that you want to add the logging to:
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
47
|
+
{{{lang=ruby,number=true,caption=Adding the logging interceptor to a service
|
48
|
+
registry.register( :service_name_here ) { |reg| ... }
|
49
|
+
registry.intercept( :service_name_here ).with! { logging_interceptor }
|
50
|
+
}}}
|
51
51
|
|
52
52
|
That's right. There's no explicit logging code in there. Instead, you just tell Needle that the methods of the class should be logged, and away it goes. This has the added benefit of allowing your objects to be unit tested, without spewing log messages everywhere.
|
53
53
|
|
@@ -55,41 +55,41 @@ h3. Reference Another Service <a name="#refsvc"></a>
|
|
55
55
|
|
56
56
|
Invariably in a large application services will reference other services. This is typically accomplished through something like this:
|
57
57
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
...
|
68
|
-
end
|
58
|
+
{{{lang=ruby,number=true,caption=Looking up services without Needle
|
59
|
+
class Component
|
60
|
+
...
|
61
|
+
def foo( parms )
|
62
|
+
@service ||= lookup_service
|
63
|
+
@service.do_something( parms )
|
64
|
+
end
|
65
|
+
|
66
|
+
def lookup_service
|
69
67
|
...
|
70
68
|
end
|
71
|
-
|
69
|
+
...
|
70
|
+
end
|
71
|
+
}}}
|
72
72
|
|
73
73
|
Whether the lookup is done lazily, as shown above, or when the class is first instantiated is irrelevant. The point is that you either have to implement a bunch of code to look up a service based on some criteria, or you hard code the class of the service (which creates tight coupling and makes things like unit testing harder).
|
74
74
|
|
75
75
|
With Needle, you just declare a setter for the service, and then tell Needle that the class depends on the other service:
|
76
76
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
end
|
84
|
-
...
|
77
|
+
{{{lang=ruby,number=true,caption=Wiring services with Needle
|
78
|
+
class Component
|
79
|
+
attr_writer :service
|
80
|
+
...
|
81
|
+
def foo( parms )
|
82
|
+
@service.do_something( parms )
|
85
83
|
end
|
84
|
+
...
|
85
|
+
end
|
86
86
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
87
|
+
registry.register( :component ) do |reg|
|
88
|
+
c = Component.new
|
89
|
+
c.service = reg.some_other_service
|
90
|
+
c
|
91
|
+
end
|
92
|
+
}}}
|
93
93
|
|
94
94
|
Then, when your service is instantiated, Needle will automatically look for and instantiate the dependencies for you. This makes for cleaner code, and looser coupling between services.
|
95
95
|
|
@@ -101,34 +101,34 @@ Needle, by its very nature, encourages loose coupling of components. Also, becau
|
|
101
101
|
|
102
102
|
Consider this tightly coupled example:
|
103
103
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
104
|
+
{{{lang=ruby,number=true,caption=Tight coupling
|
105
|
+
def foo( args )
|
106
|
+
@some_dependency ||= MyNewDependency.new
|
107
|
+
@some_dependency.do_something(args)
|
108
|
+
end
|
109
|
+
}}}
|
110
110
|
|
111
111
|
It is impossible to test the method @#foo@ without also testing the MyNewDependency class. However, if the @@some_dependency@ object is made a property that is set externally, you can replace it at test time with a blank:
|
112
112
|
|
113
|
-
|
114
|
-
|
113
|
+
{{{lang=ruby,number=true,caption=Loose coupling
|
114
|
+
attr_writer :some_dependency
|
115
115
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
116
|
+
def foo( args )
|
117
|
+
@some_dependency.do_something( args )
|
118
|
+
end
|
119
|
+
}}}
|
120
120
|
|
121
121
|
The unit test would become something like this:
|
122
122
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
123
|
+
{{{lang=ruby,number=true,caption=Unit testing with a mock object
|
124
|
+
def test_foo
|
125
|
+
@obj.some_dependecy = MyMockDependency.new
|
126
|
+
@obj.foo( args )
|
127
|
+
assert @obj.is_in_some_state
|
128
|
+
end
|
129
|
+
}}}
|
130
130
|
|
131
|
-
h3.
|
131
|
+
h3. Lifestyle Management <a name="#lifestyle"></a>
|
132
132
|
|
133
133
|
Singleton objects are a fact of life in complex systems. The singleton design pattern is powerful and useful. However, using the Singleton mixin, or declaring methods at the class level, can make your code difficult to unit test since the state of such objects cannot be easily reset.
|
134
134
|
|
@@ -136,6 +136,6 @@ Needle has a solution. You can tell Needle to treat a service as either a _proto
|
|
136
136
|
|
137
137
|
Your object is still just a plain ol' ordinary Ruby object, but Needle has effectively transformed it into a singleton. This means you can unit test it as if it were nothing special, but when it is used in your application it will act like a singleton.
|
138
138
|
|
139
|
-
|
139
|
+
Lifestyle management also means that you can control _when_ a service is instantiated. The _prototype_ and _singleton_ models will always be instantiated as soon as they are requested. Sometimes, though, you don't want that--you'd like the instantiation to be deferred as late as possible.
|
140
140
|
|
141
141
|
With Needle, you can indicate that a service should use deferred instantiation. This will cause the service to not actually be instantiated until a method is actually invoked on it. Using this model, you can have services depend on themselves, or other forms of cyclical dependencies.
|
@@ -1,39 +1,39 @@
|
|
1
1
|
Creating a registry is as simple as calling @Needle::Registry.new@. This will give you a new registry object, bootstrapped to contain a few general services.
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
{{{lang=ruby,number=true,caption=Creating a registry
|
4
|
+
require 'needle'
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
registry = Needle::Registry.new
|
7
|
+
}}}
|
8
8
|
|
9
9
|
Once you have the reference to the registry, you can register services with it, create new namespaces in it, and so forth.
|
10
10
|
|
11
11
|
Alternatively, you can pass a block to @#new@:
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
{{{lang=ruby,number=true,caption=Creating a registry with a block
|
14
|
+
registry = Needle::Registry.new do |r|
|
15
|
+
...
|
16
|
+
end
|
17
|
+
}}}
|
18
18
|
|
19
19
|
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.
|
20
20
|
|
21
21
|
Another convenience method is @#define!@:
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
{{{lang=ruby,number=true,caption=Creating a registry with #define!
|
24
|
+
registry = Needle::Registry.define! do
|
25
|
+
...
|
26
|
+
end
|
27
|
+
}}}
|
28
28
|
|
29
29
|
This block accepts no parameters, and evaluates the block as if it were passed to @Registry#define!@ (see below).
|
30
30
|
|
31
31
|
There can be problems with using @define!@, however, since it uses @instance_eval@ to evaluate the block within the context of another object. If you find yourself running into scoping issues, you might want to consider using @#define@:
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
33
|
+
{{{lang=ruby,number=true,caption=Creating a registry with #define
|
34
|
+
registry = Needle::Registry.define do |b|
|
35
|
+
...
|
36
|
+
end
|
37
|
+
}}}
|
38
38
|
|
39
39
|
This block accepts a single parameter--a "builder" object to aid in registering services--and evaluates the block as if it were passed to @Registry#define@ (see below).
|
@@ -6,51 +6,51 @@ Namespaces allow you to organize your services. The feature has many different a
|
|
6
6
|
|
7
7
|
Creating a namespace is as easy as invoking the @#namespace@ method of the registry (or of another namespace):
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
{{{lang=ruby,caption=Creating a namespace
|
10
|
+
registry.namespace :stuff
|
11
|
+
}}}
|
12
12
|
|
13
13
|
This would create a new namespace in the registry called @:stuff@. The application may then proceed to register services inside that namespace:
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
{{{lang=ruby,number=true,caption=Registering services in a namespace
|
16
|
+
registry.stuff.register( :foo ) { Bar.new }
|
17
|
+
...
|
18
|
+
svc = registry.stuff.foo
|
19
|
+
}}}
|
20
20
|
|
21
21
|
Here's a tip: _namespaces are just a special kind of service._ This means that you can access namespaces in the same ways that you can access services:
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
{{{lang=ruby,caption=Accessing a namespace
|
24
|
+
svc = registry[:stuff][:foo]
|
25
|
+
}}}
|
26
26
|
|
27
27
|
h3. Convenience Methods
|
28
28
|
|
29
29
|
Because it is often the case that you will be creating a namespace and then immediately registering services on it, you can pass a block to @namespace@. The block will receive a reference to the new namespace:
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
31
|
+
{{{lang=ruby,number=true,caption=More registering services in a namespace
|
32
|
+
registry.namespace :stuff do |spc|
|
33
|
+
spc.register( :foo ) { Bar.new }
|
34
|
+
...
|
35
|
+
end
|
36
|
+
}}}
|
37
37
|
|
38
38
|
If you prefer the @define@ approach to registering services, you may like @namespace_define@, which creates the new namespace and immediately calls @define@ on it:
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
40
|
+
{{{lang=ruby,number=true,caption=Creating namespaces with #namespace_define
|
41
|
+
registry.namespace_define :stuff do |b|
|
42
|
+
b.foo { Bar.new }
|
43
|
+
...
|
44
|
+
end
|
45
|
+
}}}
|
46
46
|
|
47
47
|
And, to mirror the @namespace_define@ method, there is also a @namespace_define!@ method. This method creates a new namespace and then does a @define!@ call on that namespace.
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
49
|
+
{{{lang=ruby,number=true,caption=Creating namespaces with #namespace_define!
|
50
|
+
registry.namespace_define! :stuff do
|
51
|
+
foo { Bar.new }
|
52
|
+
...
|
53
|
+
end
|
54
|
+
}}}
|
55
55
|
|
56
56
|
The above code would create a new namespace called @:stuff@ in the registry, and would then proceed to register a service called @:foo@ in the new namespace.
|
@@ -1,20 +1,20 @@
|
|
1
1
|
Registering services with a Needle registry is very straightforward. The simplest way to do it is:
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
{{{lang=ruby,caption=Registering services
|
4
|
+
registry.register( :foo ) { Bar.new }
|
5
|
+
}}}
|
6
6
|
|
7
7
|
The above will register a new service with the registry, naming it @:foo@. When @:foo@ is requested from the registry, a new instance of @Bar@ will be instantiated and returned.
|
8
8
|
|
9
9
|
You get services from the registry in either of two ways:
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
{{{lang=ruby,number=true,caption=Accessing services
|
12
|
+
# Treating the registry as a Hash
|
13
|
+
svc = registry[:foo]
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
# Treating the service as a property of the registry
|
16
|
+
svc = registry.foo
|
17
|
+
}}}
|
18
18
|
|
19
19
|
h3. Convenience Methods
|
20
20
|
|
@@ -22,36 +22,36 @@ Because you will often need to register many services with a registry at once, t
|
|
22
22
|
|
23
23
|
The first is @define@. Just pass a block to define that accepts one parameter. This parameter will be a "builder" object that allows you to define services just by sending them as messages to the builder:
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
25
|
+
{{{lang=ruby,number=true,caption=Defining services
|
26
|
+
registry.define do |b|
|
27
|
+
b.foo { Bar.new }
|
28
|
+
b.bar { Foo.new }
|
29
|
+
...
|
30
|
+
end
|
31
|
+
}}}
|
32
32
|
|
33
33
|
Alternative, you can call @define!@, passing a block that accepts no parameters. This block will be evaluated in the "builder" object's context, with any unrecognized method call being interpreted as a new service registration of that name:
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
35
|
+
{{{lang=ruby,number=true,caption=Defining services via #define!
|
36
|
+
registry.define! do
|
37
|
+
foo { Bar.new }
|
38
|
+
bar { Foo.new }
|
39
|
+
...
|
40
|
+
end
|
41
|
+
}}}
|
42
42
|
|
43
43
|
Both of the above will register two new services with the registry, @:foo@ and @:bar@.
|
44
44
|
|
45
|
-
h3. Default
|
45
|
+
h3. Default Lifestyle
|
46
46
|
|
47
47
|
By default, a service is only instantiated once per registry. This means that (using the above example) if the service @:foo@ were queried twice, the registry would return the same object for both queries:
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
{{{lang=ruby,number=true,caption=Demonstrating singleton service behavior
|
50
|
+
svc1 = registry.foo
|
51
|
+
svc2 = registry.foo
|
52
52
|
|
53
|
-
|
54
|
-
|
53
|
+
p svc1.object_id == svc2.object_id #=> true
|
54
|
+
}}}
|
55
55
|
|
56
56
|
You can change this behavior, with _service models_. See the chapter on Service Models for more information.
|
57
57
|
|
@@ -61,14 +61,14 @@ Needle also supports _parameterized services_. These are services that, when req
|
|
61
61
|
|
62
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
63
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
64
|
+
{{{lang=ruby,number=true,caption=Parameterized services
|
65
|
+
registry.register( :printer, :model => :multiton ) do |c,p,name|
|
66
|
+
Printer.new( c.log_for( p ), name )
|
67
|
+
end
|
68
68
|
|
69
|
-
|
70
|
-
|
71
|
-
|
69
|
+
mono = registry.printer( :monochrome )
|
70
|
+
color = registry.printer( :color )
|
71
|
+
}}}
|
72
72
|
|
73
73
|
There are a few things to note about the above example:
|
74
74
|
|
@@ -80,10 +80,9 @@ There are a few things to note about the above example:
|
|
80
80
|
|
81
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
82
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
83
|
+
{{{lang=ruby,number=true,caption=Accessing parameterized services
|
84
|
+
dot_matrix = registry[ :printer, :dot_matrix ]
|
85
|
+
ink_jet = registry.get( :printer, :ink_jet )
|
86
|
+
}}}
|
87
87
|
|
88
88
|
Choose the style that works best for you.
|
89
|
-
|