needle 0.6.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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>"
@@ -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.6.0</strong><br />
18
- Manual Last Updated: <strong>2004-10-21 16:17 GMT</strong>
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
- Dependency Injection
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
- Interceptors
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
- Service Models
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
- Logging
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.6.0</strong><br />
18
- Manual Last Updated: <strong>2004-10-21 16:17 GMT</strong>
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
- Dependency Injection
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
- Interceptors
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
- Service Models
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
- Logging
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>One other convenience method is <code>#new!</code>:</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.new! do
227
+ registry = Needle::Registry.define do |b|
198
228
  ...
199
229
  end
200
230
  </pre>
201
231
 
202
- <p>This block accepts no parameters, and evaluates the block as if it were passed to <code>Registry#define!</code> (see below).<br />
232
+ <p>This block accepts a single parameter&#8212;a &#8220;builder&#8221; object to aid in registering services&#8212;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: Dependency Injection</title>
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.6.0</strong><br />
18
- Manual Last Updated: <strong>2004-10-21 16:17 GMT</strong>
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
- Dependency Injection
73
+ Service Locator
74
74
  </a>
75
75
  </strong> <big>&larr;</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
- Interceptors
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
- Service Models
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
- Logging
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. Dependency Injection</h1>
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&#8217;ll pretend we&#8217;re going to write an online forum application. To start, let&#8217;s come up with a rough design by cataloging the components we&#8217;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&#8217;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&#8212;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 &#8220;builder&#8221; 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