needle 0.9.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/doc/faq/faq.yml +396 -14
- data/doc/manual-html/chapter-1.html +33 -4
- data/doc/manual-html/chapter-2.html +44 -6
- data/doc/manual-html/chapter-3.html +34 -5
- data/doc/manual-html/chapter-4.html +33 -4
- data/doc/manual-html/chapter-5.html +208 -4
- data/doc/manual-html/chapter-6.html +226 -4
- data/doc/manual-html/chapter-7.html +165 -4
- data/doc/manual-html/chapter-8.html +138 -6
- data/doc/manual-html/index.html +36 -9
- data/doc/manual/manual.rb +1 -1
- data/doc/manual/manual.yml +17 -4
- data/doc/manual/page.erb +2 -1
- data/doc/manual/parts/02_namespaces.txt +11 -2
- data/doc/manual/parts/03_overview.txt +1 -1
- data/doc/manual/parts/interceptors_architecture.txt +5 -0
- data/doc/manual/parts/interceptors_attaching.txt +64 -0
- data/doc/manual/parts/interceptors_custom.txt +25 -0
- data/doc/manual/parts/interceptors_ordering.txt +13 -0
- data/doc/manual/parts/interceptors_overview.txt +5 -0
- data/doc/manual/parts/libraries_creating.txt +30 -0
- data/doc/manual/parts/libraries_overview.txt +3 -0
- data/doc/manual/parts/libraries_using.txt +31 -0
- data/doc/manual/parts/logging_configuration.txt +30 -0
- data/doc/manual/parts/logging_logfactory.txt +31 -0
- data/doc/manual/parts/logging_overview.txt +5 -0
- data/doc/manual/parts/models_models.txt +35 -0
- data/doc/manual/parts/models_overview.txt +3 -0
- data/doc/manual/parts/models_pipelines.txt +63 -0
- data/lib/needle/container.rb +52 -7
- data/lib/needle/lifecycle/initialize.rb +1 -1
- data/lib/needle/log-factory.rb +46 -8
- data/lib/needle/thread.rb +6 -0
- data/lib/needle/version.rb +2 -2
- data/test/pipeline/tc_collection.rb +1 -1
- data/test/pipeline/tc_element.rb +2 -2
- data/test/services.rb +21 -0
- data/test/tc_container.rb +18 -0
- data/test/tc_logger.rb +35 -1
- metadata +17 -2
@@ -1,6 +1,6 @@
|
|
1
1
|
<html>
|
2
2
|
<head>
|
3
|
-
<title>Needle Manual :: Chapter 8:
|
3
|
+
<title>Needle Manual :: Chapter 8: Service Libraries</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-
|
17
|
+
Needle Version: <strong>1.0.0</strong><br />
|
18
|
+
Manual Last Updated: <strong>2004-11-04 14:24 GMT</strong>
|
19
19
|
</div>
|
20
20
|
</td></tr>
|
21
21
|
</table>
|
@@ -105,6 +105,16 @@
|
|
105
105
|
|
106
106
|
<ol type="1">
|
107
107
|
|
108
|
+
<li><a href="chapter-5.html#s1">Overview</a></li>
|
109
|
+
|
110
|
+
<li><a href="chapter-5.html#s2">Architecture</a></li>
|
111
|
+
|
112
|
+
<li><a href="chapter-5.html#s3">Attaching</a></li>
|
113
|
+
|
114
|
+
<li><a href="chapter-5.html#s4">Ordering</a></li>
|
115
|
+
|
116
|
+
<li><a href="chapter-5.html#s5">Custom</a></li>
|
117
|
+
|
108
118
|
</ol>
|
109
119
|
</li>
|
110
120
|
|
@@ -115,6 +125,12 @@
|
|
115
125
|
|
116
126
|
<ol type="1">
|
117
127
|
|
128
|
+
<li><a href="chapter-6.html#s1">Overview</a></li>
|
129
|
+
|
130
|
+
<li><a href="chapter-6.html#s2">Pipelines</a></li>
|
131
|
+
|
132
|
+
<li><a href="chapter-6.html#s3">Models</a></li>
|
133
|
+
|
118
134
|
</ol>
|
119
135
|
</li>
|
120
136
|
|
@@ -125,25 +141,38 @@
|
|
125
141
|
|
126
142
|
<ol type="1">
|
127
143
|
|
144
|
+
<li><a href="chapter-7.html#s1">Overview</a></li>
|
145
|
+
|
146
|
+
<li><a href="chapter-7.html#s2">LogFactory</a></li>
|
147
|
+
|
148
|
+
<li><a href="chapter-7.html#s3">Configuration</a></li>
|
149
|
+
|
128
150
|
</ol>
|
129
151
|
</li>
|
130
152
|
|
131
153
|
<li><strong>
|
132
154
|
<a href="chapter-8.html">
|
133
|
-
|
155
|
+
Service Libraries
|
134
156
|
</a>
|
135
157
|
</strong> <big>←</big>
|
136
158
|
<ol type="1">
|
137
159
|
|
160
|
+
<li><a href="chapter-8.html#s1">Overview</a></li>
|
161
|
+
|
162
|
+
<li><a href="chapter-8.html#s2">Creating Libraries</a></li>
|
163
|
+
|
164
|
+
<li><a href="chapter-8.html#s3">Using Libraries</a></li>
|
165
|
+
|
138
166
|
</ol>
|
139
167
|
</li>
|
140
168
|
|
141
169
|
</ol>
|
142
170
|
|
143
|
-
<h2>
|
171
|
+
<h2>Other Documentation</h2>
|
144
172
|
|
145
173
|
<ul>
|
146
174
|
<li><a href="http://needle.rubyforge.org/api/index.html">Needle API</a></li>
|
175
|
+
<li><a href="http://needle.rubyforge.org/faq.html">Needle FAQ</a></li>
|
147
176
|
</ul>
|
148
177
|
|
149
178
|
<h2>Tutorials</h2>
|
@@ -163,7 +192,110 @@
|
|
163
192
|
|
164
193
|
<div id="content">
|
165
194
|
|
166
|
-
<h1>8.
|
195
|
+
<h1>8. Service Libraries</h1>
|
196
|
+
|
197
|
+
|
198
|
+
|
199
|
+
<h2>
|
200
|
+
<a name="s1"></a>
|
201
|
+
8.1. Overview
|
202
|
+
</h2>
|
203
|
+
|
204
|
+
|
205
|
+
|
206
|
+
<div class="section">
|
207
|
+
<p>Using Needle as it has been presented so far works fine when you are dealing with a single application, all self-encapsulated. When you start dealing with combining multiple libraries, each potentially written by a different author, into a single registry, things get a little more complicated.</p>
|
208
|
+
|
209
|
+
<p>Needle provides a way for service authors to share their services. All it requires is that authors centralize their service configuration.<br />
|
210
|
+
</p>
|
211
|
+
</div>
|
212
|
+
|
213
|
+
|
214
|
+
|
215
|
+
<h2>
|
216
|
+
<a name="s2"></a>
|
217
|
+
8.2. Creating Libraries
|
218
|
+
</h2>
|
219
|
+
|
220
|
+
|
221
|
+
|
222
|
+
<div class="section">
|
223
|
+
<p>This centralization is implemented by creating a module for each library you want to have services for. That module should then define a module function called (by default) <code>register_services</code>. This function should accept a single parameter—the Needle container that the services will be added to.</p>
|
224
|
+
|
225
|
+
<p>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.</p>
|
226
|
+
|
227
|
+
<pre>
|
228
|
+
module Crypto
|
229
|
+
|
230
|
+
def register_services( container )
|
231
|
+
container.namespace_define( :crypto ) do |b|
|
232
|
+
b.prng do
|
233
|
+
require 'crypto/prng'
|
234
|
+
PRNG.new
|
235
|
+
end
|
236
|
+
|
237
|
+
b.des do
|
238
|
+
require 'crypto/des'
|
239
|
+
DES.new
|
240
|
+
end
|
241
|
+
|
242
|
+
...
|
243
|
+
end
|
244
|
+
end
|
245
|
+
module_function :register_services
|
246
|
+
|
247
|
+
end
|
248
|
+
</pre>
|
249
|
+
|
250
|
+
<p>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.</p>
|
251
|
+
|
252
|
+
<p>Once a service library has been created, it can then be accessed from another library or application that wishes to import those services.<br />
|
253
|
+
</p>
|
254
|
+
</div>
|
255
|
+
|
256
|
+
|
257
|
+
|
258
|
+
<h2>
|
259
|
+
<a name="s3"></a>
|
260
|
+
8.3. Using Libraries
|
261
|
+
</h2>
|
262
|
+
|
263
|
+
|
264
|
+
|
265
|
+
<div class="section">
|
266
|
+
<p>Using the libraries is as simple as requiring the file that has the service definitions, and then invoking the <code>#register_services</code> module function:</p>
|
267
|
+
|
268
|
+
<pre>
|
269
|
+
require 'needle'
|
270
|
+
|
271
|
+
reg = Needle::Registry.new
|
272
|
+
reg.define do |b|
|
273
|
+
b.foo { Foo.new }
|
274
|
+
|
275
|
+
require 'crypto/services'
|
276
|
+
Crypto.register_services( reg )
|
277
|
+
end
|
278
|
+
|
279
|
+
prng = reg.crypto.prng
|
280
|
+
</pre>
|
281
|
+
|
282
|
+
<p>To make this easier, the Container class has a convenience method named <code>#require</code>:</p>
|
283
|
+
|
284
|
+
<pre>
|
285
|
+
require 'needle'
|
286
|
+
|
287
|
+
reg = Needle::Registry.new
|
288
|
+
reg.define do |b|
|
289
|
+
b.foo { Foo.new }
|
290
|
+
b.require 'crypto/services', "Crypto"
|
291
|
+
end
|
292
|
+
|
293
|
+
prng = reg.crypto.prng
|
294
|
+
</pre>
|
295
|
+
|
296
|
+
<p>The <code>Container#require</code> method will require the file, and then look for a <code>#register_services</code> method of the named module. It will execute that method, passing the container as an argument.<br />
|
297
|
+
</p>
|
298
|
+
</div>
|
167
299
|
|
168
300
|
|
169
301
|
|
data/doc/manual-html/index.html
CHANGED
@@ -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-
|
17
|
+
Needle Version: <strong>1.0.0</strong><br />
|
18
|
+
Manual Last Updated: <strong>2004-11-04 14:24 GMT</strong>
|
19
19
|
</div>
|
20
20
|
</td></tr>
|
21
21
|
</table>
|
@@ -105,6 +105,16 @@
|
|
105
105
|
|
106
106
|
<ol type="1">
|
107
107
|
|
108
|
+
<li><a href="chapter-5.html#s1">Overview</a></li>
|
109
|
+
|
110
|
+
<li><a href="chapter-5.html#s2">Architecture</a></li>
|
111
|
+
|
112
|
+
<li><a href="chapter-5.html#s3">Attaching</a></li>
|
113
|
+
|
114
|
+
<li><a href="chapter-5.html#s4">Ordering</a></li>
|
115
|
+
|
116
|
+
<li><a href="chapter-5.html#s5">Custom</a></li>
|
117
|
+
|
108
118
|
</ol>
|
109
119
|
</li>
|
110
120
|
|
@@ -115,6 +125,12 @@
|
|
115
125
|
|
116
126
|
<ol type="1">
|
117
127
|
|
128
|
+
<li><a href="chapter-6.html#s1">Overview</a></li>
|
129
|
+
|
130
|
+
<li><a href="chapter-6.html#s2">Pipelines</a></li>
|
131
|
+
|
132
|
+
<li><a href="chapter-6.html#s3">Models</a></li>
|
133
|
+
|
118
134
|
</ol>
|
119
135
|
</li>
|
120
136
|
|
@@ -125,25 +141,38 @@
|
|
125
141
|
|
126
142
|
<ol type="1">
|
127
143
|
|
144
|
+
<li><a href="chapter-7.html#s1">Overview</a></li>
|
145
|
+
|
146
|
+
<li><a href="chapter-7.html#s2">LogFactory</a></li>
|
147
|
+
|
148
|
+
<li><a href="chapter-7.html#s3">Configuration</a></li>
|
149
|
+
|
128
150
|
</ol>
|
129
151
|
</li>
|
130
152
|
|
131
153
|
<li>
|
132
154
|
<a href="chapter-8.html">
|
133
|
-
|
155
|
+
Service Libraries
|
134
156
|
</a>
|
135
157
|
|
136
158
|
<ol type="1">
|
137
159
|
|
160
|
+
<li><a href="chapter-8.html#s1">Overview</a></li>
|
161
|
+
|
162
|
+
<li><a href="chapter-8.html#s2">Creating Libraries</a></li>
|
163
|
+
|
164
|
+
<li><a href="chapter-8.html#s3">Using Libraries</a></li>
|
165
|
+
|
138
166
|
</ol>
|
139
167
|
</li>
|
140
168
|
|
141
169
|
</ol>
|
142
170
|
|
143
|
-
<h2>
|
171
|
+
<h2>Other Documentation</h2>
|
144
172
|
|
145
173
|
<ul>
|
146
174
|
<li><a href="http://needle.rubyforge.org/api/index.html">Needle API</a></li>
|
175
|
+
<li><a href="http://needle.rubyforge.org/faq.html">Needle FAQ</a></li>
|
147
176
|
</ul>
|
148
177
|
|
149
178
|
<h2>Tutorials</h2>
|
@@ -178,6 +207,8 @@
|
|
178
207
|
|
179
208
|
API Documentation: <a href="http://needle.rubyforge.org/api">http://needle.rubyforge.org/api</a><br />
|
180
209
|
|
210
|
+
FAQ Document: <a href="http://needle.rubyforge.org/faq.html">http://needle.rubyforge.org/faq.html</a><br />
|
211
|
+
|
181
212
|
Needle Wiki: <a href="http://needle.rubyforge.org/wiki/wiki.pl">http://needle.rubyforge.org/wiki/wiki.pl</a><br />
|
182
213
|
|
183
214
|
</p>
|
@@ -187,11 +218,7 @@
|
|
187
218
|
<table border='0' cellpadding='0' cellspacing='0' align='center'><tr><td>
|
188
219
|
<ul>
|
189
220
|
|
190
|
-
<li>added
|
191
|
-
|
192
|
-
<li>renamed Container#register! to Container#define!</li>
|
193
|
-
|
194
|
-
<li>added documentation of Container#new!</li>
|
221
|
+
<li>added remaining chapters</li>
|
195
222
|
|
196
223
|
</ul>
|
197
224
|
</table>
|
data/doc/manual/manual.rb
CHANGED
@@ -100,7 +100,7 @@ module Needle
|
|
100
100
|
def initialize( index, title, content )
|
101
101
|
@index = index
|
102
102
|
@title = RedCloth.new( title ).to_html.gsub( %r{</?p>}, "" ) if title
|
103
|
-
@content = RedCloth.new( content )
|
103
|
+
@content = RedCloth.new( content || "" )
|
104
104
|
end
|
105
105
|
end
|
106
106
|
|
data/doc/manual/manual.yml
CHANGED
@@ -19,12 +19,11 @@ product: !^product
|
|
19
19
|
- Project Page: http://rubyforge.org/projects/needle
|
20
20
|
- User Manual: http://needle.rubyforge.org
|
21
21
|
- API Documentation: http://needle.rubyforge.org/api
|
22
|
+
- FAQ Document: http://needle.rubyforge.org/faq.html
|
22
23
|
- Needle Wiki: http://needle.rubyforge.org/wiki/wiki.pl
|
23
24
|
|
24
25
|
recent_updates:
|
25
|
-
- added
|
26
|
-
- renamed Container#register! to Container#define!
|
27
|
-
- added documentation of Container#new!
|
26
|
+
- added remaining chapters
|
28
27
|
|
29
28
|
chapters:
|
30
29
|
|
@@ -51,9 +50,23 @@ chapters:
|
|
51
50
|
- Setup: !!file parts/04_setup.txt
|
52
51
|
|
53
52
|
- Interceptors:
|
53
|
+
- Overview: !!file parts/interceptors_overview.txt
|
54
|
+
- Architecture: !!file parts/interceptors_architecture.txt
|
55
|
+
- Attaching: !!file parts/interceptors_attaching.txt
|
56
|
+
- Ordering: !!file parts/interceptors_ordering.txt
|
57
|
+
- Custom: !!file parts/interceptors_custom.txt
|
54
58
|
|
55
59
|
- Service Models:
|
60
|
+
- Overview: !!file parts/models_overview.txt
|
61
|
+
- Pipelines: !!file parts/models_pipelines.txt
|
62
|
+
- Models: !!file parts/models_models.txt
|
56
63
|
|
57
64
|
- Logging:
|
65
|
+
- Overview: !!file parts/logging_overview.txt
|
66
|
+
- LogFactory: !!file parts/logging_logfactory.txt
|
67
|
+
- Configuration: !!file parts/logging_configuration.txt
|
58
68
|
|
59
|
-
-
|
69
|
+
- Service Libraries:
|
70
|
+
- Overview: !!file parts/libraries_overview.txt
|
71
|
+
- Creating Libraries: !!file parts/libraries_creating.txt
|
72
|
+
- Using Libraries: !!file parts/libraries_using.txt
|
data/doc/manual/page.erb
CHANGED
@@ -45,10 +45,11 @@
|
|
45
45
|
<% end %>
|
46
46
|
</ol>
|
47
47
|
|
48
|
-
<h2>
|
48
|
+
<h2>Other Documentation</h2>
|
49
49
|
|
50
50
|
<ul>
|
51
51
|
<li><a href="http://needle.rubyforge.org/api/index.html">Needle API</a></li>
|
52
|
+
<li><a href="http://needle.rubyforge.org/faq.html">Needle FAQ</a></li>
|
52
53
|
</ul>
|
53
54
|
|
54
55
|
<h2>Tutorials</h2>
|
@@ -35,10 +35,19 @@ Because it is often the case that you will be creating a namespace and then imme
|
|
35
35
|
end
|
36
36
|
</pre>
|
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
40
|
<pre>
|
41
|
-
registry.
|
41
|
+
registry.namespace_define :stuff do |b|
|
42
|
+
b.foo { Bar.new }
|
43
|
+
...
|
44
|
+
end
|
45
|
+
</pre>
|
46
|
+
|
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
|
+
|
49
|
+
<pre>
|
50
|
+
registry.namespace_define! :stuff do
|
42
51
|
foo { Bar.new }
|
43
52
|
...
|
44
53
|
end
|
@@ -9,7 +9,7 @@ To demonstrate both techniques, we'll pretend we're going to write an online for
|
|
9
9
|
* @View@. The presentation manager, used to render pages to the user.
|
10
10
|
* @Application@. The controller that ties it all together.
|
11
11
|
|
12
|
-
(Of course, a _real_ online forum application would be significantly more complex, but the above components will do for our
|
12
|
+
(Of course, a _real_ online forum application would be significantly more complex, but the above components will do for our purposes.)
|
13
13
|
|
14
14
|
The dependencies between these components are:
|
15
15
|
|
@@ -0,0 +1,5 @@
|
|
1
|
+
Interceptors are implemented as proxy objects that front the requested service. Thus, if you request a service that has 3 interceptors wrapped around it, you're really getting a proxy object back that will invoke the interceptors (in order) before the requested method of the service is invoked.
|
2
|
+
|
3
|
+
The interceptors themselves are attached to the service during the execution of its instantiation pipeline (see Service Models). Thus, any action in the pipeline that is situated closer to the service than the interceptor pipeline action will bypass the interceptors altogether. This allows you to attach hooks to your service that can be called without invoking the interceptors on the service.
|
4
|
+
|
5
|
+
Another thing to keep in mind is that the interceptors are one-to-one for each service instance. Thus, if your service is a prototype (see the Service Models chapter), you'll have one instance of each interceptor for each instance of your service.
|
@@ -0,0 +1,64 @@
|
|
1
|
+
There are two ways to attach interceptors to your services. The first is to implement an interceptor _factory_ that returns new interceptor _instances_, and attach the factory to your service. The second is to specify a block that implements the required functionality. Both have their uses.
|
2
|
+
|
3
|
+
h3. Interceptor Factories
|
4
|
+
|
5
|
+
Interceptor factories are useful in situations where you want to implement some functionality and have it apply to multiple services. Interceptors from factories are also faster (less overhead) than interceptors from blocks, and so might be appropriate where performance is an issue.
|
6
|
+
|
7
|
+
An example is the LoggingInterceptor that ships with Needle. Because it is functionality that could be used on any number of services, it is implemented as a factory.
|
8
|
+
|
9
|
+
You can attach interceptor factories to your service using the @#interceptor(...).with {...}@ syntax:
|
10
|
+
|
11
|
+
<pre>
|
12
|
+
reg.register( :foo ) {...}
|
13
|
+
reg.intercept( :foo ).with { MyInterceptorFactory }
|
14
|
+
</pre>
|
15
|
+
|
16
|
+
Note that you could also make the interceptor factory a service:
|
17
|
+
|
18
|
+
<pre>
|
19
|
+
reg.register( :foo ) {...}
|
20
|
+
reg.register( :my_interceptor ) { MyInterceptorFactory }
|
21
|
+
reg.intercept( :foo ).with { |c| c.my_interceptor }
|
22
|
+
</pre>
|
23
|
+
|
24
|
+
And, to make accessing interceptor services even more convenient, you can use the @#with!@ method (which executes its block within the context of the calling container):
|
25
|
+
|
26
|
+
<pre>
|
27
|
+
reg.register( :foo ) {...}
|
28
|
+
reg.register( :my_interceptor ) { MyInterceptorFactory }
|
29
|
+
reg.intercept( :foo ).with! { my_interceptor }
|
30
|
+
</pre>
|
31
|
+
|
32
|
+
|
33
|
+
h3. Blocks
|
34
|
+
|
35
|
+
Sometimes creating an entire class to implement an interceptor is overkill. This is particularly the case during debugging or testing, when you might want to attach an interceptor to class to verify that a parameter passed is correct, or a return value is what you expect. To satisfy these conditions, you can using the
|
36
|
+
@#doing@ method. Just give it a block that accepts two parameters (the chain, and context) and you're good to go!
|
37
|
+
|
38
|
+
<pre>
|
39
|
+
reg.register( :foo ) {...}
|
40
|
+
reg.intercept( :foo ).doing { |chain,ctx| ...; chain.process_next( ctx ) }
|
41
|
+
</pre>
|
42
|
+
|
43
|
+
Note that this approach is about 40% slower than using an interceptor factory, so it should not be used if performance is an issue.
|
44
|
+
|
45
|
+
h3. Options
|
46
|
+
|
47
|
+
Some interceptors can accept configuration options. For example, the LoggingInterceptor allows clients to specify methods that should and shouldn't be intercepted. Options are specified via the @#with_options@ method.
|
48
|
+
|
49
|
+
<pre>
|
50
|
+
reg.register( :foo ) {...}
|
51
|
+
reg.intercept( :foo ).
|
52
|
+
with { |c| c.logging_interceptor }.
|
53
|
+
with_options( :exclude => [ "method1", "method2" ] )
|
54
|
+
</pre>
|
55
|
+
|
56
|
+
Options can apply to the blocks given to the @#doing@ method, too. The block may access the options via the @#data[:options]@ member of the context:
|
57
|
+
|
58
|
+
<pre>
|
59
|
+
reg.intercept( :foo ).
|
60
|
+
doing { |ch,ctx| ...; p ctx.data[:options][:value]; ... }.
|
61
|
+
with_options( :value => "hello" )
|
62
|
+
</pre>
|
63
|
+
|
64
|
+
With blocks, of course, the value of such an approach is limited.
|