copland 0.8.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/doc/manual-html/chapter-1.html +227 -36
  2. data/doc/manual-html/chapter-10.html +155 -82
  3. data/doc/manual-html/chapter-11.html +90 -267
  4. data/doc/manual-html/chapter-12.html +289 -71
  5. data/doc/manual-html/chapter-13.html +430 -0
  6. data/doc/manual-html/chapter-2.html +45 -21
  7. data/doc/manual-html/chapter-3.html +45 -21
  8. data/doc/manual-html/chapter-4.html +45 -21
  9. data/doc/manual-html/chapter-5.html +45 -21
  10. data/doc/manual-html/chapter-6.html +49 -21
  11. data/doc/manual-html/chapter-7.html +45 -21
  12. data/doc/manual-html/chapter-8.html +66 -26
  13. data/doc/manual-html/chapter-9.html +48 -24
  14. data/doc/manual-html/index.html +54 -22
  15. data/doc/manual-html/manual.css +12 -0
  16. data/doc/manual-html/tutorial-1.html +45 -21
  17. data/doc/manual-html/tutorial-2.html +45 -21
  18. data/doc/manual-html/tutorial-3.html +45 -21
  19. data/doc/manual-html/tutorial-4.html +45 -21
  20. data/doc/manual-html/tutorial-5.html +45 -21
  21. data/doc/manual/manual.css +12 -0
  22. data/doc/manual/manual.rb +1 -1
  23. data/doc/manual/manual.yml +426 -20
  24. data/doc/packages/copland.html +41 -9
  25. data/doc/packages/copland.lib.html +36 -8
  26. data/doc/packages/copland.remote.html +46 -10
  27. data/doc/packages/copland.webrick.html +16 -65
  28. data/doc/packages/index.html +1 -1
  29. data/doc/presentation/copland.mgp +1083 -0
  30. data/doc/presentation/to_html.rb +52 -0
  31. data/lib/copland/configuration-point/common.rb +32 -1
  32. data/lib/copland/configuration/yaml/service-point.rb +10 -1
  33. data/lib/copland/log-factory.rb +28 -12
  34. data/lib/copland/logger.rb +155 -0
  35. data/lib/copland/models/singleton.rb +8 -2
  36. data/lib/copland/package.rb +32 -14
  37. data/lib/copland/service-point.rb +7 -0
  38. data/lib/copland/thread.rb +104 -0
  39. data/lib/copland/utils.rb +10 -3
  40. data/lib/copland/version.rb +2 -2
  41. data/test/configuration/yaml/tc_service-point-processor.rb +8 -0
  42. data/test/custom-logger.yml +2 -1
  43. data/test/impl/tc_logging-interceptor.rb +12 -12
  44. data/test/logger.yml +1 -1
  45. data/test/mock.rb +2 -0
  46. data/test/tc_logger.rb +19 -6
  47. data/test/tc_package.rb +25 -0
  48. data/test/tc_queryable-mutex.rb +75 -0
  49. data/test/tc_registry.rb +8 -4
  50. metadata +9 -2
@@ -14,8 +14,8 @@
14
14
  </div>
15
15
  </td><td valign='middle' align='right'>
16
16
  <div class="info">
17
- Copland Version: <strong>0.8.0</strong><br />
18
- Manual Last Updated: <strong>2004-09-27 03:37 GMT</strong>
17
+ Copland Version: <strong>1.0.0</strong><br />
18
+ Manual Last Updated: <strong>2004-10-12 02:22 GMT</strong>
19
19
  </div>
20
20
  </td></tr>
21
21
  </table>
@@ -39,13 +39,21 @@
39
39
 
40
40
  <li><a href="chapter-1.html#s1">What is Copland?</a></li>
41
41
 
42
- <li><a href="chapter-1.html#s2">Features</a></li>
42
+ <li><a href="chapter-1.html#s2">What Can Copland Do For Me?</a></li>
43
43
 
44
- <li><a href="chapter-1.html#s3">Getting Copland</a></li>
44
+ <li><a href="chapter-1.html#s3">Copland <em>sans</em> Buzzwords</a></li>
45
45
 
46
- <li><a href="chapter-1.html#s4">License Information</a></li>
46
+ <li><a href="chapter-1.html#s4">Copland <em>avec</em> Buzzwords</a></li>
47
47
 
48
- <li><a href="chapter-1.html#s5">Support</a></li>
48
+ <li><a href="chapter-1.html#s5">The Buzzwords Themselves</a></li>
49
+
50
+ <li><a href="chapter-1.html#s6">Features</a></li>
51
+
52
+ <li><a href="chapter-1.html#s7">Getting Copland</a></li>
53
+
54
+ <li><a href="chapter-1.html#s8">License Information</a></li>
55
+
56
+ <li><a href="chapter-1.html#s9">Support</a></li>
49
57
 
50
58
  </ol>
51
59
  </li>
@@ -149,7 +157,9 @@
149
157
 
150
158
  <li><a href="chapter-8.html#s1">Descriptor Syntax</a></li>
151
159
 
152
- <li><a href="chapter-8.html#s2">DefaultSymbolSource</a></li>
160
+ <li><a href="chapter-8.html#s2">FactoryDefaults</a></li>
161
+
162
+ <li><a href="chapter-8.html#s3">ApplicationDefaults</a></li>
153
163
 
154
164
  </ol>
155
165
  </li>
@@ -166,54 +176,68 @@
166
176
 
167
177
  <li>
168
178
  <a href="chapter-10.html">
179
+ Logging
180
+ </a>
181
+
182
+ <ol type="1">
183
+
184
+ <li><a href="chapter-10.html#s1">Log Factory</a></li>
185
+
186
+ <li><a href="chapter-10.html#s2">Configuration</a></li>
187
+
188
+ </ol>
189
+ </li>
190
+
191
+ <li>
192
+ <a href="chapter-11.html">
169
193
  Service Factories
170
194
  </a>
171
195
 
172
196
  <ol type="1">
173
197
 
174
- <li><a href="chapter-10.html#s1">Schemas</a></li>
198
+ <li><a href="chapter-11.html#s1">Schemas</a></li>
175
199
 
176
- <li><a href="chapter-10.html#s2">How do they work?</a></li>
200
+ <li><a href="chapter-11.html#s2">How do they work?</a></li>
177
201
 
178
- <li><a href="chapter-10.html#s3">BuilderFactory</a></li>
202
+ <li><a href="chapter-11.html#s3">BuilderFactory</a></li>
179
203
 
180
204
  </ol>
181
205
  </li>
182
206
 
183
207
  <li>
184
- <a href="chapter-11.html">
208
+ <a href="chapter-12.html">
185
209
  Schemas
186
210
  </a>
187
211
 
188
212
  <ol type="1">
189
213
 
190
- <li><a href="chapter-11.html#s1">Basic Format</a></li>
214
+ <li><a href="chapter-12.html#s1">Basic Format</a></li>
191
215
 
192
- <li><a href="chapter-11.html#s2">Subschemas</a></li>
216
+ <li><a href="chapter-12.html#s2">Subschemas</a></li>
193
217
 
194
- <li><a href="chapter-11.html#s3">Arrays</a></li>
218
+ <li><a href="chapter-12.html#s3">Arrays</a></li>
195
219
 
196
- <li><a href="chapter-11.html#s4">Named vs. Anonymous Schemas</a></li>
220
+ <li><a href="chapter-12.html#s4">Named vs. Anonymous Schemas</a></li>
197
221
 
198
- <li><a href="chapter-11.html#s5">Extending Schemas</a></li>
222
+ <li><a href="chapter-12.html#s5">Extending Schemas</a></li>
199
223
 
200
- <li><a href="chapter-11.html#s6">Limitations</a></li>
224
+ <li><a href="chapter-12.html#s6">Limitations</a></li>
201
225
 
202
226
  </ol>
203
227
  </li>
204
228
 
205
229
  <li>
206
- <a href="chapter-12.html">
230
+ <a href="chapter-13.html">
207
231
  Listeners and Event Producers
208
232
  </a>
209
233
 
210
234
  <ol type="1">
211
235
 
212
- <li><a href="chapter-12.html#s1">Event Producers</a></li>
236
+ <li><a href="chapter-13.html#s1">Event Producers</a></li>
213
237
 
214
- <li><a href="chapter-12.html#s2">Listeners</a></li>
238
+ <li><a href="chapter-13.html#s2">Listeners</a></li>
215
239
 
216
- <li><a href="chapter-12.html#s3">The Registry as an Event Producer</a></li>
240
+ <li><a href="chapter-13.html#s3">The Registry as an Event Producer</a></li>
217
241
 
218
242
  </ol>
219
243
  </li>
@@ -303,11 +327,160 @@
303
327
 
304
328
 
305
329
  <div class="section">
306
- <p>Copland is an <em>Inversion of Control</em> (IoC) container, written in <a href="http://www.ruby-lang.org">Ruby</a>, for use in Ruby programs. Depending on whether or not the term &#8220;IoC&#8221; means anything at all to you, you can read either the next section (&#8220;Copland <em>sans</em> Buzzwords&#8221;), or the one after it (&#8220;Copland <em>avec</em> Buzzwords&#8221;).</p>
330
+ <p>Copland is an <em>Inversion of Control</em> (IoC) container, written in <a href="http://www.ruby-lang.org">Ruby</a>, for use in Ruby programs. In a nutshell, it allows you to simplify the instantiation and initialization of your classes.<br />
331
+ </p>
332
+ </div>
333
+
334
+
335
+
336
+ <h2>
337
+ <a name="s2"></a>
338
+ 1.2. What Can Copland Do For Me?
339
+ </h2>
340
+
341
+
342
+
343
+ <div class="section">
344
+ <p>So, what can Copland do for you? Ultimately, it can reduce the amount of code that you have to write, simplifying many common programming tasks for you. This has the two-fold benefit of both decreasing application development time, and of decreasing the effort needed to maintain your application.</p>
345
+
346
+ <p>But what, <em>specifically</em>, can Copland do for you?</p>
347
+
348
+ <p>Try these on for size:</p>
349
+
350
+ <ul>
351
+ <li><a href="#logmethods">Log Method Execution</a></li>
352
+ <li><a href="#refsvc">Reference Another Service</a></li>
353
+ <li><a href="#svcconfig">Service Configuration</a></li>
354
+ <li><a href="#unittest">Unit Testing</a></li>
355
+ <li><a href="#lifecycle">Lifecycle Management</a></li>
356
+ </ul>
357
+
358
+ <p>(Thanks to Howard Lewis Ship for his <a href="http://jakarta.apache.org/hivemind">HiveMind</a> documentation, from which most of the above bullet points were adapted.)</p>
359
+
360
+ <h3>A. Log Method Execution <a name="logmethods"></a></h3>
361
+
362
+ <p>Copland has an integrated logging framework, and the ability to log execution trace information <em>without modifying a single line of your code</em>. This means that you can easily see what methods get called, with what arguments, and what the return values are, all without having to add a single line of logging code to your classes.</p>
363
+
364
+ <p>Consider the following code, demonstrating how this would be done <em>without</em> Copland:</p>
365
+
366
+ <pre>
367
+ def foo( arg1, arg2 )
368
+ @log.debug( "in foo with #{arg1} and #{arg2}" ) if @log.debug?
369
+ ...
370
+ result = the_result_of_the_method
371
+ @log.debug( "finishing foo with #{result}" ) if @log.debug
372
+ return result
373
+ rescue Exception =&gt; e
374
+ @log.debug( "foo raised exception #{e.message} (#{e.class})" ) if @log.debug?
375
+ raise
376
+ end
377
+ </pre>
378
+
379
+ <p>Now, multiply that by the number of methods in your class&#8230; the logging messages quickly overpower the rest of the code, and detract from the flow of your program. This makes your program harder to debug, test, and maintain.</p>
380
+
381
+ <p>Now, consider the same method using Copland&#8217;s integrated logging framework&#8230;</p>
382
+
383
+ <pre>
384
+ def foo( arg1, arg2 )
385
+ ...
386
+ return the_result_of_the_method
387
+ end
388
+ </pre>
389
+
390
+ <p>Uh, huh. That&#8217;s right. <em>There&#8217;s no explicit logging code in there.</em> Instead, you just tell Copland that the methods of the class should be logged, and away it goes. This has the added benefit of allowing your objects to be unit tested, without spewing log messages everywhere. (Look for the tutorial about &#8220;Logging Interceptors&#8221; for an actual demonstration of how to do this.)</p>
391
+
392
+ <h3>B. Reference Another Service <a name="refsvc"></a></h3>
393
+
394
+ <p>Invariably in a large application services will reference other services. This is typically accomplished through something like this:</p>
395
+
396
+ <pre>
397
+ def foo( parms )
398
+ @service ||= lookup_service
399
+ @service.do_something( parms )
400
+ end
401
+ </pre>
402
+
403
+ <p>Whether the lookup is done lazily, as shown above, or when the class is first instantiated is irrelevant. The point is that you either have to implement a bunch of code to look up a service based on some criteria, or you hard code the class of the service (which creates tight coupling and makes things like unit testing harder).</p>
404
+
405
+ <p>With Copland, you just declare a setter for the service, and then tell Copland that the class depends on the other service:</p>
406
+
407
+ <pre>
408
+ attr_writer :service
409
+
410
+ def foo( parms )
411
+ @service.do_something( parms )
412
+ end
413
+ </pre>
414
+
415
+ <p>Then, when your service is instantiated, Copland will automatically look for, instantiate, and set the dependencies for you. This makes for cleaner code, and looser coupling between services.</p>
416
+
417
+ <h3>C. Service Configuration <a name="svcconfig"></a></h3>
418
+
419
+ <p>Often, a large application or library will need some way to allow different classes to be configured at runtime. Whether you have a factory class that requires the available producable classes to register themselves with it, or whether you just want to allow third-parties to be able to extend your application, you wind up implementing some form of decentralized configuration.</p>
420
+
421
+ <p>Copland allows you to define <em>configuration points</em>, which any package may then contribute values to. Furthermore, just as services can reference other services, services can reference configuration points, and Copland will manage them just like any other dependency.</p>
422
+
423
+ <p>See the tutorial about &#8220;Configuration Points&#8221; for an example of this feature.</p>
424
+
425
+ <h3>D. Unit Testing <a name="unittest"></a></h3>
426
+
427
+ <p>Large applications can prove troublesome to unit test exhaustively, especially if there is any kind of tight coupling between components. Such coupling of components can make it difficult to test them separately.</p>
428
+
429
+ <p>Copland, by its very nature, encourages loose coupling of components. Also, because dependencies are never instantiated in code, but are instead accepted via setters or constructor arguments, it is trivial to replace those dependencies with mock objects at unit test time.</p>
430
+
431
+ <p>Consider this tightly coupled example:</p>
432
+
433
+ <pre>
434
+ def foo( args )
435
+ @some_dependency ||= MyNewDependency.new
436
+ @some_dependency.do_something(args)
437
+ end
438
+ </pre>
439
+
440
+ <p>It is impossible to test the method <code>#foo</code> without also testing the MyNewDependency class. However, if the <code>@some_dependency</code> object is made a property that is set externally, you can replace it at test time with a blank:</p>
441
+
442
+ <pre>
443
+ attr_writer :some_dependency
444
+
445
+ def foo( args )
446
+ @some_dependency.do_something( args )
447
+ end
448
+ </pre>
449
+
450
+ <p>The unit test would become something like this:</p>
451
+
452
+ <pre>
453
+ def test_foo
454
+ @obj.some_dependecy = MyMockDependency.new
455
+ @obj.foo( args )
456
+ assert @obj.is_in_some_state
457
+ end
458
+ </pre>
459
+
460
+ <h3>E. Lifecycle Management <a name="lifecycle"></a></h3>
307
461
 
308
- <h3>Copland <em>sans</em> Buzzwords</h3>
462
+ <p>Singleton objects are a fact of life in complex systems. The singleton design pattern is powerful and useful. However, using the Singleton mixin, or declaring methods at the class level, can make your code difficult to unit test since the state of such objects cannot be easily reset.</p>
309
463
 
310
- <p>Imagine being able to take all the various pieces of your program and implement them in a vacuum. You just assume that all dependencies will be satisfied at runtime, by properties being set, or parameters being passed to each object&#8217;s constructor. Nowhere in your code do you specify the instantiation of another application component.</p>
464
+ <p>Copland has a solution. You can tell Copland to treat a service as either a <em>prototype</em> service (meaning it will be instantiated every time you ask for it, like calling <code>#new</code>), or a <em>singleton</em> service (meaning it will only be instantiated once, and the same instance will be returned for subsequent requests).</p>
465
+
466
+ <p>Your object is still just a plain ol&#8217; ordinary Ruby object, but Copland has effectively transformed it into a singleton. This means you can unit test it as if it were nothing special, but when it is used in your application it will act like a singleton.</p>
467
+
468
+ <p>Lifecycle management also means that you can control <em>when</em> a service is instantiated. The <em>prototype</em> and <em>singleton</em> models will always be instantiated as soon as they are requested. Sometimes, though, you don&#8217;t want that&#8212;you&#8217;d like the instantiation to be deferred as long as possible.</p>
469
+
470
+ <p>With Copland, you can indicate that a service should use deferred instantiation. This will cause the service to not actually be instantiated until a method is actually invoked on it. Using this model, you can have services depend on themselves, or other forms of cyclical dependencies.</p>
471
+ </div>
472
+
473
+
474
+
475
+ <h2>
476
+ <a name="s3"></a>
477
+ 1.3. Copland <em>sans</em> Buzzwords
478
+ </h2>
479
+
480
+
481
+
482
+ <div class="section">
483
+ <p>Imagine being able to take all the various pieces of your program and implement them in a vacuum. You just assume that all dependencies will be satisfied at runtime, by properties being set, or parameters being passed to each object&#8217;s constructor. Nowhere in your code do you specify the instantiation of another application component.</p>
311
484
 
312
485
  <p>However, those dependencies and relationships between objects <em>still exist</em>; you&#8217;ve just written your code without any explicit knowledge of them. Somehow, you still need to be able to wire the pieces all together so they work as required in tandem. To put it another way, you need to be able to take your classes and <em>compose</em> them into something greater.</p>
313
486
 
@@ -321,10 +494,19 @@
321
494
  <li>It helps you to focus on the implementation of a single class by allowing you to ignore the implementation details of the dependencies of the current class.</li>
322
495
  <li>It helps you to focus on the relationships between classes by allowing you to specify those relationships and dependencies as run-time configuration options.</li>
323
496
  </ol>
497
+ </div>
324
498
 
325
- <h3>Copland <em>avec</em> Buzzwords</h3>
326
499
 
327
- <p>If you are already familiar with IoC, then you either love it or hate it. In the first case, you probably only want to know what features Copland has compared to other IoC containers you&#8217;ve used. In the second case, there&#8217;s probably not much I can say to change your opinion, so I won&#8217;t try.</p>
500
+
501
+ <h2>
502
+ <a name="s4"></a>
503
+ 1.4. Copland <em>avec</em> Buzzwords
504
+ </h2>
505
+
506
+
507
+
508
+ <div class="section">
509
+ <p>If you are already familiar with IoC, then you either love it or hate it. In the first case, you probably only want to know what features Copland has compared to other IoC containers you&#8217;ve used. In the second case, there&#8217;s probably not much I can say to change your opinion, so I won&#8217;t try.</p>
328
510
 
329
511
  <p>I&#8217;ll save all the meaty discussions for the chapter on Copland&#8217;s design, but here&#8217;s the rundown:</p>
330
512
 
@@ -333,10 +515,19 @@
333
515
  <li>Copland allows you to define <em>service points</em>, <em>configuration points</em>, contribute to <em>configuration points</em>, add <em>interceptors</em> to services, add <em>listeners</em> to services, define <em>multicast</em> services, and even make your services web-enabled by using the built-in <span class="caps">SOAP</span> or dRuby interfaces.</li>
334
516
  <li>Copland uses <a href="http://www.yaml.org">YAML</a> for its configuration files, instead of <span class="caps">XML</span>. If you&#8217;ve never used <span class="caps">YAML</span>, you&#8217;ll find it surprisingly easy to pick up.</li>
335
517
  </ul>
518
+ </div>
519
+
520
+
521
+
522
+ <h2>
523
+ <a name="s5"></a>
524
+ 1.5. The Buzzwords Themselves
525
+ </h2>
336
526
 
337
- <h3>The Buzzwords Themselves</h3>
527
+
338
528
 
339
- <p>As <a href="http://www.martinfowler.com">Martin Fowler</a> <a href="http://martinfowler.com/articles/injection.html">points out</a>, the term &#8220;Inversion of Control&#8221; is a poorly-chosen one. Still, it is the one that most people know the concept by, so I&#8217;ll use it throughout this document.</p>
529
+ <div class="section">
530
+ <p>As <a href="http://www.martinfowler.com">Martin Fowler</a> <a href="http://martinfowler.com/articles/injection.html">points out</a>, the term &#8220;Inversion of Control&#8221; is a poorly-chosen one. Still, it is the one that most people know the concept by, so I&#8217;ll use it throughout this document.</p>
340
531
 
341
532
  <p>For more info about IoC, you might try reading the following articles:</p>
342
533
 
@@ -350,8 +541,8 @@
350
541
 
351
542
 
352
543
  <h2>
353
- <a name="s2"></a>
354
- 1.2. Features
544
+ <a name="s6"></a>
545
+ 1.6. Features
355
546
  </h2>
356
547
 
357
548
 
@@ -375,8 +566,8 @@
375
566
 
376
567
 
377
568
  <h2>
378
- <a name="s3"></a>
379
- 1.3. Getting Copland
569
+ <a name="s7"></a>
570
+ 1.7. Getting Copland
380
571
  </h2>
381
572
 
382
573
 
@@ -416,8 +607,8 @@
416
607
 
417
608
 
418
609
  <h2>
419
- <a name="s4"></a>
420
- 1.4. License Information
610
+ <a name="s8"></a>
611
+ 1.8. License Information
421
612
  </h2>
422
613
 
423
614
 
@@ -432,8 +623,8 @@
432
623
 
433
624
 
434
625
  <h2>
435
- <a name="s5"></a>
436
- 1.5. Support
626
+ <a name="s9"></a>
627
+ 1.9. Support
437
628
  </h2>
438
629
 
439
630
 
@@ -1,6 +1,6 @@
1
1
  <html>
2
2
  <head>
3
- <title>Copland Manual :: Chapter 10: Service Factories</title>
3
+ <title>Copland Manual :: Chapter 10: Logging</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
- Copland Version: <strong>0.8.0</strong><br />
18
- Manual Last Updated: <strong>2004-09-27 03:37 GMT</strong>
17
+ Copland Version: <strong>1.0.0</strong><br />
18
+ Manual Last Updated: <strong>2004-10-12 02:22 GMT</strong>
19
19
  </div>
20
20
  </td></tr>
21
21
  </table>
@@ -39,13 +39,21 @@
39
39
 
40
40
  <li><a href="chapter-1.html#s1">What is Copland?</a></li>
41
41
 
42
- <li><a href="chapter-1.html#s2">Features</a></li>
42
+ <li><a href="chapter-1.html#s2">What Can Copland Do For Me?</a></li>
43
43
 
44
- <li><a href="chapter-1.html#s3">Getting Copland</a></li>
44
+ <li><a href="chapter-1.html#s3">Copland <em>sans</em> Buzzwords</a></li>
45
45
 
46
- <li><a href="chapter-1.html#s4">License Information</a></li>
46
+ <li><a href="chapter-1.html#s4">Copland <em>avec</em> Buzzwords</a></li>
47
47
 
48
- <li><a href="chapter-1.html#s5">Support</a></li>
48
+ <li><a href="chapter-1.html#s5">The Buzzwords Themselves</a></li>
49
+
50
+ <li><a href="chapter-1.html#s6">Features</a></li>
51
+
52
+ <li><a href="chapter-1.html#s7">Getting Copland</a></li>
53
+
54
+ <li><a href="chapter-1.html#s8">License Information</a></li>
55
+
56
+ <li><a href="chapter-1.html#s9">Support</a></li>
49
57
 
50
58
  </ol>
51
59
  </li>
@@ -149,7 +157,9 @@
149
157
 
150
158
  <li><a href="chapter-8.html#s1">Descriptor Syntax</a></li>
151
159
 
152
- <li><a href="chapter-8.html#s2">DefaultSymbolSource</a></li>
160
+ <li><a href="chapter-8.html#s2">FactoryDefaults</a></li>
161
+
162
+ <li><a href="chapter-8.html#s3">ApplicationDefaults</a></li>
153
163
 
154
164
  </ol>
155
165
  </li>
@@ -166,54 +176,68 @@
166
176
 
167
177
  <li><strong>
168
178
  <a href="chapter-10.html">
169
- Service Factories
179
+ Logging
170
180
  </a>
171
181
  </strong> <big>&larr;</big>
172
182
  <ol type="1">
173
183
 
174
- <li><a href="chapter-10.html#s1">Schemas</a></li>
184
+ <li><a href="chapter-10.html#s1">Log Factory</a></li>
175
185
 
176
- <li><a href="chapter-10.html#s2">How do they work?</a></li>
177
-
178
- <li><a href="chapter-10.html#s3">BuilderFactory</a></li>
186
+ <li><a href="chapter-10.html#s2">Configuration</a></li>
179
187
 
180
188
  </ol>
181
189
  </li>
182
190
 
183
191
  <li>
184
192
  <a href="chapter-11.html">
193
+ Service Factories
194
+ </a>
195
+
196
+ <ol type="1">
197
+
198
+ <li><a href="chapter-11.html#s1">Schemas</a></li>
199
+
200
+ <li><a href="chapter-11.html#s2">How do they work?</a></li>
201
+
202
+ <li><a href="chapter-11.html#s3">BuilderFactory</a></li>
203
+
204
+ </ol>
205
+ </li>
206
+
207
+ <li>
208
+ <a href="chapter-12.html">
185
209
  Schemas
186
210
  </a>
187
211
 
188
212
  <ol type="1">
189
213
 
190
- <li><a href="chapter-11.html#s1">Basic Format</a></li>
214
+ <li><a href="chapter-12.html#s1">Basic Format</a></li>
191
215
 
192
- <li><a href="chapter-11.html#s2">Subschemas</a></li>
216
+ <li><a href="chapter-12.html#s2">Subschemas</a></li>
193
217
 
194
- <li><a href="chapter-11.html#s3">Arrays</a></li>
218
+ <li><a href="chapter-12.html#s3">Arrays</a></li>
195
219
 
196
- <li><a href="chapter-11.html#s4">Named vs. Anonymous Schemas</a></li>
220
+ <li><a href="chapter-12.html#s4">Named vs. Anonymous Schemas</a></li>
197
221
 
198
- <li><a href="chapter-11.html#s5">Extending Schemas</a></li>
222
+ <li><a href="chapter-12.html#s5">Extending Schemas</a></li>
199
223
 
200
- <li><a href="chapter-11.html#s6">Limitations</a></li>
224
+ <li><a href="chapter-12.html#s6">Limitations</a></li>
201
225
 
202
226
  </ol>
203
227
  </li>
204
228
 
205
229
  <li>
206
- <a href="chapter-12.html">
230
+ <a href="chapter-13.html">
207
231
  Listeners and Event Producers
208
232
  </a>
209
233
 
210
234
  <ol type="1">
211
235
 
212
- <li><a href="chapter-12.html#s1">Event Producers</a></li>
236
+ <li><a href="chapter-13.html#s1">Event Producers</a></li>
213
237
 
214
- <li><a href="chapter-12.html#s2">Listeners</a></li>
238
+ <li><a href="chapter-13.html#s2">Listeners</a></li>
215
239
 
216
- <li><a href="chapter-12.html#s3">The Registry as an Event Producer</a></li>
240
+ <li><a href="chapter-13.html#s3">The Registry as an Event Producer</a></li>
217
241
 
218
242
  </ol>
219
243
  </li>
@@ -291,101 +315,150 @@
291
315
 
292
316
  <div id="content">
293
317
 
294
- <h1>10. Service Factories</h1>
318
+ <h1>10. Logging</h1>
295
319
 
296
320
 
297
321
 
298
322
  <div class="section">
299
- <p>Sometimes it requires a little more effort (or a lot more effort) to instantiate and initialize a service than simply calling <code>#new</code> on it&#8217;s associated class. In such cases, you need to rely on a <em>service factory</em> to instantiate the service.</p>
300
-
301
- <p>A service factory is just a regular service as far as Copland is concerned. It is declared in the usual way. However, a service that will be used as a service factory must implement at least one method: <code>#create_instance</code>. This method should accept two parameters, the <em>service point</em> to instantiate, and the <em>parameters</em> associated with this instantiation. (The <code>parameters</code> parameter will always be a hash.)</p>
302
-
303
- <p>Here is an example that implements a trivial service factory:</p>
304
-
305
- <pre>
306
- class ExampleServiceFactory
307
-
308
- def create_instance( point, parms )
309
- return { :point =&gt; point,
310
- :parms =&gt; parms }
311
- end
312
-
313
- end
314
- </pre>
323
+ <p>Copland has an integrated logging system based on a slight variation of Ruby&#8217;s own &#8220;logger&#8221; library. In addition to the features of that library, Copland&#8217;s logging system provides:</p>
315
324
 
316
- <p>This service would be introduced into Copland via the following package descriptor:</p>
325
+ <ul>
326
+ <li>A logger factory for creating shared logger instances</li>
327
+ <li><span class="caps">YAML</span>-based configuration of the factory</li>
328
+ <li>Customizable message formatting</li>
329
+ </ul>
330
+ </div>
317
331
 
318
- <pre>
319
- ---
320
- id: example
321
332
 
322
- service-points:
323
333
 
324
- ExampleServiceFactory:
325
- implementor: some/file/ExampleServiceFactory
326
- </pre>
334
+ <h2>
335
+ <a name="s1"></a>
336
+ 10.1. Log Factory
337
+ </h2>
327
338
 
328
- <p>And it would be employed like this:</p>
339
+
329
340
 
330
- <pre>
331
- ---
332
- id: demo
341
+ <div class="section">
342
+ <p>Each registry instance has its own instance of <code>Copland::LogFactory</code>. This provides a way to map a name to a logger instance, and allows each service point (for example) to have its own logger instance. This allows various logger settings to be tweaked <em>per service-point</em>.</p>
333
343
 
334
- service-points:
344
+ <p>For example, if I want debugging information to be logged for one service point, but not for another, I can specify the logging priority level for the one service point to include debug messages, and have such messages excluded for the other service point.</p>
335
345
 
336
- ExampleService:
337
- implementor:
338
- factory: example.ExampleServiceFactory
339
- </pre>
346
+ <p>The log factory allows default values to be set for the priority level, date format string, and message format string, which will be applied to any logger that does not have a specific value set for those values. The log factory also allows configuration of the IO device to log to, the filename to log to, and various other (Logger-specific) data items (see the <span class="caps">API</span> documentation for <code>Copland::LogFactory</code> for more information).</p>
340
347
 
341
- <p>This service factory, when used to instantiate a service, would always return a new Hash object consisting of the service point and its parameters. Thus, the <code>ExampleService</code> service point would, when instantiated, always consist of a hash containing its own service point, and any parameters that were given when it was instantiated (none, in this case). Not a very useful service factory, but it demonstrates what it ought to do.<br />
348
+ <p>If you ever need to access the current log factory instance, you can get it from the registry as the <code>copland.LogFactory</code> service.<br />
342
349
  </p>
343
350
  </div>
344
351
 
345
352
 
346
353
 
347
354
  <h2>
348
- <a name="s1"></a>
349
- 10.1. Schemas
355
+ <a name="s2"></a>
356
+ 10.2. Configuration
350
357
  </h2>
351
358
 
352
359
 
353
360
 
354
361
  <div class="section">
355
- <p>As mentioned in the chapter on service points, a service point may be associated with a <em>schema</em>. More will be said on schemas (and specifically, on their formats) in the next chapter, but suffice it to say here that the schema allows a service point to specify what parameters it accepts when invoked as a factory service.<br />
356
- </p>
357
- </div>
362
+ <p>There are three ways to configure loggers. The first is programmatically, manually tweaking each log instance directly. The second is to pass configuration options to the registry itself when it is built, and the third is to use a configuration file.</p>
358
363
 
364
+ <h3>Programmatic Configuration</h3>
359
365
 
366
+ <p>To programmatically configure a logger, just obtain a handle to a logger instance and then tweak it via the accessor methods of the logger:</p>
360
367
 
361
- <h2>
362
- <a name="s2"></a>
363
- 10.2. How do they work?
364
- </h2>
368
+ <pre>
369
+ factory = registry.service( "copland.LogFactory" )
370
+ log = factory.get( "some.log.name" )
365
371
 
366
-
372
+ # Standard Logger attributes
373
+ log.level = Logger::INFO
374
+ log.progname = "different.log.name"
375
+ log.datetime_format = "%Y%m%d"
367
376
 
368
- <div class="section">
369
- <p>When you specify a factory service as the implementor of another service, Copland automatically marks that service point as needing a <em>complex instantiator</em>. Thus, when it comes time to instantiate the service point, the parameters are collected, and if the factory has a schema, the parameters are validated against that schema. Then, the parameters are preprocessed (to translate values to their appropriate and expected types), and the factory&#8217;s <code>#create_instance</code> method is called. The result of that call is then treated as the new service.</p>
377
+ # Extended Logger attributes (provided by Copland)
378
+ log.message_format = "[%-5p] %d %c: %m"
379
+ </pre>
370
380
 
371
- <p>Contrast this with the <em>simple instantiator</em>. When the simple instantiator is used, all it does (more or less) is invoke <code>#new</code> on the named class and return the result. The existence of factory services allows for much more complex (and powerful) behavior.<br />
372
- </p>
373
- </div>
381
+ <p><em>Note:</em> programmatically changing the logger <code>progname</code> does not change the name by which the logger is known to the factory!</p>
374
382
 
383
+ <p>In general, programmatic tweaking of the logger attributes as demonstrated above is not going to be the best way to do it. First of all, it is tedious, and bug prone. Secondly, it is difficult to know where the best place to do such work is, since you need to set those attributes before any other service tries to access that log.</p>
375
384
 
385
+ <h3>Configuration via <code>Registry.build</code></h3>
376
386
 
377
- <h2>
378
- <a name="s3"></a>
379
- 10.3. BuilderFactory
380
- </h2>
387
+ <p>The second approach is to pass logger configuration options to <code>Registry.build</code>, when you create a registry instance:</p>
381
388
 
382
-
389
+ <pre>
390
+ registry = Copland::Registry.build(
391
+ :log_device =&gt; STDOUT,
392
+ :log_default_level =&gt; Logger::INFO,
393
+ ...
394
+ )
395
+ </pre>
383
396
 
384
- <div class="section">
385
- <p>Copland comes with one predefined factory service: <code>copland.BuilderFactory</code>. With this factory you can implement most of the more common types of services. (There are always special cases, though&#8212;the <code>copland.lib</code>, <code>copland.remote</code> and <code>copland.webrick</code> libraries all define additional factory services for specialized uses.)</p>
397
+ <p>This gives you much greater flexibility than the previous approach, since it allows you to specify a greater array of options. The allowed options are:</p>
398
+
399
+ <table class="list">
400
+ <tr>
401
+ <td style="vertical-align:top;"><code>:log_config_file</code></td>
402
+ <td>This is the name of the configuration file to use to configure the log factory. It defaults to &#8220;logger.yml&#8221;. (See the next section for more information.)</td>
403
+ </tr>
404
+ <tr>
405
+ <td style="vertical-align:top;"><code>:log_device</code></td>
406
+ <td>This is the <span class="caps">IO </span>(or pseudo-IO) object to write log messages to. If this option is specified, <code>:log_filename</code> must not be specified (and vice versa). This allows you to log messages to arbitrary IO streams.</td>
407
+ </tr>
408
+ <tr>
409
+ <td style="vertical-align:top;"><code>:log_filename</code></td>
410
+ <td>This is the name of the file to write log messages to. If this option is specified, <code>:log_device</code> must not be specified (and vice versa). This defaults to &#8221;./copland.log&#8221;.</td>
411
+ </tr>
412
+ <tr>
413
+ <td style="vertical-align:top;"><code>:log_roll_age</code></td>
414
+ <td>This specifies the number of days before the log should be rolled. (This option is only useful when used with <code>:log_filename</code>.)</td>
415
+ </tr>
416
+ <tr>
417
+ <td style="vertical-align:top;"><code>:log_roll_frequency</code></td>
418
+ <td>This should be either <code>nil</code>, &#8220;daily&#8221;, &#8220;weekly&#8221;, or &#8220;monthly&#8221;, and specifies how frequently the log should be rolled. This option cannot be used with <code>:log_roll_age</code>.</td>
419
+ </tr>
420
+ <tr>
421
+ <td style="vertical-align:top;"><code>:log_roll_size</code></td>
422
+ <td>This specifies the maximum size of a log file. Once the log file gets larger than this value, the log will be rolled. (This option is only useful when used with <code>:log_filename</code>.)</td>
423
+ </tr>
424
+ <tr>
425
+ <td style="vertical-align:top;"><code>:log_default_date_format</code></td>
426
+ <td>This is the default date format string to use for the loggers that are created. It may be any string that is understood by <code>Time#strftime</code>. If <code>nil</code> (the default), then the default Logger date format string will be used.</td>
427
+ </tr>
428
+ <tr>
429
+ <td style="vertical-align:top;"><code>:log_default_message_format</code></td>
430
+ <td>This is the default message format string to use for the loggers that are created. It is a printf-styled string with special format specifiers&#8212;see the <span class="caps">API</span> documentation for <code>Copland::Logger#message_format=</code> for the available specifiers.</td>
431
+ </tr>
432
+ <tr>
433
+ <td style="vertical-align:top;"><code>:log_default_level</code></td>
434
+ <td>This is the default level for each logger. Messages that are logged below this level (also &#8220;priority&#8221; or &#8220;severity&#8221;) will not be logged. By default, all messages are logged.</td>
435
+ </tr>
436
+ <tr>
437
+ <td style="vertical-align:top;"><code>:log_levels</code></td>
438
+ <td>This is a hash. Each key should be a string containing a regular expression. For every logger whose name matches one of these patterns, the corresponding value will be used to define specific values for that logger. The value for each key must either be a string (in which case it is the name of the severity level to use), or a hash (containing any of the keys &#8220;level&#8221;, &#8220;date-format&#8221;, or &#8220;message-format&#8221;).</td>
439
+ </tr>
440
+ </table>
441
+
442
+ <h3>Configuration via <span class="caps">YAML</span></h3>
443
+
444
+ <p>The third option (and the most flexible) is to use a <span class="caps">YAML</span> configuration file to define the parameters of the log factory and its loggers. By default, this file is called &#8220;logger.yml&#8221;, but you can specify a different logger configuration file via the <code>:log_config_file</code> option to <code>Copland::Registry.build</code>. By default, the configuration file must reside in current working directory, but you can specify an explicit path in the string you give to <code>:log_config_file</code>.</p>
445
+
446
+ <p>The file has options that correspond to the configuration parameters described above:</p>
386
447
 
387
- <p>The BuilderFactory allows you to not only instantiate a class, but to specify constructor parameters and set properties on the new object. It also allows you to specify methods that should be invoked in order to initialize a service. It is by this means that the &#8220;dependency injection&#8221; aspect of Copland comes into play.<br />
388
- </p>
448
+ <pre>
449
+ ---
450
+ filename: log/filename.log
451
+ roll-age: 5
452
+ default-date-format: %Y%m%d%H%M%S
453
+ default-message-format: %p %d %t %C: %m (%l)
454
+ default-level: DEBUG
455
+ levels:
456
+ copland.*: WARN
457
+ project.*:
458
+ level: INFO
459
+ date-format: %Y%m%d::%H%M%S
460
+ message-format: %p %d: %m
461
+ </pre>
389
462
  </div>
390
463
 
391
464