needle 0.6.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/doc/faq/faq.rb +143 -0
- data/doc/faq/faq.yml +75 -0
- data/doc/manual-html/chapter-1.html +26 -6
- data/doc/manual-html/chapter-2.html +39 -9
- data/doc/manual-html/chapter-3.html +177 -8
- data/doc/manual-html/chapter-4.html +108 -8
- data/doc/manual-html/chapter-5.html +28 -8
- data/doc/manual-html/chapter-6.html +28 -8
- data/doc/manual-html/chapter-7.html +29 -9
- data/doc/manual-html/chapter-8.html +176 -0
- data/doc/manual-html/index.html +26 -6
- data/doc/manual/manual.rb +1 -1
- data/doc/manual/manual.yml +7 -0
- data/doc/manual/parts/02_creating.txt +12 -2
- data/doc/manual/parts/03_conventional.txt +29 -0
- data/doc/manual/parts/03_locator.txt +60 -0
- data/doc/manual/parts/03_overview.txt +19 -0
- data/doc/manual/parts/04_overview.txt +9 -0
- data/doc/manual/parts/04_setup.txt +44 -0
- data/lib/needle/container.rb +68 -11
- data/lib/needle/registry.rb +25 -28
- data/lib/needle/service-point.rb +6 -1
- data/lib/needle/version.rb +1 -1
- data/test/models/model_test.rb +21 -1
- data/test/tc_container.rb +28 -1
- data/test/tc_registry.rb +22 -5
- data/test/tc_service_point.rb +9 -0
- metadata +11 -2
data/doc/faq/faq.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'redcloth'
|
3
|
+
|
4
|
+
def process_faq_list( faqs )
|
5
|
+
puts "<ul>"
|
6
|
+
faqs.each do |faq|
|
7
|
+
process_faq_list_item faq
|
8
|
+
end
|
9
|
+
puts "</ul>"
|
10
|
+
end
|
11
|
+
|
12
|
+
def process_faq_list_item( faq )
|
13
|
+
question = faq.keys.first
|
14
|
+
answer = faq.values.first
|
15
|
+
|
16
|
+
print "<li>"
|
17
|
+
|
18
|
+
question_text = RedCloth.new(question).to_html.gsub( %r{</?p>},"" )
|
19
|
+
if answer.is_a?( Array )
|
20
|
+
puts question_text
|
21
|
+
process_faq_list answer
|
22
|
+
else
|
23
|
+
print "<a href='##{question.id}'>#{question_text}</a>"
|
24
|
+
end
|
25
|
+
|
26
|
+
puts "</li>"
|
27
|
+
end
|
28
|
+
|
29
|
+
def process_faq_descriptions( faqs, path=nil )
|
30
|
+
faqs.each do |faq|
|
31
|
+
process_faq_description faq, path
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def process_faq_description( faq, path )
|
36
|
+
question = faq.keys.first
|
37
|
+
path = ( path ? path + " " : "" ) + question
|
38
|
+
answer = faq.values.first
|
39
|
+
|
40
|
+
if answer.is_a?( Array )
|
41
|
+
process_faq_descriptions( answer, path )
|
42
|
+
else
|
43
|
+
title = RedCloth.new( path ).to_html.gsub( %r{</?p>}, "" )
|
44
|
+
answer = RedCloth.new( answer || "" )
|
45
|
+
|
46
|
+
puts "<a name='#{question.id}'></a>"
|
47
|
+
puts "<div class='faq-title'>#{title}</div>"
|
48
|
+
puts "<div class='faq-answer'>#{add_api_links(answer.to_html)}</div>"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
API_OBJECTS = [
|
53
|
+
"Container", "Interceptor", "LogFactory", "Logger", "LoggingInterceptor",
|
54
|
+
"Registry", "ServicePoint", "QueryableMutex", "Pipeline::Collection",
|
55
|
+
"Pipeline::Element" ].inject( "(" ) { |acc,name|
|
56
|
+
acc << "|" if acc.length > 1
|
57
|
+
acc << name
|
58
|
+
acc
|
59
|
+
} + ")"
|
60
|
+
|
61
|
+
def add_api_links( text )
|
62
|
+
text.gsub( /#{API_OBJECTS}(#(\w+))?/ ) do
|
63
|
+
disp_obj = obj = $1
|
64
|
+
|
65
|
+
method = $3
|
66
|
+
s = "<a href='http://needle.rubyforge.org/api/classes/Needle/#{obj}.html'>#{disp_obj}"
|
67
|
+
s << "##{method}" if method
|
68
|
+
s << "</a>"
|
69
|
+
s
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
faqs = YAML.load( File.read( "faq.yml" ) )
|
74
|
+
|
75
|
+
puts <<-EOF
|
76
|
+
<html>
|
77
|
+
<head>
|
78
|
+
<title>Needle FAQ</title>
|
79
|
+
<style type="text/css">
|
80
|
+
a, a:visited, a:active {
|
81
|
+
color: #00F;
|
82
|
+
text-decoration: none;
|
83
|
+
}
|
84
|
+
|
85
|
+
a:hover {
|
86
|
+
text-decoration: underline;
|
87
|
+
}
|
88
|
+
|
89
|
+
.faq-list {
|
90
|
+
color: #000;
|
91
|
+
font-family: vera-sans, verdana, arial, sans-serif;
|
92
|
+
}
|
93
|
+
|
94
|
+
.faq-title {
|
95
|
+
background: #007;
|
96
|
+
color: #FFF;
|
97
|
+
font-family: vera-sans, verdana, arial, sans-serif;
|
98
|
+
padding-left: 1em;
|
99
|
+
padding-top: 0.5em;
|
100
|
+
padding-bottom: 0.5em;
|
101
|
+
font-weight: bold;
|
102
|
+
font-size: large;
|
103
|
+
border: 1px solid #000;
|
104
|
+
}
|
105
|
+
|
106
|
+
.faq-answer {
|
107
|
+
margin-left: 1em;
|
108
|
+
color: #000;
|
109
|
+
font-family: vera-sans, verdana, arial, sans-serif;
|
110
|
+
}
|
111
|
+
|
112
|
+
.faq-answer pre {
|
113
|
+
margin-left: 1em;
|
114
|
+
color: #000;
|
115
|
+
background: #FFE;
|
116
|
+
font-size: normal;
|
117
|
+
border: 1px dotted #CCC;
|
118
|
+
padding: 1em;
|
119
|
+
}
|
120
|
+
|
121
|
+
h1 {
|
122
|
+
background: #005;
|
123
|
+
color: #FFF;
|
124
|
+
font-family: vera-sans, verdana, arial, sans-serif;
|
125
|
+
padding-left: 1em;
|
126
|
+
padding-top: 1em;
|
127
|
+
padding-bottom: 1em;
|
128
|
+
font-weight: bold;
|
129
|
+
font-size: x-large;
|
130
|
+
border: 1px solid #00F;
|
131
|
+
}
|
132
|
+
</style>
|
133
|
+
</head>
|
134
|
+
<body>
|
135
|
+
<h1>Needle FAQ</h1>
|
136
|
+
<div class="faq-list">
|
137
|
+
EOF
|
138
|
+
|
139
|
+
process_faq_list( faqs )
|
140
|
+
puts "</div>"
|
141
|
+
process_faq_descriptions( faqs )
|
142
|
+
|
143
|
+
puts "</body></html>"
|
data/doc/faq/faq.yml
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
---
|
2
|
+
- "What is a...":
|
3
|
+
- "container?": >-
|
4
|
+
A _container_ is collection of service points and other containers. It
|
5
|
+
is used to organize services. Each container has access to all of the
|
6
|
+
service points in its ancestor containers.
|
7
|
+
|
8
|
+
- "registry?": >-
|
9
|
+
A _registry_ is a special kind of container that has no parent container.
|
10
|
+
It also defines a few services (such as the LoggingInterceptor, and
|
11
|
+
the various service models and pipeline elements), so that they are
|
12
|
+
available by default to all of the services it contains.
|
13
|
+
|
14
|
+
- "service point?": >-
|
15
|
+
A _service point_ is the definition of a service. Just as a class is the
|
16
|
+
definition of an object, and you instantiate an object from a class, so
|
17
|
+
do you instantiate services from service points.
|
18
|
+
|
19
|
+
- "service?": >-
|
20
|
+
A _service_ is the instantiation of a service point.
|
21
|
+
|
22
|
+
- "service model?": >-
|
23
|
+
A _service model_ is a description of the lifecycle of a service. By
|
24
|
+
default, all services are _singletons_, meaning that every time you ask
|
25
|
+
a container for a particular service, you'll get the same object
|
26
|
+
instance back.
|
27
|
+
|
28
|
+
|
29
|
+
There are other service models available, though, including "prototype"
|
30
|
+
(which returns a new instance for each request of a service) and
|
31
|
+
"deferred" (which returns a proxy, deferring the instatiation of the
|
32
|
+
service itself until a method is invoked on the service).
|
33
|
+
|
34
|
+
- "interceptor?": >-
|
35
|
+
An _interceptor_ is an object that may be placed between the client and
|
36
|
+
a service. Every request to the service is thus _intercepted_ by that
|
37
|
+
object, which can do operations on the request (such as logging) and may
|
38
|
+
even reroute or ignore the request altogether. This provides a kind of
|
39
|
+
"poor man's AOP", since you can do "before", "after", and "around" advice
|
40
|
+
on the methods of a service.
|
41
|
+
|
42
|
+
|
43
|
+
Needle comes with one standard interceptor, the LoggingInterceptor. It
|
44
|
+
will log a message on method entry and exit, and also when an exception
|
45
|
+
is raised.
|
46
|
+
|
47
|
+
- "pipeline?": >-
|
48
|
+
In Needle, the _instantiation pipeline_ is used to control how and when
|
49
|
+
services are instantiated. The _service models_ are implemented as
|
50
|
+
pipelines.
|
51
|
+
|
52
|
+
|
53
|
+
Just as the _interceptors_ are for hooking into method invocations, the
|
54
|
+
pipelines are for hooking into service instantiations. Every time a
|
55
|
+
service is requested, it's instantiation pipeline is executed. By
|
56
|
+
choosing the appropriate kinds of pipeline elements, all of the available
|
57
|
+
service models can be implemented (prototype, prototype_deferred,
|
58
|
+
singleton, singleton_deferred, etc.).
|
59
|
+
|
60
|
+
- "How do I...":
|
61
|
+
- "create a new registry?":
|
62
|
+
- "register a service?":
|
63
|
+
- "create a namespace?":
|
64
|
+
- "write log messages?":
|
65
|
+
- "exclude methods from being intercepted?":
|
66
|
+
- "When should I...":
|
67
|
+
- "specify a service model?":
|
68
|
+
- "Like, :prototype?":
|
69
|
+
- "Like, :prototype_deferred?":
|
70
|
+
- "Like, :singleton?":
|
71
|
+
- "Like, :singleton_deferred?":
|
72
|
+
- "Like, :threaded?":
|
73
|
+
- "Like, :threaded_deferred?":
|
74
|
+
- "use interceptors?":
|
75
|
+
- "create my own pipeline element?":
|
@@ -14,8 +14,8 @@
|
|
14
14
|
</div>
|
15
15
|
</td><td valign='middle' align='right'>
|
16
16
|
<div class="info">
|
17
|
-
Needle Version: <strong>0.
|
18
|
-
Manual Last Updated: <strong>2004-10-
|
17
|
+
Needle Version: <strong>0.9.0</strong><br />
|
18
|
+
Manual Last Updated: <strong>2004-10-28 15:34 GMT</strong>
|
19
19
|
</div>
|
20
20
|
</td></tr>
|
21
21
|
</table>
|
@@ -70,27 +70,37 @@
|
|
70
70
|
|
71
71
|
<li>
|
72
72
|
<a href="chapter-3.html">
|
73
|
-
|
73
|
+
Service Locator
|
74
74
|
</a>
|
75
75
|
|
76
76
|
<ol type="1">
|
77
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
|
+
|
78
84
|
</ol>
|
79
85
|
</li>
|
80
86
|
|
81
87
|
<li>
|
82
88
|
<a href="chapter-4.html">
|
83
|
-
|
89
|
+
Dependency Injection
|
84
90
|
</a>
|
85
91
|
|
86
92
|
<ol type="1">
|
87
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
|
+
|
88
98
|
</ol>
|
89
99
|
</li>
|
90
100
|
|
91
101
|
<li>
|
92
102
|
<a href="chapter-5.html">
|
93
|
-
|
103
|
+
Interceptors
|
94
104
|
</a>
|
95
105
|
|
96
106
|
<ol type="1">
|
@@ -100,7 +110,7 @@
|
|
100
110
|
|
101
111
|
<li>
|
102
112
|
<a href="chapter-6.html">
|
103
|
-
|
113
|
+
Service Models
|
104
114
|
</a>
|
105
115
|
|
106
116
|
<ol type="1">
|
@@ -110,6 +120,16 @@
|
|
110
120
|
|
111
121
|
<li>
|
112
122
|
<a href="chapter-7.html">
|
123
|
+
Logging
|
124
|
+
</a>
|
125
|
+
|
126
|
+
<ol type="1">
|
127
|
+
|
128
|
+
</ol>
|
129
|
+
</li>
|
130
|
+
|
131
|
+
<li>
|
132
|
+
<a href="chapter-8.html">
|
113
133
|
Creating Libraries
|
114
134
|
</a>
|
115
135
|
|
@@ -14,8 +14,8 @@
|
|
14
14
|
</div>
|
15
15
|
</td><td valign='middle' align='right'>
|
16
16
|
<div class="info">
|
17
|
-
Needle Version: <strong>0.
|
18
|
-
Manual Last Updated: <strong>2004-10-
|
17
|
+
Needle Version: <strong>0.9.0</strong><br />
|
18
|
+
Manual Last Updated: <strong>2004-10-28 15:34 GMT</strong>
|
19
19
|
</div>
|
20
20
|
</td></tr>
|
21
21
|
</table>
|
@@ -70,27 +70,37 @@
|
|
70
70
|
|
71
71
|
<li>
|
72
72
|
<a href="chapter-3.html">
|
73
|
-
|
73
|
+
Service Locator
|
74
74
|
</a>
|
75
75
|
|
76
76
|
<ol type="1">
|
77
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
|
+
|
78
84
|
</ol>
|
79
85
|
</li>
|
80
86
|
|
81
87
|
<li>
|
82
88
|
<a href="chapter-4.html">
|
83
|
-
|
89
|
+
Dependency Injection
|
84
90
|
</a>
|
85
91
|
|
86
92
|
<ol type="1">
|
87
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
|
+
|
88
98
|
</ol>
|
89
99
|
</li>
|
90
100
|
|
91
101
|
<li>
|
92
102
|
<a href="chapter-5.html">
|
93
|
-
|
103
|
+
Interceptors
|
94
104
|
</a>
|
95
105
|
|
96
106
|
<ol type="1">
|
@@ -100,7 +110,7 @@
|
|
100
110
|
|
101
111
|
<li>
|
102
112
|
<a href="chapter-6.html">
|
103
|
-
|
113
|
+
Service Models
|
104
114
|
</a>
|
105
115
|
|
106
116
|
<ol type="1">
|
@@ -110,6 +120,16 @@
|
|
110
120
|
|
111
121
|
<li>
|
112
122
|
<a href="chapter-7.html">
|
123
|
+
Logging
|
124
|
+
</a>
|
125
|
+
|
126
|
+
<ol type="1">
|
127
|
+
|
128
|
+
</ol>
|
129
|
+
</li>
|
130
|
+
|
131
|
+
<li>
|
132
|
+
<a href="chapter-8.html">
|
113
133
|
Creating Libraries
|
114
134
|
</a>
|
115
135
|
|
@@ -191,15 +211,25 @@
|
|
191
211
|
|
192
212
|
<p>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.</p>
|
193
213
|
|
194
|
-
<p>
|
214
|
+
<p>Another convenience method is <code>#define!</code>:</p>
|
215
|
+
|
216
|
+
<pre>
|
217
|
+
registry = Needle::Registry.define! do
|
218
|
+
...
|
219
|
+
end
|
220
|
+
</pre>
|
221
|
+
|
222
|
+
<p>This block accepts no parameters, and evaluates the block as if it were passed to <code>Registry#define!</code> (see below).</p>
|
223
|
+
|
224
|
+
<p>There can be problems with using <code>define!</code>, however, since it uses <code>instance_eval</code> to evaluate the block within the context of another object. If you find yourself running into scoping issues, you might want to consider using <code>#define</code>:</p>
|
195
225
|
|
196
226
|
<pre>
|
197
|
-
registry = Needle::Registry.
|
227
|
+
registry = Needle::Registry.define do |b|
|
198
228
|
...
|
199
229
|
end
|
200
230
|
</pre>
|
201
231
|
|
202
|
-
<p>This block accepts
|
232
|
+
<p>This block accepts a single parameter—a “builder” object to aid in registering services—and evaluates the block as if it were passed to <code>Registry#define</code> (see below).<br />
|
203
233
|
</p>
|
204
234
|
</div>
|
205
235
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<html>
|
2
2
|
<head>
|
3
|
-
<title>Needle Manual :: Chapter 3:
|
3
|
+
<title>Needle Manual :: Chapter 3: Service Locator</title>
|
4
4
|
<link type="text/css" rel="stylesheet" href="manual.css" />
|
5
5
|
</head>
|
6
6
|
|
@@ -14,8 +14,8 @@
|
|
14
14
|
</div>
|
15
15
|
</td><td valign='middle' align='right'>
|
16
16
|
<div class="info">
|
17
|
-
Needle Version: <strong>0.
|
18
|
-
Manual Last Updated: <strong>2004-10-
|
17
|
+
Needle Version: <strong>0.9.0</strong><br />
|
18
|
+
Manual Last Updated: <strong>2004-10-28 15:34 GMT</strong>
|
19
19
|
</div>
|
20
20
|
</td></tr>
|
21
21
|
</table>
|
@@ -70,27 +70,37 @@
|
|
70
70
|
|
71
71
|
<li><strong>
|
72
72
|
<a href="chapter-3.html">
|
73
|
-
|
73
|
+
Service Locator
|
74
74
|
</a>
|
75
75
|
</strong> <big>←</big>
|
76
76
|
<ol type="1">
|
77
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
|
+
|
78
84
|
</ol>
|
79
85
|
</li>
|
80
86
|
|
81
87
|
<li>
|
82
88
|
<a href="chapter-4.html">
|
83
|
-
|
89
|
+
Dependency Injection
|
84
90
|
</a>
|
85
91
|
|
86
92
|
<ol type="1">
|
87
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
|
+
|
88
98
|
</ol>
|
89
99
|
</li>
|
90
100
|
|
91
101
|
<li>
|
92
102
|
<a href="chapter-5.html">
|
93
|
-
|
103
|
+
Interceptors
|
94
104
|
</a>
|
95
105
|
|
96
106
|
<ol type="1">
|
@@ -100,7 +110,7 @@
|
|
100
110
|
|
101
111
|
<li>
|
102
112
|
<a href="chapter-6.html">
|
103
|
-
|
113
|
+
Service Models
|
104
114
|
</a>
|
105
115
|
|
106
116
|
<ol type="1">
|
@@ -110,6 +120,16 @@
|
|
110
120
|
|
111
121
|
<li>
|
112
122
|
<a href="chapter-7.html">
|
123
|
+
Logging
|
124
|
+
</a>
|
125
|
+
|
126
|
+
<ol type="1">
|
127
|
+
|
128
|
+
</ol>
|
129
|
+
</li>
|
130
|
+
|
131
|
+
<li>
|
132
|
+
<a href="chapter-8.html">
|
113
133
|
Creating Libraries
|
114
134
|
</a>
|
115
135
|
|
@@ -143,7 +163,156 @@
|
|
143
163
|
|
144
164
|
<div id="content">
|
145
165
|
|
146
|
-
<h1>3.
|
166
|
+
<h1>3. Service Locator</h1>
|
167
|
+
|
168
|
+
|
169
|
+
|
170
|
+
<h2>
|
171
|
+
<a name="s1"></a>
|
172
|
+
3.1. Overview
|
173
|
+
</h2>
|
174
|
+
|
175
|
+
|
176
|
+
|
177
|
+
<div class="section">
|
178
|
+
<p>The <em>service locator</em> design pattern can be considered a subset of dependency injection. Because it is simpler, it is as good of a place to start teaching DI as any.</p>
|
179
|
+
|
180
|
+
<p>To demonstrate both techniques, we’ll pretend we’re going to write an online forum application. To start, let’s come up with a rough design by cataloging the components we’ll need.</p>
|
181
|
+
|
182
|
+
<ul>
|
183
|
+
<li><code>Logger</code>. This will be used to write messages to a file.</li>
|
184
|
+
<li><code>Authenticator</code>. This will be used to validate whether a user is who they say they are.</li>
|
185
|
+
<li><code>Database</code>. This encapsulates access to the database that will store our forum data.</li>
|
186
|
+
<li><code>Session</code>. This represents a single user’s session.</li>
|
187
|
+
<li><code>View</code>. The presentation manager, used to render pages to the user.</li>
|
188
|
+
<li><code>Application</code>. The controller that ties it all together.</li>
|
189
|
+
</ul>
|
190
|
+
|
191
|
+
<p>(Of course, a <em>real</em> online forum application would be significantly more complex, but the above components will do for our puroses.)</p>
|
192
|
+
|
193
|
+
<p>The dependencies between these components are:</p>
|
194
|
+
|
195
|
+
<ul>
|
196
|
+
<li><code>Authenticator</code> <em>has</em> <code>Database</code> (for querying user authentication information) and <code>Logger</code></li>
|
197
|
+
<li><code>Database</code> <em>has</em> <code>Logger</code> (for indicating database accesses and query times)</li>
|
198
|
+
<li><code>Session</code> <em>has</em> <code>Database</code> (for storing session information) and <code>Logger</code></li>
|
199
|
+
<li><code>Application</code> <em>has</em> <code>Database</code>, <code>View</code>, <code>Session</code>, and <code>Authenticator</code>, and <code>Logger</code></li>
|
200
|
+
</ul>
|
201
|
+
</div>
|
202
|
+
|
203
|
+
|
204
|
+
|
205
|
+
<h2>
|
206
|
+
<a name="s2"></a>
|
207
|
+
3.2. Conventional Architecture
|
208
|
+
</h2>
|
209
|
+
|
210
|
+
|
211
|
+
|
212
|
+
<div class="section">
|
213
|
+
<p>A conventional architecture will have each component instantiate its own dependencies. For example, the <code>Application</code> would do something like this:</p>
|
214
|
+
|
215
|
+
<pre>
|
216
|
+
class Application
|
217
|
+
def initialize
|
218
|
+
@logger = Logger.new
|
219
|
+
@authenticator = Authenticator.new
|
220
|
+
@database = Database.new
|
221
|
+
@view = View.new
|
222
|
+
@session = Session.new
|
223
|
+
end
|
224
|
+
end
|
225
|
+
</pre>
|
226
|
+
|
227
|
+
<p>However, the above is already flawed, because the <code>Authenticator</code> and the <code>Session</code> both need access to the <code>Database</code>, so you really need to make sure you instantiate things in the right order and pass them as parameters to the constructor of each object that needs them, like so:</p>
|
228
|
+
|
229
|
+
<pre>
|
230
|
+
class Application
|
231
|
+
def initialize
|
232
|
+
@view = View.new
|
233
|
+
@logger = Logger.new
|
234
|
+
@database = Database.new( @logger )
|
235
|
+
@authenticator = Authenticator.new( @logger, @database )
|
236
|
+
@session = Session.new( @logger, @database )
|
237
|
+
end
|
238
|
+
end
|
239
|
+
</pre>
|
240
|
+
|
241
|
+
<p>The problem with this is that if you later decide that <code>View</code> needs to access the database, you need to rearrange the order of how things are instantiated in the <code>Application</code> constructor.<br />
|
242
|
+
</p>
|
243
|
+
</div>
|
244
|
+
|
245
|
+
|
246
|
+
|
247
|
+
<h2>
|
248
|
+
<a name="s3"></a>
|
249
|
+
3.3. Locator Pattern
|
250
|
+
</h2>
|
251
|
+
|
252
|
+
|
253
|
+
|
254
|
+
<div class="section">
|
255
|
+
<p>The <em>service locator</em> pattern makes things a <em>little</em> easier. Instead of instantiating everything in the constructor of the <code>Application</code>, you can create a factory method somewhere that returns the new <code>Application</code> instance. Then, inside of this factory method, you assign each new object to collection, and pass that collection to each constructor.</p>
|
256
|
+
|
257
|
+
<pre>
|
258
|
+
require 'needle'
|
259
|
+
|
260
|
+
def create_application
|
261
|
+
locator = Needle::Registry.new
|
262
|
+
|
263
|
+
locator.register( :view ) { View.new(locator) }
|
264
|
+
locator.register( :logger ) { Logger.new(locator) }
|
265
|
+
locator.register( :database ) { Database.new(locator) }
|
266
|
+
locator.register( :authenticator ) {Authenticator.new(locator) }
|
267
|
+
locator.register( :session ) { Session.new(locator) }
|
268
|
+
locator.register( :app ) { Application.new(locator) }
|
269
|
+
|
270
|
+
locator[:app]
|
271
|
+
end
|
272
|
+
|
273
|
+
class Application
|
274
|
+
def initialize( locator )
|
275
|
+
@view = locator[:view]
|
276
|
+
@logger = locator[:logger]
|
277
|
+
@database = locator[:database]
|
278
|
+
@authenticator = locator[:authenticator]
|
279
|
+
@session = locator[:session]
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
class Session
|
284
|
+
def initialize( locator )
|
285
|
+
@database = locator[:database]
|
286
|
+
@logger = locator[:logger]
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
...
|
291
|
+
</pre>
|
292
|
+
|
293
|
+
<p>This has the benefit of allowing each object to construct itself <em>� la carte</em> from the objects in the locator. Also, each object no longer cares what class implements each service—it only cares that each object implements the methods it will attempt to invoke on that object.</p>
|
294
|
+
|
295
|
+
<p>Also, because Needle defers the instantiation of each service until the service is actually requested, we can actually register each item with the locator in any arbitrary order. All that is happening is the block is associated with the symbol, so that when the service is requested, the corresponding block is invoked. What is more, by default each service is then cached, so that it is only instantiated once.</p>
|
296
|
+
|
297
|
+
<p>Thus, when we get the <code>:app</code> service (on the last line), the <code>Application</code> constructor is invoked, passing the locator to the constructor. Inside the constructor, <code>Application</code> retrieves each of its dependencies from the locator, causing each of them to be instantiated in turn. By this means, everything is initialized and constructed when the <code>create_application</code> method returns.</p>
|
298
|
+
|
299
|
+
<p>In the interest of brevity, the <code>create_application</code> could have been written like this, using a “builder” object (called <code>b</code> in the example below) to help register the services:</p>
|
300
|
+
|
301
|
+
<pre>
|
302
|
+
def create_application
|
303
|
+
locator = Needle::Registry.define do |b|
|
304
|
+
b.view { View.new(locator) }
|
305
|
+
b.logger { Logger.new(locator) }
|
306
|
+
b.database { Database.new(locator) }
|
307
|
+
b.authenticator {Authenticator.new(locator) }
|
308
|
+
b.session { Session.new(locator) }
|
309
|
+
b.app { Application.new(locator) }
|
310
|
+
end
|
311
|
+
|
312
|
+
locator[:app]
|
313
|
+
end
|
314
|
+
</pre>
|
315
|
+
</div>
|
147
316
|
|
148
317
|
|
149
318
|
|