copland 0.8.0 → 1.0.0
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 +227 -36
- data/doc/manual-html/chapter-10.html +155 -82
- data/doc/manual-html/chapter-11.html +90 -267
- data/doc/manual-html/chapter-12.html +289 -71
- data/doc/manual-html/chapter-13.html +430 -0
- data/doc/manual-html/chapter-2.html +45 -21
- data/doc/manual-html/chapter-3.html +45 -21
- data/doc/manual-html/chapter-4.html +45 -21
- data/doc/manual-html/chapter-5.html +45 -21
- data/doc/manual-html/chapter-6.html +49 -21
- data/doc/manual-html/chapter-7.html +45 -21
- data/doc/manual-html/chapter-8.html +66 -26
- data/doc/manual-html/chapter-9.html +48 -24
- data/doc/manual-html/index.html +54 -22
- data/doc/manual-html/manual.css +12 -0
- data/doc/manual-html/tutorial-1.html +45 -21
- data/doc/manual-html/tutorial-2.html +45 -21
- data/doc/manual-html/tutorial-3.html +45 -21
- data/doc/manual-html/tutorial-4.html +45 -21
- data/doc/manual-html/tutorial-5.html +45 -21
- data/doc/manual/manual.css +12 -0
- data/doc/manual/manual.rb +1 -1
- data/doc/manual/manual.yml +426 -20
- data/doc/packages/copland.html +41 -9
- data/doc/packages/copland.lib.html +36 -8
- data/doc/packages/copland.remote.html +46 -10
- data/doc/packages/copland.webrick.html +16 -65
- data/doc/packages/index.html +1 -1
- data/doc/presentation/copland.mgp +1083 -0
- data/doc/presentation/to_html.rb +52 -0
- data/lib/copland/configuration-point/common.rb +32 -1
- data/lib/copland/configuration/yaml/service-point.rb +10 -1
- data/lib/copland/log-factory.rb +28 -12
- data/lib/copland/logger.rb +155 -0
- data/lib/copland/models/singleton.rb +8 -2
- data/lib/copland/package.rb +32 -14
- data/lib/copland/service-point.rb +7 -0
- data/lib/copland/thread.rb +104 -0
- data/lib/copland/utils.rb +10 -3
- data/lib/copland/version.rb +2 -2
- data/test/configuration/yaml/tc_service-point-processor.rb +8 -0
- data/test/custom-logger.yml +2 -1
- data/test/impl/tc_logging-interceptor.rb +12 -12
- data/test/logger.yml +1 -1
- data/test/mock.rb +2 -0
- data/test/tc_logger.rb +19 -6
- data/test/tc_package.rb +25 -0
- data/test/tc_queryable-mutex.rb +75 -0
- data/test/tc_registry.rb +8 -4
- metadata +9 -2
@@ -0,0 +1,1083 @@
|
|
1
|
+
%%========================================================================
|
2
|
+
%deffont "standard" xfont "Sans"
|
3
|
+
%deffont "standard-italic" xfont "Sans:Oblique"
|
4
|
+
%deffont "standard-bold" xfont "Sans:Bold"
|
5
|
+
%deffont "code" xfont "Courier"
|
6
|
+
%deffont "code-bold" xfont "Courier:Bold"
|
7
|
+
%%========================================================================
|
8
|
+
%page
|
9
|
+
%%========================================================================
|
10
|
+
%% h1. How Dynamic Can You Get?
|
11
|
+
%%
|
12
|
+
%% Ruby: already very dynamic.
|
13
|
+
%% This presentation will show that DI (IoC) compliments and enhances
|
14
|
+
%% that attribute of Ruby.
|
15
|
+
%%========================================================================
|
16
|
+
%back "white"
|
17
|
+
|
18
|
+
|
19
|
+
%center, fore "#F00", font "standard", hgap 60, size 6.5
|
20
|
+
Dependency Injection in Ruby
|
21
|
+
%bar "#F77" 4 20 60
|
22
|
+
%size 4, font "standard-italic", fore "#F60"
|
23
|
+
How Dynamic Can You Get?
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
%size 2.5, font "standard", fore "#600"
|
28
|
+
2004 International Ruby Conference
|
29
|
+
%size 2, fore "black", font "standard-bold"
|
30
|
+
C h a n t i l l y , V i r g i n i a , U S A
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
%size 2, font "standard", fore "#600"
|
37
|
+
Jamis Buck (jgb3@email.byu.edu)
|
38
|
+
%fore "#006", font "standard-bold"
|
39
|
+
Brigham Young University
|
40
|
+
|
41
|
+
%page
|
42
|
+
%%========================================================================
|
43
|
+
%% h1. What is "Dependency Injection"?
|
44
|
+
%%
|
45
|
+
%% # The mirror of garbage collection, according to HLS. GC is concerned
|
46
|
+
%% with reclaiming memory from dead objects at the end of their
|
47
|
+
%% lifecycle; DI/IoC is concerned with instantiating and initializing
|
48
|
+
%% new objects at the beginning of their lifecycle.
|
49
|
+
%% # Abdicates responsibility for instantiating dependencies. Instead of
|
50
|
+
%% an object being responsible for instantiating its own dependencies,
|
51
|
+
%% each object assumes that someone else will carry that responsibility,
|
52
|
+
%% "injecting" those dependencies into the object at the time of its
|
53
|
+
%% creation.
|
54
|
+
%%
|
55
|
+
%% h1. What about "Inversion of Control"
|
56
|
+
%%
|
57
|
+
%% # "Inverts" control over some aspect of the object by relegating that
|
58
|
+
%% control to some other object.
|
59
|
+
%% # There are lots of opinions about whether "Inversion of Control" is
|
60
|
+
%% the "correct" term for what containers like Copland do. Martin Fowler
|
61
|
+
%% believes the term is too generic, while others claim that IoC
|
62
|
+
%% containers do more than just DI.
|
63
|
+
%% # DI is a form of IoC--control over dependencies is just one kind of
|
64
|
+
%% control that may be inverted. Other kinds of control include
|
65
|
+
%% configuration, logging, concurrency, authorization, etc.
|
66
|
+
%%
|
67
|
+
%% h1. is it for me?
|
68
|
+
%%
|
69
|
+
%% # Maybe. DI does introduce some overhead, so for small tasks, or
|
70
|
+
%% those that rely on milking every last ounce of performance from the
|
71
|
+
%% Ruby interpreter, DI is probably not appropriate.
|
72
|
+
%% # For larger, complex applications, DI can improve maintainability.
|
73
|
+
%%========================================================================
|
74
|
+
%fore "black", center, prefix 0, area 100 100 0 0
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
%rcutin, size 5, font "standard", fore "#00F"
|
79
|
+
What is "Dependency Injection"?
|
80
|
+
%size 4
|
81
|
+
|
82
|
+
%pause, rcutin, fore "black"
|
83
|
+
Mirror of garbage collection
|
84
|
+
%pause, rcutin
|
85
|
+
Abdicates responsibility for instantiating dependencies
|
86
|
+
%pause, rcutin
|
87
|
+
Loose coupling of components
|
88
|
+
|
89
|
+
%pause, rcutin, size 5, font "standard", fore "#00F"
|
90
|
+
What about "Inversion of Control"?
|
91
|
+
%size 4
|
92
|
+
|
93
|
+
%pause, rcutin, fore "black"
|
94
|
+
Depends on who you ask
|
95
|
+
%size 2
|
96
|
+
|
97
|
+
%pause, size 4, rcutin, fore "black"
|
98
|
+
%font "code"
|
99
|
+
assert DI.kind_of?( IoC )
|
100
|
+
%font "standard"
|
101
|
+
Control over dependencies is "inverted"
|
102
|
+
|
103
|
+
%pause, rcutin, size 8, fore "#F00", font "standard-bold"
|
104
|
+
Is it for me?
|
105
|
+
%size 3
|
106
|
+
|
107
|
+
%size 4, pause, lcutin, font "standard-italic", fore "black"
|
108
|
+
Maybe...
|
109
|
+
|
110
|
+
%page
|
111
|
+
%%========================================================================
|
112
|
+
%% h1. How can it help me?
|
113
|
+
%%
|
114
|
+
%% 1. Log Method Execution
|
115
|
+
%% 2. Reference Other Services
|
116
|
+
%% 3. Service Configuration
|
117
|
+
%% 4. Unit Testing
|
118
|
+
%% 5. Lifecycle Management
|
119
|
+
%%========================================================================
|
120
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
121
|
+
|
122
|
+
|
123
|
+
|
124
|
+
|
125
|
+
%size 6
|
126
|
+
How can it help me
|
127
|
+
|
128
|
+
|
129
|
+
%size 8, fore "#F00", font "standard-bold"
|
130
|
+
Right now?
|
131
|
+
|
132
|
+
%page
|
133
|
+
%%========================================================================
|
134
|
+
%% h1. How can it help me?
|
135
|
+
%%
|
136
|
+
%% 1. Log Method Execution
|
137
|
+
%%========================================================================
|
138
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
139
|
+
|
140
|
+
Log Method Execution
|
141
|
+
%bar "#F77" 4 10 80, size 3, fore "#F00", font "standard-italic"
|
142
|
+
|
143
|
+
sans
|
144
|
+
%cont, font "standard"
|
145
|
+
Copland
|
146
|
+
|
147
|
+
%font "code", prefix 15, left, size 2.5, fore "black"
|
148
|
+
def foo( arg1, arg2 )
|
149
|
+
@log.debug( "in foo with #{arg1} and #{arg2}" ) if @log.debug?
|
150
|
+
...
|
151
|
+
result = the_result_of_the_method
|
152
|
+
@log.debug( "finishing foo with #{result}" ) if @log.debug
|
153
|
+
return result
|
154
|
+
rescue Exception => e
|
155
|
+
@log.debug( "exception #{e.message} (#{e.class})" ) if @log.debug?
|
156
|
+
raise
|
157
|
+
end
|
158
|
+
|
159
|
+
%pause, center, prefix 0, size 3, fore "#F00", font "standard"
|
160
|
+
With Copland
|
161
|
+
|
162
|
+
%font "code", prefix 15, left, size 2.5, fore "black"
|
163
|
+
def foo( arg1, arg2 )
|
164
|
+
...
|
165
|
+
the_result_of_the_method
|
166
|
+
end
|
167
|
+
|
168
|
+
%size 2, fore "#F00", font "standard-italic"
|
169
|
+
(+2 lines of YAML)
|
170
|
+
|
171
|
+
%page
|
172
|
+
%%========================================================================
|
173
|
+
%% h1. How can it help me?
|
174
|
+
%%
|
175
|
+
%% 2. Reference another service
|
176
|
+
%%========================================================================
|
177
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
178
|
+
|
179
|
+
Reference Other Services
|
180
|
+
%bar "#F77" 4 10 80, size 3, fore "#F00", font "standard-italic"
|
181
|
+
|
182
|
+
sans
|
183
|
+
%cont, font "standard"
|
184
|
+
Copland
|
185
|
+
|
186
|
+
%font "code", prefix 15, left, fore "black"
|
187
|
+
def foo( parms )
|
188
|
+
@service ||= lookup_service
|
189
|
+
@service.do_something( parms )
|
190
|
+
end
|
191
|
+
|
192
|
+
%pause, center, prefix 0, size 3, fore "#F00", font "standard"
|
193
|
+
|
194
|
+
With Copland
|
195
|
+
|
196
|
+
%font "code", prefix 15, left, fore "black"
|
197
|
+
attr_writer :service
|
198
|
+
|
199
|
+
def foo( parms )
|
200
|
+
@service.do_something( parms )
|
201
|
+
end
|
202
|
+
|
203
|
+
%size 2, fore "#F00", font "standard-italic"
|
204
|
+
(+2 lines of YAML)
|
205
|
+
|
206
|
+
%page
|
207
|
+
%%========================================================================
|
208
|
+
%% h1. How can it help me?
|
209
|
+
%%
|
210
|
+
%% 3. Service Configuration
|
211
|
+
%%========================================================================
|
212
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
213
|
+
|
214
|
+
Service Configuration
|
215
|
+
%bar "#F77" 4 10 80, size 4, prefix 10, left
|
216
|
+
|
217
|
+
%pause, rcutin
|
218
|
+
Packages define configuration points
|
219
|
+
|
220
|
+
%pause, rcutin
|
221
|
+
Packages contribute to configuration points
|
222
|
+
|
223
|
+
%pause, rcutin
|
224
|
+
Decentralized configuration
|
225
|
+
|
226
|
+
%pause, rcutin
|
227
|
+
Services specify dependencies on configuration points
|
228
|
+
|
229
|
+
%page
|
230
|
+
%%========================================================================
|
231
|
+
%% h1. How can it help me?
|
232
|
+
%%
|
233
|
+
%% 4. Unit Testing
|
234
|
+
%%========================================================================
|
235
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
236
|
+
|
237
|
+
Unit Testing
|
238
|
+
%bar "#F77" 4 10 80, size 3, fore "#F00", font "standard-italic"
|
239
|
+
|
240
|
+
sans
|
241
|
+
%cont, font "standard"
|
242
|
+
Copland
|
243
|
+
|
244
|
+
%area 50 50 0 30
|
245
|
+
%font "code", prefix 15, left, fore "black", size 5
|
246
|
+
def foo( parms )
|
247
|
+
@dependency ||= MyDependency.new
|
248
|
+
@dependency.do_something( parms )
|
249
|
+
end
|
250
|
+
|
251
|
+
%area 50 50 50 30
|
252
|
+
%pause, left, prefix 15, font "standard", fore "#F00"
|
253
|
+
Tightly coupled
|
254
|
+
Hard to test independently
|
255
|
+
|
256
|
+
%area 100 50 0 50
|
257
|
+
%pause, center, prefix 0, size 6, fore "#F00", font "standard"
|
258
|
+
With Copland
|
259
|
+
|
260
|
+
%area 50 50 0 60
|
261
|
+
%font "code", prefix 15, left, fore "black", size 5
|
262
|
+
attr_writer :dependency
|
263
|
+
|
264
|
+
def foo( parms )
|
265
|
+
@dependency.do_something( parms )
|
266
|
+
end
|
267
|
+
|
268
|
+
%pause, area 50 50 45 60
|
269
|
+
def test_foo
|
270
|
+
@obj.dependency = MyMockDependency.new
|
271
|
+
@obj.foo( parms )
|
272
|
+
assert @obj.in_some_state?
|
273
|
+
end
|
274
|
+
|
275
|
+
%area 100 100 0 80
|
276
|
+
%pause, center, prefix 0, font "standard", fore "#F00", size 3
|
277
|
+
Mock dependencies are easily injected
|
278
|
+
|
279
|
+
%page
|
280
|
+
%%========================================================================
|
281
|
+
%% h1. How can it help me?
|
282
|
+
%%
|
283
|
+
%% 5. Lifecycle Management
|
284
|
+
%%========================================================================
|
285
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
286
|
+
|
287
|
+
Lifecycle Management
|
288
|
+
%bar "#F77" 4 10 80, size 4, left
|
289
|
+
|
290
|
+
|
291
|
+
%fore "#00F", prefix 30
|
292
|
+
* Service model
|
293
|
+
%fore "black", font "standard-italic", prefix 35
|
294
|
+
- Prototype
|
295
|
+
- Singleton
|
296
|
+
- Threaded
|
297
|
+
|
298
|
+
|
299
|
+
%fore "#00F", font "standard", prefix 30
|
300
|
+
* Instantiation
|
301
|
+
%fore "black", font "standard-italic", prefix 35
|
302
|
+
- immediate
|
303
|
+
- deferred
|
304
|
+
|
305
|
+
%page
|
306
|
+
%%========================================================================
|
307
|
+
%% h1. How can it help me, in general?
|
308
|
+
%%
|
309
|
+
%% # configurable (provides a configuration framework)
|
310
|
+
%% # reusable (encourages loose-coupling of components)
|
311
|
+
%% # extensible (easy to swap components in or out)
|
312
|
+
%% # debuggable (logging framework, including interceptors)
|
313
|
+
%% # testable (small, independent components)
|
314
|
+
%%========================================================================
|
315
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
316
|
+
|
317
|
+
%size 4
|
318
|
+
In general, then...
|
319
|
+
%lcutin, size 6, fore "#00F"
|
320
|
+
How can DI/IoC help me?
|
321
|
+
%size 4
|
322
|
+
|
323
|
+
%pause, rcutin, fore "#F00", size 5
|
324
|
+
"CREDT"
|
325
|
+
%size 4
|
326
|
+
|
327
|
+
|
328
|
+
%pause, lcutin, fore "red", font "standard-bold"
|
329
|
+
C
|
330
|
+
%cont, fore "black", font "standard"
|
331
|
+
onfigurable
|
332
|
+
|
333
|
+
%pause, rcutin, fore "red", font "standard-bold"
|
334
|
+
R
|
335
|
+
%cont, fore "black", font "standard"
|
336
|
+
eusable
|
337
|
+
|
338
|
+
%pause, lcutin, fore "red", font "standard-bold"
|
339
|
+
E
|
340
|
+
%cont, fore "black", font "standard"
|
341
|
+
xtensible
|
342
|
+
|
343
|
+
%pause, rcutin, fore "red", font "standard-bold"
|
344
|
+
D
|
345
|
+
%cont, fore "black", font "standard"
|
346
|
+
ebuggable
|
347
|
+
|
348
|
+
%pause, lcutin, fore "red", font "standard-bold"
|
349
|
+
T
|
350
|
+
%cont, fore "black", font "standard"
|
351
|
+
estable
|
352
|
+
|
353
|
+
%page
|
354
|
+
%%========================================================================
|
355
|
+
%% h1. Calculator Example
|
356
|
+
%%
|
357
|
+
%% Given: a trivial calculator
|
358
|
+
%%
|
359
|
+
%% * can't easily replace an operation with a "mock" operation
|
360
|
+
%% * third parties cannot easily add new operations
|
361
|
+
%%========================================================================
|
362
|
+
%fore "black", center, size 6, font "standard", prefix 0
|
363
|
+
|
364
|
+
Consider a Calculator...
|
365
|
+
%bar "#F77" 4 10 80
|
366
|
+
%size 3
|
367
|
+
|
368
|
+
%font "code", prefix 30, left
|
369
|
+
class Calculator
|
370
|
+
def add( a, b )
|
371
|
+
a.to_f + b.to_f
|
372
|
+
end
|
373
|
+
|
374
|
+
def subtract( a, b )
|
375
|
+
a.to_f - b.to_f
|
376
|
+
end
|
377
|
+
|
378
|
+
def multiply( a, b )
|
379
|
+
a.to_f * b.to_f
|
380
|
+
end
|
381
|
+
|
382
|
+
def divide( a, b )
|
383
|
+
a.to_f / b.to_f
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
%pause, font "standard-italic", size 3, prefix 0, center
|
388
|
+
%bar "#F77" 4 10 80
|
389
|
+
What are some drawbacks of this approach?
|
390
|
+
|
391
|
+
%page
|
392
|
+
%%========================================================================
|
393
|
+
%% h1. Calculator Example: Refactored
|
394
|
+
%%
|
395
|
+
%% Break the operations into their own classes. This allows each operation
|
396
|
+
%% to be unit tested individually.
|
397
|
+
%%========================================================================
|
398
|
+
%fore "black", center
|
399
|
+
|
400
|
+
|
401
|
+
%size 6, font "standard", prefix 0
|
402
|
+
Refactor the operations
|
403
|
+
into their own classes
|
404
|
+
%bar "#F77" 4 10 80
|
405
|
+
%size 3
|
406
|
+
(calc.rb)
|
407
|
+
|
408
|
+
%area 50 50 0 30
|
409
|
+
%font "code", size 6, prefix 20, left
|
410
|
+
class Adder
|
411
|
+
def compute( a, b )
|
412
|
+
a.to_f + b.to_f
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
class Subtracter
|
417
|
+
def compute( a, b )
|
418
|
+
a.to_f - b.to_f
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
%area 50 50 50 30
|
423
|
+
class Multiplier
|
424
|
+
def compute( a, b )
|
425
|
+
a.to_f * b.to_f
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
class Divider
|
430
|
+
def compute( a, b )
|
431
|
+
a.to_f / b.to_f
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
%page
|
436
|
+
%%========================================================================
|
437
|
+
%% h1. Calculator as Container
|
438
|
+
%%
|
439
|
+
%% The calculator becomes a container for holding all known operations.
|
440
|
+
%%========================================================================
|
441
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
442
|
+
|
443
|
+
Calculator as Container
|
444
|
+
%bar "#F77" 4 10 80
|
445
|
+
%size 3
|
446
|
+
(calc.rb, continued)
|
447
|
+
|
448
|
+
%font "code", size 3, prefix 15, left
|
449
|
+
class Calculator
|
450
|
+
attr_writer :adder
|
451
|
+
attr_writer :subtractor
|
452
|
+
attr_writer :multiplier
|
453
|
+
attr_writer :divider
|
454
|
+
|
455
|
+
def add( a, b ); @adder.compute( a, b ); end
|
456
|
+
def subtract( a, b ); @subtractor.compute( a, b ); end
|
457
|
+
def multiply( a, b ); @multiplier.compute( a, b ); end
|
458
|
+
def divide( a, b ); @divider.compute( a, b ); end
|
459
|
+
end
|
460
|
+
|
461
|
+
%pause, font "standard", prefix 0, center
|
462
|
+
%bar "#F77" 4 10 80
|
463
|
+
Notice: no explicit dependencies!
|
464
|
+
|
465
|
+
%page
|
466
|
+
%%========================================================================
|
467
|
+
%% h1. Manual Dependency Injection
|
468
|
+
%%
|
469
|
+
%% Manual dependency injection. Manually instantiate all of the
|
470
|
+
%% operations, manually instantiate the calculator, etc, etc.
|
471
|
+
%%========================================================================
|
472
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
473
|
+
|
474
|
+
%size 6, font "standard", prefix 0
|
475
|
+
Manual Dependency Injection
|
476
|
+
%bar "#F77" 4 10 80
|
477
|
+
%size 3
|
478
|
+
(main.rb)
|
479
|
+
|
480
|
+
|
481
|
+
%font "code", size 3, prefix 30, left
|
482
|
+
require 'calc'
|
483
|
+
|
484
|
+
\# instantiate the Calculator
|
485
|
+
calc = Calculator.new
|
486
|
+
|
487
|
+
\# inject the dependencies
|
488
|
+
calc.adder = Adder.new
|
489
|
+
calc.subtracter = Subtracter.new
|
490
|
+
calc.multiplier = Multiplier.new
|
491
|
+
calc.divider = Divider.new
|
492
|
+
|
493
|
+
\# Voil�!
|
494
|
+
p calc.add( 8, 5 )
|
495
|
+
p calc.subtract( 8, 5 )
|
496
|
+
p calc.multiply( 8, 5 )
|
497
|
+
p calc.divide( 8, 5 )
|
498
|
+
|
499
|
+
%page
|
500
|
+
%%========================================================================
|
501
|
+
%% h1. Enter Copland
|
502
|
+
%%
|
503
|
+
%% Copland removes the drudgery from dependency injection, automating it
|
504
|
+
%% based on the dependencies you specify in a _package descriptor_.
|
505
|
+
%% The dependencies in this case are trivial services, and are easily
|
506
|
+
%% defined.
|
507
|
+
%%========================================================================
|
508
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
509
|
+
|
510
|
+
Enter
|
511
|
+
%cont, fore "#F00", font "standard-bold"
|
512
|
+
Copland
|
513
|
+
%bar "#F77" 4 10 80
|
514
|
+
%fore "black", font "standard", size 4
|
515
|
+
|
516
|
+
%pause, lcutin
|
517
|
+
First, create a
|
518
|
+
%cont, font "standard-italic"
|
519
|
+
package descriptor
|
520
|
+
%cont, font "standard"
|
521
|
+
:
|
522
|
+
%size 3
|
523
|
+
(package.yml)
|
524
|
+
|
525
|
+
%pause, font "code", size 3, prefix 30, left
|
526
|
+
---
|
527
|
+
id: calc
|
528
|
+
|
529
|
+
service-points:
|
530
|
+
|
531
|
+
Adder:
|
532
|
+
implementor: calc/Adder
|
533
|
+
Subtractor:
|
534
|
+
implementor: calc/Subtractor
|
535
|
+
Multiplier:
|
536
|
+
implementor: calc/Multiplier
|
537
|
+
Divider:
|
538
|
+
implementor: calc/Divider
|
539
|
+
|
540
|
+
|
541
|
+
%size 4, font "standard-italic", prefix 0, center, fore "#F00"
|
542
|
+
%bar "#F77" 4 10 80
|
543
|
+
continued -->
|
544
|
+
|
545
|
+
%page
|
546
|
+
%%========================================================================
|
547
|
+
%% h1. Package Descriptor (continued)
|
548
|
+
%%
|
549
|
+
%% The calculator service is more complicated, since it needs to declare
|
550
|
+
%% the other services that it is dependent upon.
|
551
|
+
%%========================================================================
|
552
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
553
|
+
|
554
|
+
The Calculator Service Point
|
555
|
+
%bar "#F77" 4 10 80
|
556
|
+
%size 3
|
557
|
+
(package.yml, continued)
|
558
|
+
|
559
|
+
%font "code", size 3, prefix 20, left
|
560
|
+
Calculator:
|
561
|
+
implementor:
|
562
|
+
factory: copland.BuilderFactory
|
563
|
+
class: calc/Calculator
|
564
|
+
properties:
|
565
|
+
adder: !!service calc.Adder
|
566
|
+
subtracter: !!service calc.Subtracter
|
567
|
+
multiplier: !!service calc.Multiplier
|
568
|
+
divider: !!service calc.Divider
|
569
|
+
|
570
|
+
%page
|
571
|
+
%%========================================================================
|
572
|
+
%% h1. Driver File
|
573
|
+
%%
|
574
|
+
%% Here we instantiate a registry, get a reference to the service, and
|
575
|
+
%% use it!
|
576
|
+
%%========================================================================
|
577
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
578
|
+
|
579
|
+
Putting it All Together
|
580
|
+
%bar "#F77" 4 10 80
|
581
|
+
%size 3
|
582
|
+
(main.rb)
|
583
|
+
|
584
|
+
%font "code", size 3, prefix 20, left
|
585
|
+
require 'copland'
|
586
|
+
|
587
|
+
registry = Copland::Registry.build
|
588
|
+
|
589
|
+
calc = registry.service( "calc.Calculator" )
|
590
|
+
|
591
|
+
p calc.add( 8, 5 )
|
592
|
+
p calc.subtract( 8, 5 )
|
593
|
+
p calc.multiply( 8, 5 )
|
594
|
+
p calc.divide( 8, 5 )
|
595
|
+
|
596
|
+
|
597
|
+
%pause, font "standard-italic", size 3, center, prefix 0
|
598
|
+
%bar "#F77" 4 10 80
|
599
|
+
Good, but can we make it better?
|
600
|
+
Can we make it so that third-parties can add new functions?
|
601
|
+
|
602
|
+
%page
|
603
|
+
%%========================================================================
|
604
|
+
%% h1. Refining
|
605
|
+
%%========================================================================
|
606
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
607
|
+
|
608
|
+
Refining the Calculator
|
609
|
+
%bar "#F77" 4 10 80
|
610
|
+
%size 3
|
611
|
+
(calc.rb)
|
612
|
+
%size 2
|
613
|
+
|
614
|
+
%font "code", size 3, prefix 20, left
|
615
|
+
class Calculator
|
616
|
+
# +operations+ must quack like a Hash
|
617
|
+
def initialize( operations )
|
618
|
+
@operations = operations
|
619
|
+
end
|
620
|
+
|
621
|
+
def method_missing( op, *args )
|
622
|
+
if @operations.has_key?( op )
|
623
|
+
return @operations[ op ].compute( *args )
|
624
|
+
else
|
625
|
+
super
|
626
|
+
end
|
627
|
+
end
|
628
|
+
|
629
|
+
def respond_to?( op )
|
630
|
+
super || @operations.has_key?( op )
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
634
|
+
%page
|
635
|
+
%%========================================================================
|
636
|
+
%% h1. Refining (continued)
|
637
|
+
%%
|
638
|
+
%% Since we now expect the operations to be specified to the constructor
|
639
|
+
%% via a hash (or hash-like), we need to change how the Calculator service
|
640
|
+
%% point is defined.
|
641
|
+
%%
|
642
|
+
%% First, we'll create a configuration point for holding the new
|
643
|
+
%% operations, and then we'll contribute the "default" operations to that
|
644
|
+
%% point.
|
645
|
+
%%========================================================================
|
646
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
647
|
+
|
648
|
+
Fix the Package Descriptor
|
649
|
+
%bar "#F77" 4 10 80
|
650
|
+
%size 3
|
651
|
+
(package.yml)
|
652
|
+
|
653
|
+
|
654
|
+
%font "code", size 3, prefix 20, left
|
655
|
+
configuration-points:
|
656
|
+
|
657
|
+
%fore "#F00", font "code-bold"
|
658
|
+
Operations
|
659
|
+
%cont, fore "black", font "code"
|
660
|
+
:
|
661
|
+
type: map
|
662
|
+
|
663
|
+
contributions:
|
664
|
+
|
665
|
+
%font "code-bold", fore "#F00"
|
666
|
+
calc.Operations
|
667
|
+
%cont, font "code", fore "black"
|
668
|
+
:
|
669
|
+
:add: !!service calc.Adder
|
670
|
+
:subtract: !!service calc.Subtracter
|
671
|
+
:multiply: !!service calc.Multiplier
|
672
|
+
:divide: !!service calc.Divider
|
673
|
+
|
674
|
+
|
675
|
+
%bar "#F77" 4 10 80
|
676
|
+
%size 2.5, center, prefix 0, font "standard-italic", fore "#F00"
|
677
|
+
(Note that the keys in this case are symbols,
|
678
|
+
hence the leading colon...)
|
679
|
+
|
680
|
+
%page
|
681
|
+
%%========================================================================
|
682
|
+
%% h1. Refining (continued)
|
683
|
+
%%
|
684
|
+
%% Lastly, we just use the configuration point to initialize the
|
685
|
+
%% Calculator service.
|
686
|
+
%%========================================================================
|
687
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
688
|
+
|
689
|
+
Fix the Package Descriptor
|
690
|
+
%bar "#F77" 4 10 80
|
691
|
+
%size 3
|
692
|
+
(package.yml, continued)
|
693
|
+
|
694
|
+
|
695
|
+
%font "code", size 3, prefix 20, left
|
696
|
+
service-points:
|
697
|
+
...
|
698
|
+
|
699
|
+
Calculator:
|
700
|
+
implementor:
|
701
|
+
factory: copland.BuilderFactory
|
702
|
+
class: calc/Calculator
|
703
|
+
parameters:
|
704
|
+
- !!configuration
|
705
|
+
%cont, fore "#F00", font "code-bold"
|
706
|
+
calc.Operations
|
707
|
+
|
708
|
+
%page
|
709
|
+
%%========================================================================
|
710
|
+
%% h1. Refining (continued)
|
711
|
+
%%
|
712
|
+
%% Finally, we change our driver file and run it.
|
713
|
+
%%========================================================================
|
714
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
715
|
+
|
716
|
+
Driver File
|
717
|
+
%bar "#F77" 4 10 80
|
718
|
+
%size 3
|
719
|
+
(main.rb)
|
720
|
+
|
721
|
+
%font "code", size 3, prefix 20, left
|
722
|
+
require 'copland'
|
723
|
+
|
724
|
+
registry = Copland::Registry.build
|
725
|
+
|
726
|
+
calc = registry.service( "calc.Calculator" )
|
727
|
+
|
728
|
+
p calc.add( 8, 5 )
|
729
|
+
p calc.subtract( 8, 5 )
|
730
|
+
p calc.multiply( 8, 5 )
|
731
|
+
p calc.divide( 8, 5 )
|
732
|
+
|
733
|
+
|
734
|
+
%pause, font "standard-italic", size 3, center, prefix 0
|
735
|
+
%bar "#F77" 4 10 80
|
736
|
+
Note that this is unchanged!
|
737
|
+
We did not change the client interface for Calculator,
|
738
|
+
so clients of the service do not need to change.
|
739
|
+
|
740
|
+
%page
|
741
|
+
%%========================================================================
|
742
|
+
%% h1. Benefits of this approach
|
743
|
+
%%
|
744
|
+
%% * Third parties can easily add new functions to the calculator
|
745
|
+
%% * With just a little more work we could make it possible for third
|
746
|
+
%% parties to redefine the "core" operations
|
747
|
+
%% * Modular, "agile", testable
|
748
|
+
%%========================================================================
|
749
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
750
|
+
|
751
|
+
So What Did This Buy Us?
|
752
|
+
%bar "#F77" 4 10 80
|
753
|
+
|
754
|
+
%size 4, pause, prefix 20, left
|
755
|
+
* Configurable, Reusable, Extensible
|
756
|
+
|
757
|
+
%pause
|
758
|
+
* Third parties can easily add new functions
|
759
|
+
to the calculator by contributing their own
|
760
|
+
services to the calc.Operations configuration
|
761
|
+
point.
|
762
|
+
|
763
|
+
%pause
|
764
|
+
* With just a little more work we could make
|
765
|
+
it possible for third parties to redefine
|
766
|
+
existing operations.
|
767
|
+
|
768
|
+
%page
|
769
|
+
%%========================================================================
|
770
|
+
%% h1. Debuggable: logs
|
771
|
+
%%
|
772
|
+
%% You can easily obtain access to a consistent logging mechanism by
|
773
|
+
%% setting an attribute of your service to a log instance in your
|
774
|
+
%% package descriptor.
|
775
|
+
%%========================================================================
|
776
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
777
|
+
|
778
|
+
Debuggable: Consistent Logging
|
779
|
+
%bar "#F77" 4 10 80
|
780
|
+
%size 3
|
781
|
+
(package.yml)
|
782
|
+
|
783
|
+
%font "code", size 3, prefix 20, left
|
784
|
+
service-points:
|
785
|
+
Calculator:
|
786
|
+
implementor:
|
787
|
+
factory: copland.BuilderFactory
|
788
|
+
class: calc/Calculator
|
789
|
+
parameters:
|
790
|
+
- !!configuration calc.Operations
|
791
|
+
properties:
|
792
|
+
log: !!log ~
|
793
|
+
|
794
|
+
|
795
|
+
%size 4, font "standard-italic", prefix 0, center, fore "#F00"
|
796
|
+
%bar "#F77" 4 10 80
|
797
|
+
continued -->
|
798
|
+
|
799
|
+
%page
|
800
|
+
%%========================================================================
|
801
|
+
%% h1. Debuggable: logs (continued)
|
802
|
+
%%========================================================================
|
803
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
804
|
+
|
805
|
+
Debuggable (continued)
|
806
|
+
%bar "#F77" 4 10 80
|
807
|
+
%size 3
|
808
|
+
(calc.rb)
|
809
|
+
|
810
|
+
%font "code", size 3, prefix 20, left
|
811
|
+
class Calculator
|
812
|
+
|
813
|
+
attr_writer :log
|
814
|
+
|
815
|
+
...
|
816
|
+
|
817
|
+
def method_missing( op, *args )
|
818
|
+
if @operations.has_key?( op )
|
819
|
+
%fore "#F00"
|
820
|
+
@log.debug "invoking #{op}" if @log.debug?
|
821
|
+
%fore "black"
|
822
|
+
return @operations[ op ].compute( *args )
|
823
|
+
else
|
824
|
+
super
|
825
|
+
end
|
826
|
+
end
|
827
|
+
|
828
|
+
...
|
829
|
+
end
|
830
|
+
|
831
|
+
%page
|
832
|
+
%%========================================================================
|
833
|
+
%% h1. Debuggable: interceptors
|
834
|
+
%%========================================================================
|
835
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
836
|
+
|
837
|
+
Debuggable: Interceptors
|
838
|
+
%bar "#F77" 4 10 80
|
839
|
+
%size 3
|
840
|
+
(package.yml)
|
841
|
+
|
842
|
+
%font "code", size 3, prefix 20, left
|
843
|
+
service-points:
|
844
|
+
Calculator:
|
845
|
+
implementor:
|
846
|
+
factory: copland.BuilderFactory
|
847
|
+
class: calc/Calculator
|
848
|
+
parameters:
|
849
|
+
- !!configuration calc.Operations
|
850
|
+
interceptors:
|
851
|
+
- service: copland.LoggingInterceptor
|
852
|
+
|
853
|
+
%page
|
854
|
+
%%========================================================================
|
855
|
+
%% h1. Testable
|
856
|
+
%%
|
857
|
+
%% Each component may be tested independently of the others by substituting
|
858
|
+
%% mock objects.
|
859
|
+
%%========================================================================
|
860
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
861
|
+
|
862
|
+
Testable
|
863
|
+
%bar "#F77" 4 10 80
|
864
|
+
%size 3
|
865
|
+
(test.rb)
|
866
|
+
|
867
|
+
%font "code", size 2.5, prefix 15, left
|
868
|
+
class TC_Calculator < Test::Unit::TestCase
|
869
|
+
class MockOperation; def compute( a, b ); "#{a}:#{b}"; end; end
|
870
|
+
|
871
|
+
def setup; @calc = Calculator.new( :mock => MockOperation.new ); end
|
872
|
+
|
873
|
+
def test_operation
|
874
|
+
assert_equal "hello:world", @calc.mock( "hello", "world" )
|
875
|
+
end
|
876
|
+
|
877
|
+
def test_missing
|
878
|
+
assert_raised( NoMethodError ) { @calc.bogus( 1, 2 ) }
|
879
|
+
end
|
880
|
+
end
|
881
|
+
|
882
|
+
class TC_Adder < Test::Unit::TestCase
|
883
|
+
def test_add
|
884
|
+
assert_equal -5, Adder.new.compute( 1, -6 )
|
885
|
+
end
|
886
|
+
end
|
887
|
+
|
888
|
+
%bar "#F77" 4 10 80
|
889
|
+
%size 2.5, center, prefix 0, font "standard-italic", fore "#F00"
|
890
|
+
Each component can be tested independently
|
891
|
+
|
892
|
+
%page
|
893
|
+
%%========================================================================
|
894
|
+
%% h1. A "real" example
|
895
|
+
%%
|
896
|
+
%%========================================================================
|
897
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
898
|
+
|
899
|
+
A Real Example
|
900
|
+
%bar "#F77" 4 10 80
|
901
|
+
|
902
|
+
%size 4, prefix 20, left
|
903
|
+
*
|
904
|
+
%cont, fore "#F00"
|
905
|
+
Packrat
|
906
|
+
%cont, fore "black"
|
907
|
+
: package documentation extractor
|
908
|
+
for Copland
|
909
|
+
|
910
|
+
example output:
|
911
|
+
%fore "#00F"
|
912
|
+
http://copland.rubyforge.org/packrat
|
913
|
+
%fore "black"
|
914
|
+
|
915
|
+
* Looks at all available package descriptors
|
916
|
+
and formats them according to a specified
|
917
|
+
template.
|
918
|
+
|
919
|
+
* Implemented using Copland!
|
920
|
+
|
921
|
+
%page
|
922
|
+
%%========================================================================
|
923
|
+
%% h1. Other uses...
|
924
|
+
%%
|
925
|
+
%% * Database abstraction layer
|
926
|
+
%% * object-relational mapping tool
|
927
|
+
%% * web application framework
|
928
|
+
%% * tying all three above together!
|
929
|
+
%% * personal finance manager
|
930
|
+
%% * text editor
|
931
|
+
%%========================================================================
|
932
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
933
|
+
|
934
|
+
Copland: Other Uses
|
935
|
+
%bar "#F77" 4 10 80
|
936
|
+
|
937
|
+
%size 4, pause, lcutin
|
938
|
+
Database abstraction layer
|
939
|
+
|
940
|
+
%pause, rcutin
|
941
|
+
Object-relational mapping layer
|
942
|
+
|
943
|
+
%pause, lcutin
|
944
|
+
Web application framework
|
945
|
+
|
946
|
+
%pause, rcutin
|
947
|
+
All-In-One: "
|
948
|
+
%fore "#F00", cont
|
949
|
+
Framework Glue
|
950
|
+
%fore "black", cont
|
951
|
+
"
|
952
|
+
|
953
|
+
%pause, lcutin
|
954
|
+
Text editor, IDE, Mail client, etc...
|
955
|
+
|
956
|
+
%page
|
957
|
+
%%========================================================================
|
958
|
+
%% h1. Other features
|
959
|
+
%%
|
960
|
+
%% service models, substitution symbols, etc.
|
961
|
+
%%========================================================================
|
962
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
963
|
+
|
964
|
+
Other Features of Copland
|
965
|
+
%bar "#F77" 4 10 80, size 2
|
966
|
+
|
967
|
+
%size 3, prefix 10, font "standard-bold", rcutin, left
|
968
|
+
Service Models
|
969
|
+
|
970
|
+
%prefix 20, font "standard", pause
|
971
|
+
prototype, prototype-deferred, singleton, singleton-deferred,
|
972
|
+
threaded, roll-your-own
|
973
|
+
|
974
|
+
%pause, rcutin, prefix 10, font "standard-bold"
|
975
|
+
Substitution Symbols
|
976
|
+
|
977
|
+
%prefix 20, font "code", pause
|
978
|
+
properties:
|
979
|
+
user: ${user.name}
|
980
|
+
...
|
981
|
+
contributions:
|
982
|
+
copland.ApplicationDefaults:
|
983
|
+
user.name: minam
|
984
|
+
|
985
|
+
%pause, rcutin, prefix 10, font "standard-bold"
|
986
|
+
Other Features
|
987
|
+
|
988
|
+
%prefix 20, font "standard", pause
|
989
|
+
customizable logging, listener/producer systems,
|
990
|
+
interceptor ordering, distributing packages
|
991
|
+
|
992
|
+
%page
|
993
|
+
%%========================================================================
|
994
|
+
%% h1. Other cool things to try...
|
995
|
+
%%
|
996
|
+
%% copland-lib, copland-remote, copland-webrick
|
997
|
+
%%========================================================================
|
998
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
999
|
+
|
1000
|
+
Other Cool Things to Try
|
1001
|
+
%bar "#F77" 4 10 80
|
1002
|
+
|
1003
|
+
%prefix 10, size 3, font "standard-bold", left
|
1004
|
+
copland-lib
|
1005
|
+
|
1006
|
+
%prefix 20, font "standard", pause
|
1007
|
+
* multicast services
|
1008
|
+
%pause
|
1009
|
+
* interceptors for redirecting, blocking, and synchronizing
|
1010
|
+
messages
|
1011
|
+
|
1012
|
+
%prefix 10, font "standard-bold", pause
|
1013
|
+
copland-remote
|
1014
|
+
|
1015
|
+
%prefix 20, font "standard", pause
|
1016
|
+
* bridge remote registries via DRb, SOAP, XML-RPC
|
1017
|
+
%pause
|
1018
|
+
* import web services transparently as Copland services
|
1019
|
+
%pause
|
1020
|
+
* easily export any Copland service as a web service
|
1021
|
+
|
1022
|
+
|
1023
|
+
%prefix 10, font "standard-bold", pause
|
1024
|
+
copland-webrick
|
1025
|
+
|
1026
|
+
%prefix 20, font "standard", pause
|
1027
|
+
* easily configure and operate WEBrick servers
|
1028
|
+
%pause
|
1029
|
+
* make any service invokable via HTTP using WEBrick
|
1030
|
+
|
1031
|
+
%page
|
1032
|
+
%%========================================================================
|
1033
|
+
%% h1. Future Directions
|
1034
|
+
%%========================================================================
|
1035
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
1036
|
+
|
1037
|
+
Future Directions
|
1038
|
+
%bar "#F77" 4 10 80
|
1039
|
+
|
1040
|
+
%size 4, prefix 20, left
|
1041
|
+
* Type 1 IoC (Interface Injection)
|
1042
|
+
|
1043
|
+
* Simplified API for package/service creation
|
1044
|
+
|
1045
|
+
* Define dependencies in code
|
1046
|
+
"ActiveCopland"
|
1047
|
+
%cont, font "standard-italic", size 3
|
1048
|
+
(sorry, David...)
|
1049
|
+
%size 4, font "standard"
|
1050
|
+
|
1051
|
+
* "Ruby-ize". Might require a new project...
|
1052
|
+
|
1053
|
+
* Educate, educate, educate!
|
1054
|
+
|
1055
|
+
%page
|
1056
|
+
%%========================================================================
|
1057
|
+
%% h1. Getting Copland
|
1058
|
+
%%========================================================================
|
1059
|
+
%fore "black", center, area 100 100 0 0, size 6, font "standard", prefix 0
|
1060
|
+
|
1061
|
+
Getting Copland
|
1062
|
+
%bar "#F77" 4 10 80
|
1063
|
+
|
1064
|
+
%size 4, prefix 20, left
|
1065
|
+
* RubyGems:
|
1066
|
+
%font "code", cont
|
1067
|
+
gem install copland
|
1068
|
+
%font "standard"
|
1069
|
+
|
1070
|
+
* RPA:
|
1071
|
+
%font "code", cont
|
1072
|
+
rpa install copland
|
1073
|
+
%font "standard"
|
1074
|
+
|
1075
|
+
* tar.bz2, tar.gz, zip:
|
1076
|
+
%fore "#00F"
|
1077
|
+
http://rubyforge.org/projects/copland
|
1078
|
+
%fore "black"
|
1079
|
+
|
1080
|
+
* User's Manual:
|
1081
|
+
%fore "#00F"
|
1082
|
+
http://copland.rubyforge.org
|
1083
|
+
%fore "black"
|