olag 0.1.10

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/ChangeLog ADDED
@@ -0,0 +1,24 @@
1
+ 2011-07-24 Oren Ben-Kiki <github-oren@ben-kiki.org>
2
+
3
+ * Allow in_path error blocks to return a value.
4
+
5
+ 2011-04-22 Oren Ben-Kiki <github-oren@ben-kiki.org>
6
+
7
+ * Fix homepage specification.
8
+ * Fix version handling and other issues.
9
+
10
+ 2011-04-19 Oren Ben-Kiki <github-oren@ben-kiki.org>
11
+
12
+ * Refactor together with Codnar-0-1-62.
13
+
14
+ 2011-04-17 Oren Ben-Kiki <github-oren@ben-kiki.org>
15
+
16
+ * Fix gem installation problem.
17
+ * Exclude tests from RDoc documentation.
18
+ * Fix commit process.
19
+ * Refactor out of Codnar.
20
+
21
+ 2011-04-14 Oren Ben-Kiki <github-oren@ben-kiki.org>
22
+
23
+ * Initial empty repository
24
+
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010-2011 Oren Ben-Kiki
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,17 @@
1
+ = Olag
2
+
3
+ Olag - Oren's Library/Application Gem framework
4
+
5
+ == TL;DR
6
+
7
+ === Description
8
+
9
+ Olag is Oren's set of utilities for creating a well-behaved gem. This is very
10
+ opinionated code; it eliminates a lot of the boilerplate, at the cost of making
11
+ many decisions which may not be suitable for everyone (directory structure,
12
+ code verification, Codnar for documentation, etc.).
13
+
14
+ === Installation
15
+
16
+ A simple <tt>gem install olag</tt> should do the trick, assuming you have
17
+ Ruby gems set up.
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + "/lib")
2
+
3
+ require "olag/rake"
4
+
5
+ # {{{ Gem specification
6
+
7
+ spec = Gem::Specification.new do |spec|
8
+ spec.name = "olag"
9
+ spec.version = Olag::VERSION
10
+ spec.author = "Oren Ben-Kiki"
11
+ spec.email = "rubygems-oren@ben-kiki.org"
12
+ spec.homepage = "https://rubygems.org/gems/olag"
13
+ spec.summary = "Olag - Oren's Library/Application Gem framework"
14
+ spec.description = (<<-EOF).gsub(/^\s+/, "").chomp.gsub("\n", " ")
15
+ Olag is Oren's set of utilities for creating a well-behaved gem. This is
16
+ very opinionated software; it eliminates a lot of the boilerplate, at the
17
+ cost of making many decisions which may not be suitable for everyone
18
+ (directory structure, code verification, codnar for documentation, etc.).
19
+ EOF
20
+ end
21
+
22
+ # }}}
23
+
24
+ Olag::Rake.new(spec)
data/codnar.html ADDED
@@ -0,0 +1,4379 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml">
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
5
+ <title>Olag - Oren's Library/Application Gem framework.</title>
6
+ <style type="text/css">
7
+ /*
8
+ * Copyright (c) 2010, Yahoo! Inc. All rights reserved.
9
+ * Code licensed under the BSD License:
10
+ * http://developer.yahoo.com/yui/license.html
11
+ * version: 2.8.2r1
12
+ */
13
+ /*
14
+ * YUI Reset
15
+ * @module reset
16
+ * @namespace
17
+ * @requires
18
+ */
19
+ html {
20
+ color: #000;
21
+ background: #FFF;
22
+ }
23
+
24
+ body,
25
+ div,
26
+ dl,
27
+ dt,
28
+ dd,
29
+ ul,
30
+ ol,
31
+ li,
32
+ h1,
33
+ h2,
34
+ h3,
35
+ h4,
36
+ h5,
37
+ h6,
38
+ pre,
39
+ code,
40
+ form,
41
+ fieldset,
42
+ legend,
43
+ input,
44
+ button,
45
+ textarea,
46
+ p,
47
+ blockquote,
48
+ th,
49
+ td {
50
+ margin: 0;
51
+ padding: 0;
52
+ }
53
+
54
+ table {
55
+ border-collapse: collapse;
56
+ border-spacing: 0;
57
+ }
58
+
59
+ fieldset,
60
+ img {
61
+ border: 0;
62
+ }
63
+
64
+ address,
65
+ caption,
66
+ cite,
67
+ code,
68
+ dfn,
69
+ em,
70
+ strong,
71
+ th,
72
+ var,
73
+ optgroup {
74
+ font-style: inherit;
75
+ font-weight: inherit;
76
+ }
77
+
78
+ del,
79
+ ins {
80
+ text-decoration: none;
81
+ }
82
+
83
+ li {
84
+ list-style: none;
85
+ }
86
+
87
+ caption,
88
+ th {
89
+ text-align: left;
90
+ }
91
+
92
+ h1,
93
+ h2,
94
+ h3,
95
+ h4,
96
+ h5,
97
+ h6 {
98
+ font-size: 100%;
99
+ font-weight: normal;
100
+ }
101
+
102
+ q:before,
103
+ q:after {
104
+ content: '';
105
+ }
106
+
107
+ abbr,
108
+ acronym {
109
+ border: 0;
110
+ font-variant: normal;
111
+ }
112
+
113
+ sup {
114
+ vertical-align: baseline;
115
+ }
116
+
117
+ sub {
118
+ vertical-align: baseline;
119
+ }
120
+
121
+ /*because legend doesn't inherit in IE */
122
+ legend {
123
+ color: #000;
124
+ }
125
+
126
+ input,
127
+ button,
128
+ textarea,
129
+ select,
130
+ optgroup,
131
+ option {
132
+ font-family: inherit;
133
+ font-size: inherit;
134
+ font-style: inherit;
135
+ font-weight: inherit;
136
+ }
137
+
138
+ /*@purpose To enable resizing for IE */
139
+ /*@branch For IE6-Win, IE7-Win */
140
+ input,
141
+ button,
142
+ textarea,
143
+ select {
144
+ *font-size: 100%;
145
+ }
146
+
147
+
148
+
149
+ /*
150
+ * Copyright (c) 2010, Yahoo! Inc. All rights reserved.
151
+ * Code licensed under the BSD License:
152
+ * http://developer.yahoo.com/yui/license.html
153
+ * version: 2.8.2r1
154
+ */
155
+
156
+ /*
157
+ * YUI Base
158
+ * @module base
159
+ * @namespace yui-
160
+ * @requires reset, fonts
161
+ */
162
+
163
+ body {
164
+ /* For breathing room between content and viewport. */
165
+ margin:10px;
166
+ }
167
+
168
+ h1 {
169
+ /* 18px via YUI Fonts CSS foundation. */
170
+ font-size: 138.5%;
171
+ }
172
+
173
+ h2 {
174
+ /* 16px via YUI Fonts CSS foundation. */
175
+ font-size: 123.1%;
176
+ }
177
+
178
+ h3 {
179
+ /* 14px via YUI Fonts CSS foundation. */
180
+ font-size: 108%;
181
+ }
182
+
183
+ h1,h2,h3 {
184
+ /* Top & bottom margin based on font size. */
185
+ margin: 1em 0;
186
+ }
187
+
188
+ h1,h2,h3,h4,h5,h6,strong,dt {
189
+ /* Bringing boldness back to headers and the strong element. */
190
+ font-weight: bold;
191
+ }
192
+ optgroup {
193
+ font-weight:normal;
194
+ }
195
+
196
+ abbr,acronym {
197
+ /* Indicating to users that more info is available. */
198
+ border-bottom: 1px dotted #000;
199
+ cursor: help;
200
+ }
201
+
202
+ em {
203
+ /* Bringing italics back to the em element. */
204
+ font-style: italic;
205
+ }
206
+
207
+ del {
208
+ /* Striking deleted phrases. */
209
+ text-decoration: line-through;
210
+ }
211
+
212
+ blockquote,ul,ol,dl {
213
+ /* Giving blockquotes and lists room to breath. */
214
+ margin: 1em;
215
+ }
216
+
217
+ ol,ul,dl {
218
+ /* Bringing lists on to the page with breathing room. */
219
+ margin-left: 2em;
220
+ }
221
+
222
+ ol li {
223
+ /* Giving OL's LIs generated numbers. */
224
+ list-style: decimal outside;
225
+ }
226
+
227
+ ul li {
228
+ /* Giving UL's LIs generated disc markers. */
229
+ list-style: disc outside;
230
+ }
231
+
232
+ dl dd {
233
+ /* Giving UL's LIs generated numbers. */
234
+ margin-left: 1em;
235
+ }
236
+
237
+ th,td {
238
+ /* Borders and padding to make the table readable. */
239
+ border: 1px solid #000;
240
+ padding: .5em;
241
+ }
242
+
243
+ th {
244
+ /* Distinguishing table headers from data cells. */
245
+ font-weight: bold;
246
+ text-align: center;
247
+ }
248
+
249
+ caption {
250
+ /* Coordinated margin to match cell's padding. */
251
+ margin-bottom: .5em;
252
+ /* Centered so it doesn't blend in to other content. */
253
+ text-align: center;
254
+ }
255
+
256
+ sup {
257
+ /* to preserve line-height and selector appearance */
258
+ vertical-align: super;
259
+ }
260
+
261
+ sub {
262
+ /* to preserve line-height and selector appearance */
263
+ vertical-align: sub;
264
+ }
265
+
266
+ p,
267
+ fieldset,
268
+ table,
269
+ pre {
270
+ /* So things don't run into each other. */
271
+ margin-bottom: 1em;
272
+ }
273
+ /* Opera requires 1px of passing to render with contemporary native chrome */
274
+ button,
275
+ input[type="checkbox"],
276
+ input[type="radio"],
277
+ input[type="reset"],
278
+ input[type="submit"] {
279
+ padding:1px;
280
+ }
281
+ /* Margin & Padding */
282
+
283
+ div.chunk.name,
284
+ div.chunk.html,
285
+ div.chunk.containers,
286
+ div.chunk table,
287
+ div.chunk td,
288
+ div.chunk pre {
289
+ margin: 0;
290
+ padding: 0;
291
+ }
292
+ div.chunk *:last-child {
293
+ margin-bottom: 0;
294
+ }
295
+ h4, h5, h6,
296
+ div.chunk,
297
+ div.comment pre {
298
+ margin: 1em 0;
299
+ }
300
+ pre,
301
+ div.comment,
302
+ div.chunk.html {
303
+ padding: 0.33em;
304
+ }
305
+
306
+ span.control.chunk {
307
+ padding-left: 0.25em;
308
+ padding-right: 0.25em;
309
+ }
310
+
311
+ /* Table of content */
312
+
313
+ div#contents ul {
314
+ margin-top: 0;
315
+ margin-bottom: 0;
316
+ padding: 0;
317
+ }
318
+
319
+ div#contents li {
320
+ list-style-type: none;
321
+ }
322
+
323
+ /* Lists */
324
+
325
+ ul.chunk.containers {
326
+ padding: 0;
327
+ margin: 0;
328
+ display: inline;
329
+ }
330
+ ul.chunk.containers li {
331
+ display: inline;
332
+ list-style-type: none;
333
+ }
334
+
335
+ /* Borders */
336
+
337
+ pre,
338
+ span.control.chunk,
339
+ div.chunk.html {
340
+ border: 1px solid #000;
341
+ }
342
+
343
+ table.layout td.indentation,
344
+ div.chunk pre {
345
+ border: none;
346
+ }
347
+
348
+ /* Colors */
349
+
350
+ span.control.chunk,
351
+ table.layout td.html {
352
+ background-color: Beige;
353
+ }
354
+
355
+ /* Colors for GVim classes */
356
+
357
+ span.Constant { color: Crimson; }
358
+ span.Identifier { color: Teal; }
359
+ span.PreProc { color: Indigo; }
360
+ span.Special { color: Navy; }
361
+ span.Statement { color: Maroon; }
362
+ span.Type { color: Green; }
363
+ span.Comment { color: Purple; }
364
+
365
+ /* Fonts */
366
+
367
+ body {
368
+ font-family: Sans-Serif;
369
+ }
370
+ pre {
371
+ font-family: Consolas, Inconsolata, Monaco, "Courier New", Monospace;
372
+ }
373
+ div.chunk.name {
374
+ font-weight: bold;
375
+ }
376
+ /* global styles */
377
+ .sunlight-container {
378
+ clear: both;
379
+ border: 1px solid #969696 !important;
380
+ position: relative;
381
+ background-color: #FFFFFF !important;
382
+ }
383
+ .sunlight-highlighted, .sunlight-container {
384
+ color: #000000 !important;
385
+ /*! Commented out for for Codnar - these are handled by the Codnar CSS.
386
+ font-family: Consolas, Inconsolata, Monaco, "Courier New", Monospace !important;
387
+ font-size: 12px !important;
388
+ line-height: 15px !important;
389
+ !*/
390
+ margin: 0 !important;
391
+ }
392
+ .sunlight-container > .sunlight-highlighted {
393
+ white-space: pre;
394
+ overflow-x: auto;
395
+ }
396
+ span.sunlight-highlighted {
397
+ z-index: 1;
398
+ position: relative;
399
+ }
400
+ span.sunlight-highlighted * {
401
+ background: transparent;
402
+ }
403
+ .sunlight-line-number-margin {
404
+ float: left !important;
405
+ margin-right: 5px !important;
406
+ margin-top: 0 !important;
407
+ padding: 0 !important;
408
+ padding-right: 4px !important;
409
+ padding-left: 4px !important;
410
+ border-right: 1px solid #CCCCCC !important;
411
+ background-color: #EEEEEE !important;
412
+ color: #848484 !important;
413
+ text-align: right !important;
414
+ position: relative;
415
+ z-index: 3;
416
+ }
417
+ .sunlight-highlighted a, .sunlight-line-number-margin a {
418
+ border: none !important;
419
+ text-decoration: none !important;
420
+ font-weight: normal !important;
421
+ font-style: normal !important;
422
+ padding: 0 !important;
423
+ }
424
+ .sunlight-line-number-margin a {
425
+ color: inherit !important;
426
+ }
427
+ .sunlight-line-highlight-overlay {
428
+ position: absolute;
429
+ top: 0;
430
+ left: 0;
431
+ width: 100%;
432
+ z-index: 0;
433
+ }
434
+ .sunlight-line-highlight-overlay div {
435
+ height: 15px;
436
+ width: 100%;
437
+ }
438
+ .sunlight-line-highlight-overlay .sunlight-line-highlight-active {
439
+ background-color: #E7FCFA;
440
+ }
441
+
442
+
443
+
444
+
445
+ .sunlight-string,
446
+ .sunlight-heredoc,
447
+ .sunlight-heredocDeclaration,
448
+ .sunlight-nowdoc,
449
+ .sunlight-longString,
450
+ .sunlight-rawString,
451
+ .sunlight-binaryString,
452
+ .sunlight-rawLongString,
453
+ .sunlight-binaryLongString {
454
+ color: #990000 !important;
455
+ }
456
+
457
+ .sunlight-ident,
458
+ .sunlight-operator,
459
+ .sunlight-punctuation,
460
+ .sunlight-delimiter {
461
+ color: #000000 !important;
462
+ }
463
+
464
+ .sunlight-comment,
465
+ .sunlight-xmlDocCommentContent {
466
+ color: #009900 !important;
467
+ }
468
+ .sunlight-number {
469
+ color: #CC6600 !important;
470
+ }
471
+
472
+ .sunlight-named-ident,
473
+ .sunlight-constant,
474
+ .sunlight-javascript .sunlight-globalVariable,
475
+ .sunlight-globalObject,
476
+ .sunlight-python .sunlight-attribute {
477
+ color: #2B91AF !important;
478
+ }
479
+ .sunlight-keyword,
480
+ .sunlight-languageConstruct,
481
+ .sunlight-css
482
+ .sunlight-element,
483
+ .sunlight-bash .sunlight-command,
484
+ .sunlight-ruby .sunlight-specialOperator {
485
+ color: #0000FF !important;
486
+ }
487
+ .sunlight-shortOpenTag,
488
+ .sunlight-openTag,
489
+ .sunlight-closeTag,
490
+ .sunlight-xmlOpenTag,
491
+ .sunlight-xmlCloseTag {
492
+ background-color: #FFFF99 !important;
493
+ color: #000000 !important;
494
+ }
495
+ .sunlight-content {
496
+ color: #000000 !important;
497
+ }
498
+ .sunlight-function,
499
+ .sunlight-globalFunction,
500
+ .sunlight-ruby .sunlight-specialFunction {
501
+ color: #B069AF !important;
502
+ }
503
+
504
+ .sunlight-php .sunlight-variable,
505
+ .sunlight-ruby .sunlight-globalVariable,
506
+ .sunlight-ruby .sunlight-instanceVariable {
507
+ color: #8F41AA !important;
508
+ }
509
+ .sunlight-regexLiteral {
510
+ color: #FF00B2 !important;
511
+ }
512
+
513
+
514
+
515
+ /* html/xml */
516
+ .sunlight-html .sunlight-string,
517
+ .sunlight-xml .sunlight-string {
518
+ color: #990099 !important;
519
+ }
520
+ .sunlight-cdata {
521
+ color: #CC6600 !important;
522
+ }
523
+ .sunlight-html .sunlight-ident,
524
+ .sunlight-html .sunlight-operator,
525
+ .sunlight-xml .sunlight-ident,
526
+ .sunlight-xml .sunlight-operator {
527
+ color: #0000FF !important;
528
+ }
529
+ .sunlight-html .sunlight-named-ident, .sunlight-xml .sunlight-named-ident {
530
+ color: #FF0000 !important;
531
+ }
532
+ .sunlight-html .sunlight-entity,
533
+ .sunlight-xml .sunlight-entity {
534
+ background-color: #EEEEEE !important;
535
+ color: #000000 !important;
536
+ border: 1px solid #000000 !important;
537
+ }
538
+
539
+ /* html */
540
+ .sunlight-html .sunlight-doctype {
541
+ color: #2B91AF !important;
542
+ }
543
+
544
+ /* c# */
545
+ .sunlight-csharp .sunlight-pragma {
546
+ color: #999999 !important;
547
+ font-style: italic !important;
548
+ }
549
+ .sunlight-csharp .sunlight-xmlDocCommentMeta,
550
+ .sunlight-java .sunlight-annotation,
551
+ .sunlight-ruby .sunlight-docComment {
552
+ color: #808080 !important;
553
+ }
554
+
555
+ /* javascript */
556
+ .sunlight-javascript .sunlight-reservedWord {
557
+ font-style: italic !important;
558
+ }
559
+
560
+ /* sql */
561
+ .sunlight-quotedIdent {
562
+ color: #999900 !important;
563
+ }
564
+
565
+ /* css */
566
+ .sunlight-css .sunlight-microsoftFilterPrefix {
567
+ color: #FF00FF !important;
568
+ }
569
+ .sunlight-css .sunlight-rule {
570
+ color: #0099FF !important;
571
+ }
572
+ .sunlight-css .sunlight-keyword {
573
+ color: #4E65B8 !important;
574
+ }
575
+ .sunlight-css .sunlight-class {
576
+ color: #FF0000 !important;
577
+ }
578
+ .sunlight-css .sunlight-id {
579
+ color: #8A8E13 !important;
580
+ }
581
+ .sunlight-css .sunlight-pseudoClass,
582
+ .sunlight-css .sunlight-pseudoElement {
583
+ color: #368B87 !important;
584
+ }
585
+
586
+ /* bash */
587
+ .sunlight-bash .sunlight-hashBang {
588
+ color: #3D97F5 !important;
589
+ }
590
+ .sunlight-bash .sunlight-specialVariable {
591
+ font-style: italic !important;
592
+ font-weight: bold !important;
593
+ }
594
+ .sunlight-bash .sunlight-verbatimCommand {
595
+ color: #999900 !important;
596
+ }
597
+ .sunlight-bash .sunlight-variable,
598
+ .sunlight-bash .sunlight-specialVariable {
599
+ color: #FF0000 !important;
600
+ }
601
+
602
+ /* python */
603
+ .sunlight-python .sunlight-specialMethod {
604
+ font-weight: bold !important;
605
+ color: #A07DD3;
606
+ }
607
+
608
+ /* ruby */
609
+ .sunlight-ruby .sunlight-subshellCommand {
610
+ color: #999900 !important;
611
+ }
612
+ </style>
613
+ </head>
614
+ <body>
615
+ <div id="contents"></div>
616
+ <div class='rdoc doc markup'>
617
+ <h1>Olag</h1>
618
+ <p>
619
+ Olag - Oren’s Library/Application Gem framework
620
+ </p>
621
+ <h2>TL;DR</h2>
622
+
623
+ <h3>Description</h3>
624
+ <p>
625
+ Olag is Oren’s set of utilities for creating a well-behaved gem. This is
626
+ very opinionated code; it eliminates a lot of the boilerplate, at the cost
627
+ of making many decisions which may not be suitable for everyone (directory
628
+ structure, code verification, Codnar for documentation, etc.).
629
+ </p>
630
+ <h3>Installation</h3>
631
+ <p>
632
+ A simple <tt>gem install olag</tt> should do the trick, assuming you have
633
+ Ruby gems set up.
634
+ </p>
635
+ </div>
636
+ <div class='markdown doc markup'>
637
+ <h2>Rakefile</h2>
638
+ <p>
639
+ Olag's Rakefile is a good example of how to use Olag's classes to create a
640
+ full-featured gem Rakefile:
641
+ </p>
642
+ <p>
643
+ <div class="named_with_containers chunk">
644
+ <div class="chunk name">
645
+ <a name="rakefile">
646
+ <span>Rakefile</span>
647
+ </a>
648
+ </div>
649
+ <div class="chunk html">
650
+ <pre class='ruby code syntax'>
651
+ <span class="Identifier">$LOAD_PATH</span>.unshift(<span class="Type">File</span>.dirname(<span class="Constant">__FILE__</span>) + <span class="Special">&quot;</span><span class="Constant">/lib</span><span class="Special">&quot;</span>)
652
+
653
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/rake</span><span class="Special">&quot;</span>
654
+
655
+ </pre>
656
+ <pre class='nested chunk'>
657
+ <a class='nested chunk' href='#gem-specification'>Gem specification</a>
658
+ </pre>
659
+ <pre class='ruby code syntax'>
660
+
661
+ <span class="Type">Olag</span>::<span class="Type">Rake</span>.new(spec)
662
+ </pre>
663
+ </div>
664
+ </div>
665
+ </p>
666
+ <p>
667
+ The overall Rakefile structure is as follows:
668
+ </p>
669
+ <ul>
670
+ <li>
671
+ <p>
672
+ A first line sets up the Ruby module load path to begin with
673
+ the current gem's <code>lib</code> directory. This standard idiom ensures we have access
674
+ to the current gem.
675
+ </p>
676
+ </li>
677
+ <li>
678
+ <p>
679
+ The next line imports Olag's <code>rake</code> support module.
680
+ </p>
681
+ </li>
682
+ <li>
683
+ <p>
684
+ This is followed by setting up the gem specification, which is enhanced by
685
+ Olag using monkey-patching.
686
+ </p>
687
+ </li>
688
+ <li>
689
+ <p>
690
+ Finally, Olag::Rake sets up the following tasks (as reported by <code>rake -T</code>):
691
+ </p>
692
+ <pre>
693
+ <code>rake all # Version, verify, document, package
694
+ rake analyze # Analyze source code
695
+ rake changelog # Update ChangeLog from Git
696
+ rake clean # Remove any temporary products.
697
+ rake clean_codnar # Clean all split chunks
698
+ rake clobber # Remove any generated file.
699
+ rake clobber_codnar # Remove woven HTML documentation
700
+ rake clobber_coverage # Remove rcov products for coverage
701
+ rake clobber_package # Remove package products
702
+ rake clobber_rdoc # Remove rdoc products
703
+ rake codnar # Build the code narrative HTML
704
+ rake codnar_split # Split all files into chunks
705
+ rake codnar_weave # Weave chunks into HTML
706
+ rake commit # Git commit process
707
+ rake coverage # Test code covarage with RCov
708
+ rake doc # Generate all documentation
709
+ rake first_commit # Perform the 1st (main) Git commit
710
+ rake flay # Check for duplicated code with Flay
711
+ rake gem # Build the gem file olag-&lt;version&gt;.gem
712
+ rake package # Build all the packages
713
+ rake rdoc # Build the rdoc HTML Files
714
+ rake reek # Check for smelly code with Reek
715
+ rake repackage # Force a rebuild of the package files
716
+ rake rerdoc # Force a rebuild of the RDOC files
717
+ rake roodi # Check for smelly code with Roodi
718
+ rake saikuro # Check for complex code with Saikuro
719
+ rake second_commit # Perform the 2nd (amend) Git commit
720
+ rake test # Run tests for test
721
+ rake verify # Test, coverage, analyze code
722
+ rake version # Update version file from Git
723
+ </code>
724
+ </pre>
725
+ </li>
726
+ </ul>
727
+
728
+
729
+ <h3>Gem Specification</h3>
730
+ <p>
731
+ The gem specification is provided as usual:
732
+ </p>
733
+ <p>
734
+ <div class="named_with_containers chunk">
735
+ <div class="chunk name">
736
+ <a name="gem-specification">
737
+ <span>Gem specification</span>
738
+ </a>
739
+ </div>
740
+ <div class="chunk html">
741
+ <pre class='ruby code syntax'>
742
+
743
+ spec = <span class="Type">Gem</span>::<span class="Type">Specification</span>.new <span class="Statement">do</span> |<span class="Identifier">spec</span>|
744
+ spec.name = <span class="Special">&quot;</span><span class="Constant">olag</span><span class="Special">&quot;</span>
745
+ spec.version = <span class="Type">Olag</span>::<span class="Type">VERSION</span>
746
+ spec.author = <span class="Special">&quot;</span><span class="Constant">Oren Ben-Kiki</span><span class="Special">&quot;</span>
747
+ spec.email = <span class="Special">&quot;</span><span class="Constant">rubygems-oren@ben-kiki.org</span><span class="Special">&quot;</span>
748
+ spec.homepage = <span class="Special">&quot;</span><span class="Constant"><a href="https://rubygems.org/gems/olag">https://rubygems.org/gems/olag</a></span><span class="Special">&quot;</span>
749
+ spec.summary = <span class="Special">&quot;</span><span class="Constant">Olag - Oren's Library/Application Gem framework</span><span class="Special">&quot;</span>
750
+ spec.description = (&lt;&lt;-<span class="Special">EOF</span>).gsub(<span class="Special">/</span><span class="Special">^</span><span class="Special">\s</span><span class="Special">+</span><span class="Special">/</span>, <span class="Special">&quot;&quot;</span>).chomp.gsub(<span class="Special">&quot;</span><span class="Special">\n</span><span class="Special">&quot;</span>, <span class="Special">&quot;</span><span class="Constant"> </span><span class="Special">&quot;</span>)
751
+ <span class="Constant"> Olag is Oren's set of utilities for creating a well-behaved gem. This is</span>
752
+ <span class="Constant"> very opinionated software; it eliminates a lot of the boilerplate, at the</span>
753
+ <span class="Constant"> cost of making many decisions which may not be suitable for everyone</span>
754
+ <span class="Constant"> (directory structure, code verification, codnar for documentation, etc.).</span>
755
+ <span class="Constant"> </span><span class="Special">EOF</span>
756
+ <span class="Statement">end</span>
757
+
758
+ </pre>
759
+ </div>
760
+ <div class="chunk containers">
761
+ <span class="chunk containers header">Contained in:</span>
762
+ <ul class="chunk containers">
763
+ <li class="chunk container">
764
+ <a class="chunk container" href="#rakefile">Rakefile</a>
765
+ </li>
766
+ </ul>
767
+ </div>
768
+ </div>
769
+ </p>
770
+ <p>
771
+ However, the Gem::Specification class is monkey-patched to automatically
772
+ several of the specification fields, and adding some new ones:
773
+ </p>
774
+ <p>
775
+ <div class="named_with_containers chunk">
776
+ <div class="chunk name">
777
+ <a name="lib-olag-gem-specification-rb">
778
+ <span>lib/olag/gem_specification.rb</span>
779
+ </a>
780
+ </div>
781
+ <div class="chunk html">
782
+ <table class='layout'>
783
+ <tr>
784
+ <td class='indentation'>
785
+ <pre></pre>
786
+ </td>
787
+ <td class='html'>
788
+ <div class='rdoc comment markup'>
789
+ <p>
790
+ Monkey-patch within the Gem module.
791
+ </p>
792
+ </div>
793
+ </td>
794
+ </tr>
795
+ </table>
796
+ <pre class='ruby code syntax'>
797
+ <span class="PreProc">module</span> <span class="Type">Gem</span>
798
+
799
+ </pre>
800
+ <table class='layout'>
801
+ <tr>
802
+ <td class='indentation'>
803
+ <pre> </pre>
804
+ </td>
805
+ <td class='html'>
806
+ <div class='rdoc comment markup'>
807
+ <p>
808
+ Enhanced automated gem specification.
809
+ </p>
810
+ </div>
811
+ </td>
812
+ </tr>
813
+ </table>
814
+ <pre class='ruby code syntax'>
815
+ <span class="PreProc">class</span> <span class="Type">Specification</span>
816
+
817
+ </pre>
818
+ <table class='layout'>
819
+ <tr>
820
+ <td class='indentation'>
821
+ <pre> </pre>
822
+ </td>
823
+ <td class='html'>
824
+ <div class='rdoc comment markup'>
825
+ <p>
826
+ The title of the gem for documentation (by default, the capitalized name).
827
+ </p>
828
+ </div>
829
+ </td>
830
+ </tr>
831
+ </table>
832
+ <pre class='ruby code syntax'>
833
+ <span class="Statement">attr_accessor</span> <span class="Constant">:title</span>
834
+
835
+ </pre>
836
+ <table class='layout'>
837
+ <tr>
838
+ <td class='indentation'>
839
+ <pre> </pre>
840
+ </td>
841
+ <td class='html'>
842
+ <div class='rdoc comment markup'>
843
+ <p>
844
+ The name of the file containing the gem’s version (by default,
845
+ <tt>lib/<em>name</em>/version.rb</tt>).
846
+ </p>
847
+ </div>
848
+ </td>
849
+ </tr>
850
+ </table>
851
+ <pre class='ruby code syntax'>
852
+ <span class="Statement">attr_accessor</span> <span class="Constant">:version_file</span>
853
+
854
+ alias_method <span class="Constant">:original_initialize</span>, <span class="Constant">:initialize</span>
855
+
856
+ </pre>
857
+ <table class='layout'>
858
+ <tr>
859
+ <td class='indentation'>
860
+ <pre> </pre>
861
+ </td>
862
+ <td class='html'>
863
+ <div class='rdoc comment markup'>
864
+ <p>
865
+ Create the gem specification. Requires a block to set up the basic gem
866
+ information (name, version, author, email, description). In addition, the
867
+ block may override default properties (e.g. title).
868
+ </p>
869
+ </div>
870
+ </td>
871
+ </tr>
872
+ </table>
873
+ <pre class='ruby code syntax'>
874
+ <span class="PreProc">def</span> <span class="Identifier">initialize</span>(&amp;block)
875
+ original_initialize(&amp;block)
876
+ setup_default_members
877
+ add_development_dependencies
878
+ setup_file_members
879
+ setup_rdoc
880
+ <span class="PreProc">end</span>
881
+
882
+ </pre>
883
+ <table class='layout'>
884
+ <tr>
885
+ <td class='indentation'>
886
+ <pre> </pre>
887
+ </td>
888
+ <td class='html'>
889
+ <div class='rdoc comment markup'>
890
+ <p>
891
+ Set the new data members to their default values, unless they were already
892
+ set by the gem specification block.
893
+ </p>
894
+ </div>
895
+ </td>
896
+ </tr>
897
+ </table>
898
+ <pre class='ruby code syntax'>
899
+ <span class="PreProc">def</span> <span class="Identifier">setup_default_members</span>
900
+ name = <span class="Constant">self</span>.name
901
+ <span class="Identifier">@title</span> ||= name.capitalize
902
+ <span class="Identifier">@version_file</span> ||= <span class="Special">&quot;</span><span class="Constant">lib/</span><span class="Special">#{</span>name<span class="Special">}</span><span class="Constant">/version.rb</span><span class="Special">&quot;</span>
903
+ <span class="PreProc">end</span>
904
+
905
+ </pre>
906
+ <table class='layout'>
907
+ <tr>
908
+ <td class='indentation'>
909
+ <pre> </pre>
910
+ </td>
911
+ <td class='html'>
912
+ <div class='rdoc comment markup'>
913
+ <p>
914
+ Add dependencies required for developing the gem.
915
+ </p>
916
+ </div>
917
+ </td>
918
+ </tr>
919
+ </table>
920
+ <pre class='ruby code syntax'>
921
+ <span class="PreProc">def</span> <span class="Identifier">add_development_dependencies</span>
922
+ add_dependency(<span class="Special">&quot;</span><span class="Constant">olag</span><span class="Special">&quot;</span>) <span class="Statement">unless</span> <span class="Constant">self</span>.name == <span class="Special">&quot;</span><span class="Constant">olag</span><span class="Special">&quot;</span>
923
+ <span class="Special">%w(</span><span class="Constant">Saikuro codnar fakefs flay rake rcov rdoc reek roodi test-spec</span><span class="Special">)</span>.each <span class="Statement">do</span> |<span class="Identifier">gem</span>|
924
+ add_development_dependency(gem)
925
+ <span class="Statement">end</span>
926
+ <span class="PreProc">end</span>
927
+
928
+ </pre>
929
+ <table class='layout'>
930
+ <tr>
931
+ <td class='indentation'>
932
+ <pre> </pre>
933
+ </td>
934
+ <td class='html'>
935
+ <div class='rdoc comment markup'>
936
+ <p>
937
+ Initialize the standard gem specification file list members.
938
+ </p>
939
+ </div>
940
+ </td>
941
+ </tr>
942
+ </table>
943
+ <pre class='ruby code syntax'>
944
+ <span class="PreProc">def</span> <span class="Identifier">setup_file_members</span>
945
+ </pre>
946
+ <table class='layout'>
947
+ <tr>
948
+ <td class='indentation'>
949
+ <pre> </pre>
950
+ </td>
951
+ <td class='html'>
952
+ <div class='rdoc comment markup'>
953
+ <p>
954
+ These should cover all the gem’s files, except for the extra rdoc files.
955
+ </p>
956
+ </div>
957
+ </td>
958
+ </tr>
959
+ </table>
960
+ <pre class='ruby code syntax'>
961
+ setup_file_member(<span class="Constant">:files</span>, <span class="Special">&quot;</span><span class="Constant">{lib,doc}/**/*</span><span class="Special">&quot;</span>)
962
+ <span class="Constant">self</span>.files &lt;&lt; <span class="Special">&quot;</span><span class="Constant">Rakefile</span><span class="Special">&quot;</span> &lt;&lt; <span class="Special">&quot;</span><span class="Constant">codnar.html</span><span class="Special">&quot;</span>
963
+ setup_file_member(<span class="Constant">:executables</span>, <span class="Special">&quot;</span><span class="Constant">bin/*</span><span class="Special">&quot;</span>) { |<span class="Identifier">path</span>| path.sub(<span class="Special">&quot;</span><span class="Constant">bin/</span><span class="Special">&quot;</span>, <span class="Special">&quot;&quot;</span>) }
964
+ setup_file_member(<span class="Constant">:test_files</span>, <span class="Special">&quot;</span><span class="Constant">test/**/*</span><span class="Special">&quot;</span>)
965
+ end
966
+
967
+ </pre>
968
+ <table class='layout'>
969
+ <tr>
970
+ <td class='indentation'>
971
+ <pre> </pre>
972
+ </td>
973
+ <td class='html'>
974
+ <div class='rdoc comment markup'>
975
+ <p>
976
+ Initialize a standard gem specification file list member to the files
977
+ matching a pattern. If a block is given, it is used to map the file paths.
978
+ This will append to the file list member if it has already been set by the
979
+ gem specification block.
980
+ </p>
981
+ </div>
982
+ </td>
983
+ </tr>
984
+ </table>
985
+ <pre class='ruby code syntax'>
986
+ <span class="PreProc">def</span> <span class="Identifier">setup_file_member</span>(member, pattern, &amp;block)
987
+ old_value = instance_variable_get(<span class="Special">&quot;</span><span class="Constant">@</span><span class="Special">#{</span>member<span class="Special">}</span><span class="Special">&quot;</span>)
988
+ new_value = <span class="Type">FileList</span>[pattern].find_all { |<span class="Identifier">path</span>| <span class="Type">File</span>.file?(path) }
989
+ new_value.map!(&amp;block) <span class="Statement">if</span> block
990
+ instance_variable_set(<span class="Special">&quot;</span><span class="Constant">@</span><span class="Special">#{</span>member<span class="Special">}</span><span class="Special">&quot;</span>, old_value + new_value)
991
+ <span class="PreProc">end</span>
992
+
993
+ </pre>
994
+ <table class='layout'>
995
+ <tr>
996
+ <td class='indentation'>
997
+ <pre> </pre>
998
+ </td>
999
+ <td class='html'>
1000
+ <div class='rdoc comment markup'>
1001
+ <p>
1002
+ Setup RDOC options in the gem specification.
1003
+ </p>
1004
+ </div>
1005
+ </td>
1006
+ </tr>
1007
+ </table>
1008
+ <pre class='ruby code syntax'>
1009
+ <span class="PreProc">def</span> <span class="Identifier">setup_rdoc</span>
1010
+ <span class="Constant">self</span>.extra_rdoc_files = [ <span class="Special">&quot;</span><span class="Constant">README.rdoc</span><span class="Special">&quot;</span>, <span class="Special">&quot;</span><span class="Constant">LICENSE</span><span class="Special">&quot;</span>, <span class="Special">&quot;</span><span class="Constant">ChangeLog</span><span class="Special">&quot;</span> ]
1011
+ <span class="Constant">self</span>.rdoc_options &lt;&lt; <span class="Special">&quot;</span><span class="Constant">--title</span><span class="Special">&quot;</span> &lt;&lt; <span class="Special">&quot;</span><span class="Special">#{</span>title<span class="Special">}</span><span class="Constant"> </span><span class="Special">#{</span>version<span class="Special">}</span><span class="Special">&quot;</span> \
1012
+ &lt;&lt; <span class="Special">&quot;</span><span class="Constant">--main</span><span class="Special">&quot;</span> &lt;&lt; <span class="Special">&quot;</span><span class="Constant">README.rdoc</span><span class="Special">&quot;</span> \
1013
+ &lt;&lt; <span class="Special">&quot;</span><span class="Constant">--line-numbers</span><span class="Special">&quot;</span> \
1014
+ &lt;&lt; <span class="Special">&quot;</span><span class="Constant">--all</span><span class="Special">&quot;</span> \
1015
+ &lt;&lt; <span class="Special">&quot;</span><span class="Constant">--quiet</span><span class="Special">&quot;</span>
1016
+ <span class="PreProc">end</span>
1017
+
1018
+ end
1019
+
1020
+ end
1021
+ </pre>
1022
+ </div>
1023
+ </div>
1024
+ </p>
1025
+ <h3>Rake tasks</h3>
1026
+ <p>
1027
+ The Olag::Rake class sets up the tasks listed above as follows:
1028
+ </p>
1029
+ <p>
1030
+ <div class="named_with_containers chunk">
1031
+ <div class="chunk name">
1032
+ <a name="lib-olag-rake-rb">
1033
+ <span>lib/olag/rake.rb</span>
1034
+ </a>
1035
+ </div>
1036
+ <div class="chunk html">
1037
+ <pre class='ruby code syntax'>
1038
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">codnar/rake</span><span class="Special">&quot;</span>
1039
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/change_log</span><span class="Special">&quot;</span>
1040
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/gem_specification</span><span class="Special">&quot;</span>
1041
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/update_version</span><span class="Special">&quot;</span>
1042
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/version</span><span class="Special">&quot;</span>
1043
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">rake/clean</span><span class="Special">&quot;</span>
1044
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">rake/gempackagetask</span><span class="Special">&quot;</span>
1045
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">rake/rdoctask</span><span class="Special">&quot;</span>
1046
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">rake/testtask</span><span class="Special">&quot;</span>
1047
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">rcov/rcovtask</span><span class="Special">&quot;</span>
1048
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">reek/rake/task</span><span class="Special">&quot;</span>
1049
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">roodi</span><span class="Special">&quot;</span>
1050
+
1051
+ <span class="PreProc">module</span> <span class="Type">Olag</span>
1052
+
1053
+ </pre>
1054
+ <table class='layout'>
1055
+ <tr>
1056
+ <td class='indentation'>
1057
+ <pre> </pre>
1058
+ </td>
1059
+ <td class='html'>
1060
+ <div class='rdoc comment markup'>
1061
+ <p>
1062
+ Automate Rake task creation for a gem.
1063
+ </p>
1064
+ </div>
1065
+ </td>
1066
+ </tr>
1067
+ </table>
1068
+ <pre class='ruby code syntax'>
1069
+ <span class="PreProc">class</span> <span class="Type">Rake</span>
1070
+
1071
+ </pre>
1072
+ <table class='layout'>
1073
+ <tr>
1074
+ <td class='indentation'>
1075
+ <pre> </pre>
1076
+ </td>
1077
+ <td class='html'>
1078
+ <div class='rdoc comment markup'>
1079
+ <p>
1080
+ Define all the Rake tasks.
1081
+ </p>
1082
+ </div>
1083
+ </td>
1084
+ </tr>
1085
+ </table>
1086
+ <pre class='ruby code syntax'>
1087
+ <span class="PreProc">def</span> <span class="Identifier">initialize</span>(spec)
1088
+ <span class="Identifier">@spec</span> = spec
1089
+ <span class="Identifier">@ruby_sources</span> = <span class="Identifier">@spec</span>.files.find_all { |<span class="Identifier">file</span>| file =~ <span class="Special">/</span><span class="Special">^</span><span class="Constant">Rakefile</span><span class="Special">$</span><span class="Special">|</span><span class="Special">\.</span><span class="Constant">rb</span><span class="Special">$</span><span class="Special">/</span> }
1090
+ <span class="Identifier">@weave_configurations</span> = [ <span class="Constant">:weave_include</span>, <span class="Constant">:weave_named_chunk_with_containers</span> ]
1091
+ task(<span class="Constant">:default</span> =&gt; <span class="Constant">:all</span>)
1092
+ define_all_task
1093
+ <span class="Type">CLOBBER</span> &lt;&lt; <span class="Special">&quot;</span><span class="Constant">saikuro</span><span class="Special">&quot;</span>
1094
+ <span class="PreProc">end</span>
1095
+
1096
+ <span class="Statement">protected</span>
1097
+
1098
+ </pre>
1099
+ <table class='layout'>
1100
+ <tr>
1101
+ <td class='indentation'>
1102
+ <pre> </pre>
1103
+ </td>
1104
+ <td class='html'>
1105
+ <div class='rdoc comment markup'>
1106
+ <p>
1107
+ Define a task that does “everything”.
1108
+ </p>
1109
+ </div>
1110
+ </td>
1111
+ </tr>
1112
+ </table>
1113
+ <pre class='ruby code syntax'>
1114
+ <span class="PreProc">def</span> <span class="Identifier">define_all_task</span>
1115
+ define_desc_task(<span class="Special">&quot;</span><span class="Constant">Version, verify, document, package</span><span class="Special">&quot;</span>, <span class="Constant">:all</span> =&gt; [ <span class="Constant">:version</span>, <span class="Constant">:verify</span>, <span class="Constant">:doc</span>, <span class="Constant">:package</span> ])
1116
+ define_verify_task
1117
+ define_doc_task
1118
+ define_commit_task
1119
+ </pre>
1120
+ <table class='layout'>
1121
+ <tr>
1122
+ <td class='indentation'>
1123
+ <pre> </pre>
1124
+ </td>
1125
+ <td class='html'>
1126
+ <div class='rdoc comment markup'>
1127
+ <p>
1128
+ This is a problem. If the version number gets updated, GemPackageTask
1129
+ fails. This is better than if it used the old version number, I suppose,
1130
+ but not as nice as if it just used @spec.version everywhere. The solution
1131
+ for this is to do a dry run before doing the final <tt>rake</tt>
1132
+ <tt>commit</tt>, which is a good idea in general.
1133
+ </p>
1134
+ </div>
1135
+ </td>
1136
+ </tr>
1137
+ </table>
1138
+ <pre class='ruby code syntax'>
1139
+ ::<span class="Type">Rake</span>::<span class="Type">GemPackageTask</span>.new(<span class="Identifier">@spec</span>) { |<span class="Identifier">package</span>| }
1140
+ end
1141
+
1142
+ </pre>
1143
+ <pre class='nested chunk'>
1144
+ <a class='nested chunk' href='#verify-gem-functionality'>Verify gem functionality</a>
1145
+ </pre>
1146
+ <pre class='ruby code syntax'>
1147
+
1148
+ </pre>
1149
+ <pre class='nested chunk'>
1150
+ <a class='nested chunk' href='#analyze-the-source-code'>Analyze the source code</a>
1151
+ </pre>
1152
+ <pre class='ruby code syntax'>
1153
+
1154
+ </pre>
1155
+ <pre class='nested chunk'>
1156
+ <a class='nested chunk' href='#generate-rdoc-documentation'>Generate RDoc documentation</a>
1157
+ </pre>
1158
+ <pre class='ruby code syntax'>
1159
+
1160
+ </pre>
1161
+ <pre class='nested chunk'>
1162
+ <a class='nested chunk' href='#generate-codnar-documentation'>Generate Codnar documentation</a>
1163
+ </pre>
1164
+ <pre class='ruby code syntax'>
1165
+
1166
+ </pre>
1167
+ <pre class='nested chunk'>
1168
+ <a class='nested chunk' href='#automate-git-commit-process'>Automate Git commit process</a>
1169
+ </pre>
1170
+ <pre class='ruby code syntax'>
1171
+
1172
+ </pre>
1173
+ <pre class='nested chunk'>
1174
+ <a class='nested chunk' href='#task-utilities'>Task utilities</a>
1175
+ </pre>
1176
+ <pre class='ruby code syntax'>
1177
+
1178
+ end
1179
+
1180
+ end
1181
+ </pre>
1182
+ </div>
1183
+ </div>
1184
+ </p>
1185
+ <h4>Task utilities</h4>
1186
+ <p>
1187
+ The following utilities are used to create the different tasks. It would have
1188
+ be nicer if Rake had treated the task description as just another task
1189
+ property.
1190
+ </p>
1191
+ <p>
1192
+ <div class="named_with_containers chunk">
1193
+ <div class="chunk name">
1194
+ <a name="task-utilities">
1195
+ <span>Task utilities</span>
1196
+ </a>
1197
+ </div>
1198
+ <div class="chunk html">
1199
+ <pre class='ruby code syntax'>
1200
+
1201
+ </pre>
1202
+ <table class='layout'>
1203
+ <tr>
1204
+ <td class='indentation'>
1205
+ <pre></pre>
1206
+ </td>
1207
+ <td class='html'>
1208
+ <div class='rdoc comment markup'>
1209
+ <p>
1210
+ Define a new task with a description.
1211
+ </p>
1212
+ </div>
1213
+ </td>
1214
+ </tr>
1215
+ </table>
1216
+ <pre class='ruby code syntax'>
1217
+ <span class="PreProc">def</span> <span class="Identifier">define_desc_task</span>(description, *parameters)
1218
+ desc(description)
1219
+ task(*parameters) <span class="Statement">do</span>
1220
+ <span class="Statement">yield</span>(task) <span class="Statement">if</span> block_given?
1221
+ <span class="Statement">end</span>
1222
+ <span class="PreProc">end</span>
1223
+
1224
+ </pre>
1225
+ <table class='layout'>
1226
+ <tr>
1227
+ <td class='indentation'>
1228
+ <pre></pre>
1229
+ </td>
1230
+ <td class='html'>
1231
+ <div class='rdoc comment markup'>
1232
+ <p>
1233
+ Define a new task using some class.
1234
+ </p>
1235
+ </div>
1236
+ </td>
1237
+ </tr>
1238
+ </table>
1239
+ <pre class='ruby code syntax'>
1240
+ <span class="PreProc">def</span> <span class="Identifier">define_desc_class</span>(description, klass, *parameters)
1241
+ desc(description)
1242
+ klass.new(*parameters) <span class="Statement">do</span> |<span class="Identifier">task</span>|
1243
+ <span class="Statement">yield</span>(task) <span class="Statement">if</span> block_given?
1244
+ <span class="Statement">end</span>
1245
+ <span class="PreProc">end</span>
1246
+
1247
+ </pre>
1248
+ </div>
1249
+ <div class="chunk containers">
1250
+ <span class="chunk containers header">Contained in:</span>
1251
+ <ul class="chunk containers">
1252
+ <li class="chunk container">
1253
+ <a class="chunk container" href="#lib-olag-rake-rb">lib/olag/rake.rb</a>
1254
+ </li>
1255
+ </ul>
1256
+ </div>
1257
+ </div>
1258
+ </p>
1259
+ <h4>Verify the gem</h4>
1260
+ <p>
1261
+ The following tasks verify that the gem is correct. Testing for 100% code
1262
+ coverage seems excessive but in reality it isn't that hard to do, and is really
1263
+ only a modest form of test coverage verification.
1264
+ </p>
1265
+ <p>
1266
+ <div class="named_with_containers chunk">
1267
+ <div class="chunk name">
1268
+ <a name="verify-gem-functionality">
1269
+ <span>Verify gem functionality</span>
1270
+ </a>
1271
+ </div>
1272
+ <div class="chunk html">
1273
+ <pre class='ruby code syntax'>
1274
+
1275
+ </pre>
1276
+ <table class='layout'>
1277
+ <tr>
1278
+ <td class='indentation'>
1279
+ <pre></pre>
1280
+ </td>
1281
+ <td class='html'>
1282
+ <div class='rdoc comment markup'>
1283
+ <p>
1284
+ Define a task to verify everything is OK.
1285
+ </p>
1286
+ </div>
1287
+ </td>
1288
+ </tr>
1289
+ </table>
1290
+ <pre class='ruby code syntax'>
1291
+ <span class="PreProc">def</span> <span class="Identifier">define_verify_task</span>
1292
+ define_desc_task(<span class="Special">&quot;</span><span class="Constant">Test, coverage, analyze code</span><span class="Special">&quot;</span>, <span class="Constant">:verify</span> =&gt; [ <span class="Constant">:coverage</span>, <span class="Constant">:analyze</span> ])
1293
+ define_desc_class(<span class="Special">&quot;</span><span class="Constant">Test code covarage with RCov</span><span class="Special">&quot;</span>, <span class="Type">Rcov</span>::<span class="Type">RcovTask</span>, <span class="Special">&quot;</span><span class="Constant">coverage</span><span class="Special">&quot;</span>) { |<span class="Identifier">task</span>| <span class="Type">Rake</span>.configure_coverage_task(task) }
1294
+ define_desc_class(<span class="Special">&quot;</span><span class="Constant">Test code without coverage</span><span class="Special">&quot;</span>, ::<span class="Type">Rake</span>::<span class="Type">TestTask</span>, <span class="Special">&quot;</span><span class="Constant">test</span><span class="Special">&quot;</span>) { |<span class="Identifier">task</span>| <span class="Type">Rake</span>.configure_test_task(task) }
1295
+ define_analyze_task
1296
+ <span class="PreProc">end</span>
1297
+
1298
+ </pre>
1299
+ <table class='layout'>
1300
+ <tr>
1301
+ <td class='indentation'>
1302
+ <pre></pre>
1303
+ </td>
1304
+ <td class='html'>
1305
+ <div class='rdoc comment markup'>
1306
+ <p>
1307
+ Configure a task to run all tests and verify 100% coverage. This is
1308
+ cheating a bit, since it will not complain about files that are not reached
1309
+ at all from any of the tests.
1310
+ </p>
1311
+ </div>
1312
+ </td>
1313
+ </tr>
1314
+ </table>
1315
+ <pre class='ruby code syntax'>
1316
+ <span class="PreProc">def</span> <span class="Constant">self</span>.<span class="Identifier">configure_coverage_task</span>(task)
1317
+ task.test_files = <span class="Type">FileList</span>[<span class="Special">&quot;</span><span class="Constant">test/*.rb</span><span class="Special">&quot;</span>]
1318
+ task.libs &lt;&lt; <span class="Special">&quot;</span><span class="Constant">lib</span><span class="Special">&quot;</span> &lt;&lt; <span class="Special">&quot;</span><span class="Constant">test/lib</span><span class="Special">&quot;</span>
1319
+ task.rcov_opts &lt;&lt; <span class="Special">&quot;</span><span class="Constant">--failure-threshold</span><span class="Special">&quot;</span> &lt;&lt; <span class="Special">&quot;</span><span class="Constant">100</span><span class="Special">&quot;</span> \
1320
+ &lt;&lt; <span class="Special">&quot;</span><span class="Constant">--exclude</span><span class="Special">&quot;</span> &lt;&lt; <span class="Special">&quot;</span><span class="Constant">^/</span><span class="Special">&quot;</span>
1321
+ </pre>
1322
+ <table class='layout'>
1323
+ <tr>
1324
+ <td class='indentation'>
1325
+ <pre> </pre>
1326
+ </td>
1327
+ <td class='html'>
1328
+ <div class='rdoc comment markup'>
1329
+ <p>
1330
+ Strangely, on some occasions, RCov crazily tries to compute coverage for
1331
+ files inside Ruby’s gem repository. Excluding all absolute paths prevents
1332
+ this.
1333
+ </p>
1334
+ </div>
1335
+ </td>
1336
+ </tr>
1337
+ </table>
1338
+ <pre class='ruby code syntax'>
1339
+ end
1340
+
1341
+ </pre>
1342
+ <table class='layout'>
1343
+ <tr>
1344
+ <td class='indentation'>
1345
+ <pre></pre>
1346
+ </td>
1347
+ <td class='html'>
1348
+ <div class='rdoc comment markup'>
1349
+ <p>
1350
+ Configure a task to just run the tests without verifying coverage.
1351
+ </p>
1352
+ </div>
1353
+ </td>
1354
+ </tr>
1355
+ </table>
1356
+ <pre class='ruby code syntax'>
1357
+ <span class="PreProc">def</span> <span class="Constant">self</span>.<span class="Identifier">configure_test_task</span>(task)
1358
+ task.test_files = <span class="Type">FileList</span>[<span class="Special">&quot;</span><span class="Constant">test/*.rb</span><span class="Special">&quot;</span>]
1359
+ task.libs &lt;&lt; <span class="Special">&quot;</span><span class="Constant">lib</span><span class="Special">&quot;</span> &lt;&lt; <span class="Special">&quot;</span><span class="Constant">test/lib</span><span class="Special">&quot;</span>
1360
+ <span class="PreProc">end</span>
1361
+
1362
+ </pre>
1363
+ </div>
1364
+ <div class="chunk containers">
1365
+ <span class="chunk containers header">Contained in:</span>
1366
+ <ul class="chunk containers">
1367
+ <li class="chunk container">
1368
+ <a class="chunk container" href="#lib-olag-rake-rb">lib/olag/rake.rb</a>
1369
+ </li>
1370
+ </ul>
1371
+ </div>
1372
+ </div>
1373
+ </p>
1374
+ <p>
1375
+ The following tasks verify that the code is squeacky-clean. While passing the
1376
+ code through all these verifiers seems excessive, it isn't that hard to achieve
1377
+ in practice. There were several times I did refactorings "just to satisfy
1378
+ <code>reek</code> (or <code>flay</code>)" and ended up with an unexpected code improvement. Anyway,
1379
+ if you aren't a youch OCD about this sort of thing, Olag is probably not for
1380
+ you :-)
1381
+ </p>
1382
+ <p>
1383
+ <div class="named_with_containers chunk">
1384
+ <div class="chunk name">
1385
+ <a name="analyze-the-source-code">
1386
+ <span>Analyze the source code</span>
1387
+ </a>
1388
+ </div>
1389
+ <div class="chunk html">
1390
+ <pre class='ruby code syntax'>
1391
+
1392
+ </pre>
1393
+ <table class='layout'>
1394
+ <tr>
1395
+ <td class='indentation'>
1396
+ <pre></pre>
1397
+ </td>
1398
+ <td class='html'>
1399
+ <div class='rdoc comment markup'>
1400
+ <p>
1401
+ Define a task to verify the source code is clean.
1402
+ </p>
1403
+ </div>
1404
+ </td>
1405
+ </tr>
1406
+ </table>
1407
+ <pre class='ruby code syntax'>
1408
+ <span class="PreProc">def</span> <span class="Identifier">define_analyze_task</span>
1409
+ define_desc_task(<span class="Special">&quot;</span><span class="Constant">Analyze source code</span><span class="Special">&quot;</span>, <span class="Constant">:analyze</span> =&gt; [ <span class="Constant">:reek</span>, <span class="Constant">:roodi</span>, <span class="Constant">:flay</span>, <span class="Constant">:saikuro</span> ])
1410
+ define_desc_class(<span class="Special">&quot;</span><span class="Constant">Check for smelly code with Reek</span><span class="Special">&quot;</span>, <span class="Type">Reek</span>::<span class="Type">Rake</span>::<span class="Type">Task</span>) { |<span class="Identifier">task</span>| configure_reek_task(task) }
1411
+ define_desc_task(<span class="Special">&quot;</span><span class="Constant">Check for smelly code with Roodi</span><span class="Special">&quot;</span>, <span class="Constant">:roodi</span>) { run_roodi_task }
1412
+ define_desc_task(<span class="Special">&quot;</span><span class="Constant">Check for duplicated code with Flay</span><span class="Special">&quot;</span>, <span class="Constant">:flay</span>) { run_flay_task }
1413
+ define_desc_task(<span class="Special">&quot;</span><span class="Constant">Check for complex code with Saikuro</span><span class="Special">&quot;</span>, <span class="Constant">:saikuro</span>) { run_saikuro_task }
1414
+ <span class="PreProc">end</span>
1415
+
1416
+ </pre>
1417
+ <table class='layout'>
1418
+ <tr>
1419
+ <td class='indentation'>
1420
+ <pre></pre>
1421
+ </td>
1422
+ <td class='html'>
1423
+ <div class='rdoc comment markup'>
1424
+ <p>
1425
+ Configure a task to ensure there are no code smells using Reek.
1426
+ </p>
1427
+ </div>
1428
+ </td>
1429
+ </tr>
1430
+ </table>
1431
+ <pre class='ruby code syntax'>
1432
+ <span class="PreProc">def</span> <span class="Identifier">configure_reek_task</span>(task)
1433
+ task.reek_opts &lt;&lt; <span class="Special">&quot;</span><span class="Constant">--quiet</span><span class="Special">&quot;</span>
1434
+ task.source_files = <span class="Identifier">@ruby_sources</span>
1435
+ <span class="PreProc">end</span>
1436
+
1437
+ </pre>
1438
+ <table class='layout'>
1439
+ <tr>
1440
+ <td class='indentation'>
1441
+ <pre></pre>
1442
+ </td>
1443
+ <td class='html'>
1444
+ <div class='rdoc comment markup'>
1445
+ <p>
1446
+ Run Roodi to ensure there are no code smells.
1447
+ </p>
1448
+ </div>
1449
+ </td>
1450
+ </tr>
1451
+ </table>
1452
+ <pre class='ruby code syntax'>
1453
+ <span class="PreProc">def</span> <span class="Identifier">run_roodi_task</span>
1454
+ runner = <span class="Type">Roodi</span>::<span class="Type">Core</span>::<span class="Type">Runner</span>.new
1455
+ runner.config = <span class="Special">&quot;</span><span class="Constant">roodi.config</span><span class="Special">&quot;</span> <span class="Statement">if</span> <span class="Type">File</span>.exist?(<span class="Special">&quot;</span><span class="Constant">roodi.config</span><span class="Special">&quot;</span>)
1456
+ <span class="Identifier">@ruby_sources</span>.each { |<span class="Identifier">ruby_source</span>| runner.check_file(ruby_source) }
1457
+ (errors = runner.errors).each { |<span class="Identifier">error</span>| puts(error) }
1458
+ <span class="Statement">raise</span> <span class="Special">&quot;</span><span class="Constant">Roodi found </span><span class="Special">#{</span>errors.size<span class="Special">}</span><span class="Constant"> errors.</span><span class="Special">&quot;</span> <span class="Statement">unless</span> errors.empty?
1459
+ <span class="PreProc">end</span>
1460
+
1461
+ </pre>
1462
+ <table class='layout'>
1463
+ <tr>
1464
+ <td class='indentation'>
1465
+ <pre></pre>
1466
+ </td>
1467
+ <td class='html'>
1468
+ <div class='rdoc comment markup'>
1469
+ <p>
1470
+ Run Flay to ensure there are no duplicated code fragments.
1471
+ </p>
1472
+ </div>
1473
+ </td>
1474
+ </tr>
1475
+ </table>
1476
+ <pre class='ruby code syntax'>
1477
+ <span class="PreProc">def</span> <span class="Identifier">run_flay_task</span>
1478
+ dirs = <span class="Special">%w(</span><span class="Constant">bin lib test/lib</span><span class="Special">)</span>.find_all { |<span class="Identifier">dir</span>| <span class="Type">File</span>.exist?(dir) }
1479
+ result = <span class="Type">IO</span>.popen(<span class="Special">&quot;</span><span class="Constant">flay </span><span class="Special">&quot;</span> + dirs.join(<span class="Special">'</span><span class="Constant"> </span><span class="Special">'</span>), <span class="Special">&quot;</span><span class="Constant">r</span><span class="Special">&quot;</span>).read.chomp
1480
+ <span class="Statement">return</span> <span class="Statement">if</span> result == <span class="Special">&quot;</span><span class="Constant">Total score (lower is better) = 0</span><span class="Special">\n</span><span class="Special">&quot;</span>
1481
+ puts(result)
1482
+ <span class="Statement">raise</span> <span class="Special">&quot;</span><span class="Constant">Flay found code duplication.</span><span class="Special">&quot;</span>
1483
+ <span class="PreProc">end</span>
1484
+
1485
+ </pre>
1486
+ <table class='layout'>
1487
+ <tr>
1488
+ <td class='indentation'>
1489
+ <pre></pre>
1490
+ </td>
1491
+ <td class='html'>
1492
+ <div class='rdoc comment markup'>
1493
+ <p>
1494
+ Run Saikuro to ensure there are no overly complex functions.
1495
+ </p>
1496
+ </div>
1497
+ </td>
1498
+ </tr>
1499
+ </table>
1500
+ <pre class='ruby code syntax'>
1501
+ <span class="PreProc">def</span> <span class="Identifier">run_saikuro_task</span>
1502
+ dirs = <span class="Special">%w(</span><span class="Constant">bin lib test</span><span class="Special">)</span>.find_all { |<span class="Identifier">dir</span>| <span class="Type">File</span>.exist?(dir) }
1503
+ system(<span class="Special">&quot;</span><span class="Constant">saikuro -c -t -y 0 -e 10 -o saikuro/ -i </span><span class="Special">#{</span>dirs.join(<span class="Special">'</span><span class="Constant"> -i </span><span class="Special">'</span>)<span class="Special">}</span><span class="Constant"> &gt; /dev/null</span><span class="Special">&quot;</span>)
1504
+ result = <span class="Type">File</span>.read(<span class="Special">&quot;</span><span class="Constant">saikuro/index_cyclo.html</span><span class="Special">&quot;</span>)
1505
+ <span class="Statement">raise</span> <span class="Special">&quot;</span><span class="Constant">Saikuro found complicated code.</span><span class="Special">&quot;</span> <span class="Statement">if</span> result.include?(<span class="Special">&quot;</span><span class="Constant">Errors and Warnings</span><span class="Special">&quot;</span>)
1506
+ <span class="PreProc">end</span>
1507
+
1508
+ </pre>
1509
+ </div>
1510
+ <div class="chunk containers">
1511
+ <span class="chunk containers header">Contained in:</span>
1512
+ <ul class="chunk containers">
1513
+ <li class="chunk container">
1514
+ <a class="chunk container" href="#lib-olag-rake-rb">lib/olag/rake.rb</a>
1515
+ </li>
1516
+ </ul>
1517
+ </div>
1518
+ </div>
1519
+ </p>
1520
+ <h4>Generate Documentation</h4>
1521
+ <p>
1522
+ The following tasks generate the usual RDoc documentation, required to make the
1523
+ gem behave well in the Ruby tool ecosystem:
1524
+ </p>
1525
+ <p>
1526
+ <div class="named_with_containers chunk">
1527
+ <div class="chunk name">
1528
+ <a name="generate-rdoc-documentation">
1529
+ <span>Generate RDoc documentation</span>
1530
+ </a>
1531
+ </div>
1532
+ <div class="chunk html">
1533
+ <pre class='ruby code syntax'>
1534
+
1535
+ </pre>
1536
+ <table class='layout'>
1537
+ <tr>
1538
+ <td class='indentation'>
1539
+ <pre></pre>
1540
+ </td>
1541
+ <td class='html'>
1542
+ <div class='rdoc comment markup'>
1543
+ <p>
1544
+ Define a task to build all the documentation.
1545
+ </p>
1546
+ </div>
1547
+ </td>
1548
+ </tr>
1549
+ </table>
1550
+ <pre class='ruby code syntax'>
1551
+ <span class="PreProc">def</span> <span class="Identifier">define_doc_task</span>
1552
+ desc <span class="Special">&quot;</span><span class="Constant">Generate all documentation</span><span class="Special">&quot;</span>
1553
+ task <span class="Constant">:doc</span> =&gt; [ <span class="Constant">:rdoc</span>, <span class="Constant">:codnar</span> ]
1554
+ ::<span class="Type">Rake</span>::<span class="Type">RDocTask</span>.new { |<span class="Identifier">task</span>| configure_rdoc_task(task) }
1555
+ define_codnar_task
1556
+ <span class="PreProc">end</span>
1557
+
1558
+ </pre>
1559
+ <table class='layout'>
1560
+ <tr>
1561
+ <td class='indentation'>
1562
+ <pre></pre>
1563
+ </td>
1564
+ <td class='html'>
1565
+ <div class='rdoc comment markup'>
1566
+ <p>
1567
+ Configure a task to build the RDoc documentation.
1568
+ </p>
1569
+ </div>
1570
+ </td>
1571
+ </tr>
1572
+ </table>
1573
+ <pre class='ruby code syntax'>
1574
+ <span class="PreProc">def</span> <span class="Identifier">configure_rdoc_task</span>(task)
1575
+ task.rdoc_files += <span class="Identifier">@ruby_sources</span>.reject { |<span class="Identifier">file</span>| file =~ <span class="Special">/</span><span class="Special">^</span><span class="Constant">test</span><span class="Special">|</span><span class="Constant">Rakefile</span><span class="Special">/</span> } + [ <span class="Special">&quot;</span><span class="Constant">LICENSE</span><span class="Special">&quot;</span>, <span class="Special">&quot;</span><span class="Constant">README.rdoc</span><span class="Special">&quot;</span> ]
1576
+ task.main = <span class="Special">&quot;</span><span class="Constant">README.rdoc</span><span class="Special">&quot;</span>
1577
+ task.rdoc_dir = <span class="Special">&quot;</span><span class="Constant">rdoc</span><span class="Special">&quot;</span>
1578
+ task.options = <span class="Identifier">@spec</span>.rdoc_options
1579
+ <span class="PreProc">end</span>
1580
+
1581
+ </pre>
1582
+ </div>
1583
+ <div class="chunk containers">
1584
+ <span class="chunk containers header">Contained in:</span>
1585
+ <ul class="chunk containers">
1586
+ <li class="chunk container">
1587
+ <a class="chunk container" href="#lib-olag-rake-rb">lib/olag/rake.rb</a>
1588
+ </li>
1589
+ </ul>
1590
+ </div>
1591
+ </div>
1592
+ </p>
1593
+ <p>
1594
+ The following tasks generate the Codnar documentation (e.g., the document you
1595
+ are reading now), which goes beyond RDoc to provide an end-to-end linear
1596
+ narrative describing the gem:
1597
+ </p>
1598
+ <p>
1599
+ <div class="named_with_containers chunk">
1600
+ <div class="chunk name">
1601
+ <a name="generate-codnar-documentation">
1602
+ <span>Generate Codnar documentation</span>
1603
+ </a>
1604
+ </div>
1605
+ <div class="chunk html">
1606
+ <pre class='ruby code syntax'>
1607
+
1608
+ </pre>
1609
+ <table class='layout'>
1610
+ <tr>
1611
+ <td class='indentation'>
1612
+ <pre></pre>
1613
+ </td>
1614
+ <td class='html'>
1615
+ <div class='rdoc comment markup'>
1616
+ <p>
1617
+ A set of file Regexp patterns and their matching Codnar configurations. All
1618
+ the gem files are matched agains these patterns, in order, and a
1619
+ Codnar::SplitTask is created for the first matching one. If the matching
1620
+ configuration list is empty, the file is not split. However, the file must
1621
+ match at least one of the patterns. The gem is expected to modify this
1622
+ array, if needed, before creating the Rake object.
1623
+ </p>
1624
+ </div>
1625
+ </td>
1626
+ </tr>
1627
+ </table>
1628
+ <pre class='ruby code syntax'>
1629
+ <span class="Type">CODNAR_CONFIGURATIONS</span> = [
1630
+ [
1631
+ </pre>
1632
+ <table class='layout'>
1633
+ <tr>
1634
+ <td class='indentation'>
1635
+ <pre> </pre>
1636
+ </td>
1637
+ <td class='html'>
1638
+ <div class='rdoc comment markup'>
1639
+ <p>
1640
+ Exclude the ChangeLog and generated codnar.html files from the generated
1641
+ documentation.
1642
+ </p>
1643
+ </div>
1644
+ </td>
1645
+ </tr>
1646
+ </table>
1647
+ <pre class='ruby code syntax'>
1648
+ <span class="Special">&quot;</span><span class="Constant">ChangeLog|codnar\.html</span><span class="Special">&quot;</span>,
1649
+ ], [
1650
+ </pre>
1651
+ <table class='layout'>
1652
+ <tr>
1653
+ <td class='indentation'>
1654
+ <pre> </pre>
1655
+ </td>
1656
+ <td class='html'>
1657
+ <div class='rdoc comment markup'>
1658
+ <p>
1659
+ Configurations for splitting Ruby files. Using Sunlight makes for fast
1660
+ splitting but slow viewing. Using GVim is the reverse.
1661
+ </p>
1662
+ </div>
1663
+ </td>
1664
+ </tr>
1665
+ </table>
1666
+ <pre class='ruby code syntax'>
1667
+ <span class="Special">&quot;</span><span class="Constant">Rakefile|.*\.rb|bin/.*</span><span class="Special">&quot;</span>,
1668
+ <span class="Special">&quot;</span><span class="Constant">classify_source_code:ruby</span><span class="Special">&quot;</span>,
1669
+ <span class="Special">&quot;</span><span class="Constant">format_code_gvim_css:ruby</span><span class="Special">&quot;</span>,
1670
+ <span class="Special">&quot;</span><span class="Constant">classify_shell_comments</span><span class="Special">&quot;</span>,
1671
+ <span class="Special">&quot;</span><span class="Constant">format_rdoc_comments</span><span class="Special">&quot;</span>,
1672
+ <span class="Special">&quot;</span><span class="Constant">chunk_by_vim_regions</span><span class="Special">&quot;</span>,
1673
+ ], [
1674
+ </pre>
1675
+ <table class='layout'>
1676
+ <tr>
1677
+ <td class='indentation'>
1678
+ <pre> </pre>
1679
+ </td>
1680
+ <td class='html'>
1681
+ <div class='rdoc comment markup'>
1682
+ <p>
1683
+ Configurations for HTML documentation files.
1684
+ </p>
1685
+ </div>
1686
+ </td>
1687
+ </tr>
1688
+ </table>
1689
+ <pre class='ruby code syntax'>
1690
+ <span class="Special">&quot;</span><span class="Constant">.*\.html</span><span class="Special">&quot;</span>,
1691
+ <span class="Special">&quot;</span><span class="Constant">split_html_documentation</span><span class="Special">&quot;</span>,
1692
+ ], [
1693
+ </pre>
1694
+ <table class='layout'>
1695
+ <tr>
1696
+ <td class='indentation'>
1697
+ <pre> </pre>
1698
+ </td>
1699
+ <td class='html'>
1700
+ <div class='rdoc comment markup'>
1701
+ <p>
1702
+ Configurations for Markdown documentation files.
1703
+ </p>
1704
+ </div>
1705
+ </td>
1706
+ </tr>
1707
+ </table>
1708
+ <pre class='ruby code syntax'>
1709
+ <span class="Special">&quot;</span><span class="Constant">.*\.markdown|.*\.md</span><span class="Special">&quot;</span>,
1710
+ <span class="Special">&quot;</span><span class="Constant">split_markdown_documentation</span><span class="Special">&quot;</span>,
1711
+ ], [
1712
+ </pre>
1713
+ <table class='layout'>
1714
+ <tr>
1715
+ <td class='indentation'>
1716
+ <pre> </pre>
1717
+ </td>
1718
+ <td class='html'>
1719
+ <div class='rdoc comment markup'>
1720
+ <p>
1721
+ Configurations for RDOC documentation files.
1722
+ </p>
1723
+ </div>
1724
+ </td>
1725
+ </tr>
1726
+ </table>
1727
+ <pre class='ruby code syntax'>
1728
+ <span class="Special">&quot;</span><span class="Constant">LICENSE|.*\.rdoc</span><span class="Special">&quot;</span>,
1729
+ <span class="Special">&quot;</span><span class="Constant">split_rdoc_documentation</span><span class="Special">&quot;</span>,
1730
+ ],
1731
+ ]
1732
+
1733
+ </pre>
1734
+ <table class='layout'>
1735
+ <tr>
1736
+ <td class='indentation'>
1737
+ <pre></pre>
1738
+ </td>
1739
+ <td class='html'>
1740
+ <div class='rdoc comment markup'>
1741
+ <p>
1742
+ Define a task to build the Codnar documentation.
1743
+ </p>
1744
+ </div>
1745
+ </td>
1746
+ </tr>
1747
+ </table>
1748
+ <pre class='ruby code syntax'>
1749
+ <span class="PreProc">def</span> <span class="Identifier">define_codnar_task</span>
1750
+ <span class="Identifier">@spec</span>.files.each <span class="Statement">do</span> |<span class="Identifier">file</span>|
1751
+ configurations = <span class="Type">Rake</span>.split_configurations(file)
1752
+ <span class="Type">Codnar</span>::<span class="Type">Rake</span>::<span class="Type">SplitTask</span>.new([ file ], configurations) <span class="Statement">unless</span> configurations == []
1753
+ <span class="Statement">end</span>
1754
+ <span class="Type">Codnar</span>::<span class="Type">Rake</span>::<span class="Type">WeaveTask</span>.new(<span class="Special">&quot;</span><span class="Constant">doc/root.html</span><span class="Special">&quot;</span>, [ <span class="Constant">:weave_include</span>, <span class="Constant">:weave_named_chunk_with_containers</span> ])
1755
+ <span class="PreProc">end</span>
1756
+
1757
+ </pre>
1758
+ <table class='layout'>
1759
+ <tr>
1760
+ <td class='indentation'>
1761
+ <pre></pre>
1762
+ </td>
1763
+ <td class='html'>
1764
+ <div class='rdoc comment markup'>
1765
+ <p>
1766
+ Find the Codnar split configurations for a file.
1767
+ </p>
1768
+ </div>
1769
+ </td>
1770
+ </tr>
1771
+ </table>
1772
+ <pre class='ruby code syntax'>
1773
+ <span class="PreProc">def</span> <span class="Constant">self</span>.<span class="Identifier">split_configurations</span>(file)
1774
+ <span class="Type">CODNAR_CONFIGURATIONS</span>.each <span class="Statement">do</span> |<span class="Identifier">configurations</span>|
1775
+ regexp = configurations[<span class="Constant">0</span>] = convert_to_regexp(configurations[<span class="Constant">0</span>])
1776
+ <span class="Statement">return</span> configurations[<span class="Constant">1</span>..<span class="Constant">-1</span>] <span class="Statement">if</span> regexp.match(file)
1777
+ <span class="Statement">end</span>
1778
+ <span class="Statement">abort</span>(<span class="Special">&quot;</span><span class="Constant">No Codnar configuration for file: </span><span class="Special">#{</span>file<span class="Special">}</span><span class="Special">&quot;</span>)
1779
+ <span class="PreProc">end</span>
1780
+
1781
+ </pre>
1782
+ <table class='layout'>
1783
+ <tr>
1784
+ <td class='indentation'>
1785
+ <pre></pre>
1786
+ </td>
1787
+ <td class='html'>
1788
+ <div class='rdoc comment markup'>
1789
+ <p>
1790
+ Convert a string configuration pattern to a real Regexp.
1791
+ </p>
1792
+ </div>
1793
+ </td>
1794
+ </tr>
1795
+ </table>
1796
+ <pre class='ruby code syntax'>
1797
+ <span class="PreProc">def</span> <span class="Constant">self</span>.<span class="Identifier">convert_to_regexp</span>(regexp)
1798
+ <span class="Statement">return</span> regexp <span class="Statement">if</span> <span class="Type">Regexp</span> == regexp
1799
+ <span class="Statement">begin</span>
1800
+ <span class="Statement">return</span> <span class="Type">Regexp</span>.new(<span class="Special">&quot;</span><span class="Constant">^(</span><span class="Special">#{</span>regexp<span class="Special">}</span><span class="Constant">)$</span><span class="Special">&quot;</span>)
1801
+ <span class="Statement">rescue</span>
1802
+ <span class="Statement">abort</span>(<span class="Special">&quot;</span><span class="Constant">Invalid pattern regexp: ^(</span><span class="Special">#{</span>regexp<span class="Special">}</span><span class="Constant">)$ error: </span><span class="Special">#{</span><span class="Identifier">$!</span><span class="Special">}</span><span class="Special">&quot;</span>)
1803
+ <span class="Statement">end</span>
1804
+ <span class="PreProc">end</span>
1805
+
1806
+ </pre>
1807
+ </div>
1808
+ <div class="chunk containers">
1809
+ <span class="chunk containers header">Contained in:</span>
1810
+ <ul class="chunk containers">
1811
+ <li class="chunk container">
1812
+ <a class="chunk container" href="#lib-olag-rake-rb">lib/olag/rake.rb</a>
1813
+ </li>
1814
+ </ul>
1815
+ </div>
1816
+ </div>
1817
+ </p>
1818
+ <p>
1819
+ Codnar is very configurable, and the above provides a reasonable default
1820
+ configuration for pure Ruby gems. You can modify the CODNAR_CONFIGURATIONS
1821
+ array before creating the Rake object, by unshifting additional/overriding
1822
+ patterns into it. For example, you may choose to use GVim for syntax
1823
+ highlighting. This will cause splitting to become much slower, but the
1824
+ generated HTML will already include the highlighting markup so it will display
1825
+ instantly. Or, you may have additional source file types (Javascript, CSS,
1826
+ HTML, C, etc.) to be highlighted.
1827
+ </p>
1828
+ <h4>Automate Git commit process</h4>
1829
+ <p>
1830
+ In an ideal world, committing to Git would be a simple matter of typing <code>git
1831
+ commit -m "..."</code>. In our case, things get a bit complicated.
1832
+ </p>
1833
+ <p>
1834
+ There is some information that we need to extract out of Git and inject into
1835
+ our files (to be committed). Since Git pre-commit hooks do not allow us to
1836
+ modify any source files, this turns commit into a two-phase process: we do an
1837
+ initial commit, update some files, then <code>git commit --amend</code> to merge them with
1838
+ the first commit.
1839
+ </p>
1840
+ <p>
1841
+ <div class="named_with_containers chunk">
1842
+ <div class="chunk name">
1843
+ <a name="automate-git-commit-process">
1844
+ <span>Automate Git commit process</span>
1845
+ </a>
1846
+ </div>
1847
+ <div class="chunk html">
1848
+ <pre class='ruby code syntax'>
1849
+
1850
+ </pre>
1851
+ <table class='layout'>
1852
+ <tr>
1853
+ <td class='indentation'>
1854
+ <pre></pre>
1855
+ </td>
1856
+ <td class='html'>
1857
+ <div class='rdoc comment markup'>
1858
+ <p>
1859
+ Define a task that commit changes to Git.
1860
+ </p>
1861
+ </div>
1862
+ </td>
1863
+ </tr>
1864
+ </table>
1865
+ <pre class='ruby code syntax'>
1866
+ <span class="PreProc">def</span> <span class="Identifier">define_commit_task</span>
1867
+ define_desc_task(<span class="Special">&quot;</span><span class="Constant">Git commit process</span><span class="Special">&quot;</span>, <span class="Constant">:commit</span> =&gt; [ <span class="Constant">:all</span>, <span class="Constant">:first_commit</span>, <span class="Constant">:changelog</span>, <span class="Constant">:second_commit</span> ])
1868
+ define_desc_task(<span class="Special">&quot;</span><span class="Constant">Update version file from Git</span><span class="Special">&quot;</span>, <span class="Constant">:version</span>) { update_version_file }
1869
+ define_desc_task(<span class="Special">&quot;</span><span class="Constant">Perform the 1st (main) Git commit</span><span class="Special">&quot;</span>, <span class="Constant">:first_commit</span>) { run_git_first_commit }
1870
+ define_desc_task(<span class="Special">&quot;</span><span class="Constant">Perform the 2nd (amend) Git commit</span><span class="Special">&quot;</span>, <span class="Constant">:second_commit</span>) { run_git_second_commit }
1871
+ define_desc_task(<span class="Special">&quot;</span><span class="Constant">Update ChangeLog from Git</span><span class="Special">&quot;</span>, <span class="Constant">:changelog</span>) { <span class="Type">Olag</span>::<span class="Type">ChangeLog</span>.new(<span class="Special">&quot;</span><span class="Constant">ChangeLog</span><span class="Special">&quot;</span>) }
1872
+ <span class="PreProc">end</span>
1873
+
1874
+ </pre>
1875
+ <table class='layout'>
1876
+ <tr>
1877
+ <td class='indentation'>
1878
+ <pre></pre>
1879
+ </td>
1880
+ <td class='html'>
1881
+ <div class='rdoc comment markup'>
1882
+ <p>
1883
+ Update the content of the version file to contain the correct Git-derived
1884
+ build number.
1885
+ </p>
1886
+ </div>
1887
+ </td>
1888
+ </tr>
1889
+ </table>
1890
+ <pre class='ruby code syntax'>
1891
+ <span class="PreProc">def</span> <span class="Identifier">update_version_file</span>
1892
+ version_file = <span class="Identifier">@spec</span>.version_file
1893
+ updated_version = <span class="Type">Olag</span>::<span class="Type">Version</span>::update(version_file)
1894
+ <span class="Statement">abort</span>(<span class="Special">&quot;</span><span class="Constant">Updated gem version; re-run rake</span><span class="Special">&quot;</span>) <span class="Statement">if</span> <span class="Identifier">@spec</span>.version.to_s != updated_version
1895
+ <span class="PreProc">end</span>
1896
+
1897
+ </pre>
1898
+ <table class='layout'>
1899
+ <tr>
1900
+ <td class='indentation'>
1901
+ <pre></pre>
1902
+ </td>
1903
+ <td class='html'>
1904
+ <div class='rdoc comment markup'>
1905
+ <p>
1906
+ Run the first Git commit. The user will be given an editor to review the
1907
+ commit and enter a commit message.
1908
+ </p>
1909
+ </div>
1910
+ </td>
1911
+ </tr>
1912
+ </table>
1913
+ <pre class='ruby code syntax'>
1914
+ <span class="PreProc">def</span> <span class="Identifier">run_git_first_commit</span>
1915
+ <span class="Statement">raise</span> <span class="Special">&quot;</span><span class="Constant">Git 1st commit failed</span><span class="Special">&quot;</span> <span class="Statement">unless</span> system(<span class="Special">&quot;</span><span class="Constant">set +x; git commit</span><span class="Special">&quot;</span>)
1916
+ <span class="PreProc">end</span>
1917
+
1918
+ </pre>
1919
+ <table class='layout'>
1920
+ <tr>
1921
+ <td class='indentation'>
1922
+ <pre></pre>
1923
+ </td>
1924
+ <td class='html'>
1925
+ <div class='rdoc comment markup'>
1926
+ <p>
1927
+ Run the second Git commit. This amends the first commit with the updated
1928
+ ChangeLog.
1929
+ </p>
1930
+ </div>
1931
+ </td>
1932
+ </tr>
1933
+ </table>
1934
+ <pre class='ruby code syntax'>
1935
+ <span class="PreProc">def</span> <span class="Identifier">run_git_second_commit</span>
1936
+ <span class="Statement">raise</span> <span class="Special">&quot;</span><span class="Constant">Git 2nd commit failed</span><span class="Special">&quot;</span> <span class="Statement">unless</span> system(<span class="Special">&quot;</span><span class="Constant">set +x; EDITOR=true git commit --amend ChangeLog</span><span class="Special">&quot;</span>)
1937
+ <span class="PreProc">end</span>
1938
+
1939
+ </pre>
1940
+ </div>
1941
+ <div class="chunk containers">
1942
+ <span class="chunk containers header">Contained in:</span>
1943
+ <ul class="chunk containers">
1944
+ <li class="chunk container">
1945
+ <a class="chunk container" href="#lib-olag-rake-rb">lib/olag/rake.rb</a>
1946
+ </li>
1947
+ </ul>
1948
+ </div>
1949
+ </div>
1950
+ </p>
1951
+ <p>
1952
+ The first piece of information we need to extract from Git is the current build
1953
+ number, which needs to be injected into the gem's version number:
1954
+ </p>
1955
+ <p>
1956
+ <div class="named_with_containers chunk">
1957
+ <div class="chunk name">
1958
+ <a name="lib-olag-version-rb">
1959
+ <span>lib/olag/version.rb</span>
1960
+ </a>
1961
+ </div>
1962
+ <div class="chunk html">
1963
+ <table class='layout'>
1964
+ <tr>
1965
+ <td class='indentation'>
1966
+ <pre></pre>
1967
+ </td>
1968
+ <td class='html'>
1969
+ <div class='rdoc comment markup'>
1970
+ <p>
1971
+ This module contains all the Olag code.
1972
+ </p>
1973
+ </div>
1974
+ </td>
1975
+ </tr>
1976
+ </table>
1977
+ <pre class='ruby code syntax'>
1978
+ <span class="PreProc">module</span> <span class="Type">Olag</span>
1979
+
1980
+ </pre>
1981
+ <table class='layout'>
1982
+ <tr>
1983
+ <td class='indentation'>
1984
+ <pre> </pre>
1985
+ </td>
1986
+ <td class='html'>
1987
+ <div class='rdoc comment markup'>
1988
+ <p>
1989
+ This version number. The third number is automatically updated to track the
1990
+ number of Git commits by running <tt>rake version</tt>.
1991
+ </p>
1992
+ </div>
1993
+ </td>
1994
+ </tr>
1995
+ </table>
1996
+ <pre class='ruby code syntax'>
1997
+ <span class="Type">VERSION</span> = <span class="Special">&quot;</span><span class="Constant">0.1.10</span><span class="Special">&quot;</span>
1998
+
1999
+ end
2000
+ </pre>
2001
+ </div>
2002
+ </div>
2003
+ </p>
2004
+ <p>
2005
+ Documentation generation will depend on the content (and therefore modification
2006
+ time) of this file. Luckily, we can update this number before the first commit,
2007
+ and we can ensure it only touches the file if there is a real change, to avoid
2008
+ unnecessary documentation regeneration:
2009
+ </p>
2010
+ <p>
2011
+ <div class="named_with_containers chunk">
2012
+ <div class="chunk name">
2013
+ <a name="lib-olag-update-version-rb">
2014
+ <span>lib/olag/update_version.rb</span>
2015
+ </a>
2016
+ </div>
2017
+ <div class="chunk html">
2018
+ <pre class='ruby code syntax'>
2019
+ <span class="PreProc">module</span> <span class="Type">Olag</span>
2020
+
2021
+ <span class="PreProc">module</span> <span class="Type">Version</span>
2022
+
2023
+ </pre>
2024
+ <table class='layout'>
2025
+ <tr>
2026
+ <td class='indentation'>
2027
+ <pre> </pre>
2028
+ </td>
2029
+ <td class='html'>
2030
+ <div class='rdoc comment markup'>
2031
+ <p>
2032
+ Update the file containing the gem’s version. The file is expected to
2033
+ contain a line in the format: <tt>VERSION =
2034
+ &quot;<em>major</em>.<em>minor</em>.<em>commits</em>&quot;</tt>. The third
2035
+ number is updated according to the number of Git commits. This works well
2036
+ as long as we are working in the master branch.
2037
+ </p>
2038
+ </div>
2039
+ </td>
2040
+ </tr>
2041
+ </table>
2042
+ <pre class='ruby code syntax'>
2043
+ <span class="PreProc">def</span> <span class="Constant">self</span>.<span class="Identifier">update</span>(path)
2044
+ current_file_contents, current_version, correct_version = current_status(path)
2045
+ <span class="Statement">if</span> current_version != correct_version
2046
+ correct_file_contents = current_file_contents.sub(current_version, correct_version)
2047
+ <span class="Type">File</span>.open(path, <span class="Special">&quot;</span><span class="Constant">w</span><span class="Special">&quot;</span>) { |<span class="Identifier">file</span>| file.write(correct_file_contents) }
2048
+ <span class="Statement">end</span>
2049
+ <span class="Statement">return</span> correct_version
2050
+ <span class="PreProc">end</span>
2051
+
2052
+ <span class="Statement">protected</span>
2053
+
2054
+ </pre>
2055
+ <table class='layout'>
2056
+ <tr>
2057
+ <td class='indentation'>
2058
+ <pre> </pre>
2059
+ </td>
2060
+ <td class='html'>
2061
+ <div class='rdoc comment markup'>
2062
+ <p>
2063
+ Return the current version file contents, the current version, and the
2064
+ correct version.
2065
+ </p>
2066
+ </div>
2067
+ </td>
2068
+ </tr>
2069
+ </table>
2070
+ <pre class='ruby code syntax'>
2071
+ <span class="PreProc">def</span> <span class="Constant">self</span>.<span class="Identifier">current_status</span>(path)
2072
+ prefix, current_suffix = extract_version(path, current_file_contents = <span class="Type">File</span>.read(path))
2073
+ correct_suffix = count_git_commits.to_s
2074
+ current_version = prefix + current_suffix
2075
+ correct_version = prefix + correct_suffix
2076
+ <span class="Statement">return</span> current_file_contents, current_version, correct_version
2077
+ <span class="PreProc">end</span>
2078
+
2079
+ </pre>
2080
+ <table class='layout'>
2081
+ <tr>
2082
+ <td class='indentation'>
2083
+ <pre> </pre>
2084
+ </td>
2085
+ <td class='html'>
2086
+ <div class='rdoc comment markup'>
2087
+ <p>
2088
+ Extract the version number from the contents of the version file. This is
2089
+ an array of two strings - the prefix containing the major and minor
2090
+ numbers, and the suffix containing the commits number.
2091
+ </p>
2092
+ </div>
2093
+ </td>
2094
+ </tr>
2095
+ </table>
2096
+ <pre class='ruby code syntax'>
2097
+ <span class="PreProc">def</span> <span class="Constant">self</span>.<span class="Identifier">extract_version</span>(path, file_contents)
2098
+ <span class="Statement">abort</span>(<span class="Special">&quot;</span><span class="Special">#{</span>path<span class="Special">}</span><span class="Constant">: Does not contain a valid VERSION line.</span><span class="Special">&quot;</span>) <span class="Statement">unless</span> file_contents =~ <span class="Special">/</span><span class="Constant">VERSION</span><span class="Special">\s</span><span class="Special">+</span><span class="Constant">=</span><span class="Special">\s</span><span class="Special">+</span><span class="Special">[</span><span class="Constant">&quot;'</span><span class="Special">]</span><span class="Special">(</span><span class="Special">\d</span><span class="Special">+</span><span class="Special">\.</span><span class="Special">\d</span><span class="Special">+</span><span class="Special">\.</span><span class="Special">)(</span><span class="Special">\d</span><span class="Special">+</span><span class="Special">)</span><span class="Special">[</span><span class="Constant">&quot;'</span><span class="Special">]</span><span class="Special">/</span>
2099
+ <span class="Statement">return</span> [ <span class="Identifier">$1</span>, <span class="Identifier">$2</span> ]
2100
+ <span class="PreProc">end</span>
2101
+
2102
+ </pre>
2103
+ <table class='layout'>
2104
+ <tr>
2105
+ <td class='indentation'>
2106
+ <pre> </pre>
2107
+ </td>
2108
+ <td class='html'>
2109
+ <div class='rdoc comment markup'>
2110
+ <p>
2111
+ Return the total number of Git commits that apply to the current state of
2112
+ the working directory. This means we add one to the actual number of
2113
+ commits if there are uncommitted changes; this way the version number does
2114
+ not change after doing a commit - it only changes after we make changes
2115
+ following a commit.
2116
+ </p>
2117
+ </div>
2118
+ </td>
2119
+ </tr>
2120
+ </table>
2121
+ <pre class='ruby code syntax'>
2122
+ <span class="PreProc">def</span> <span class="Constant">self</span>.<span class="Identifier">count_git_commits</span>
2123
+ git_commits = <span class="Type">IO</span>.popen(<span class="Special">&quot;</span><span class="Constant">git rev-list --all | wc -l</span><span class="Special">&quot;</span>).read.chomp.to_i
2124
+ git_status = <span class="Type">IO</span>.popen(<span class="Special">&quot;</span><span class="Constant">git status</span><span class="Special">&quot;</span>).read
2125
+ git_commits += <span class="Constant">1</span> <span class="Statement">unless</span> git_status.include?(<span class="Special">&quot;</span><span class="Constant">working directory clean</span><span class="Special">&quot;</span>)
2126
+ <span class="Statement">return</span> git_commits
2127
+ <span class="PreProc">end</span>
2128
+
2129
+ end
2130
+
2131
+ end
2132
+ </pre>
2133
+ </div>
2134
+ </div>
2135
+ </p>
2136
+ <p>
2137
+ The second information we extract from Git is the ChangeLog file. Here,
2138
+ obviously, the ChangeLog needs to include the first commit's message, so we are
2139
+ forced to regenerate the file and amend Git's history with a second commit:
2140
+ </p>
2141
+ <p>
2142
+ <div class="named_with_containers chunk">
2143
+ <div class="chunk name">
2144
+ <a name="lib-olag-change-log-rb">
2145
+ <span>lib/olag/change_log.rb</span>
2146
+ </a>
2147
+ </div>
2148
+ <div class="chunk html">
2149
+ <pre class='ruby code syntax'>
2150
+ <span class="PreProc">module</span> <span class="Type">Olag</span>
2151
+
2152
+ </pre>
2153
+ <table class='layout'>
2154
+ <tr>
2155
+ <td class='indentation'>
2156
+ <pre> </pre>
2157
+ </td>
2158
+ <td class='html'>
2159
+ <div class='rdoc comment markup'>
2160
+ <p>
2161
+ Create ChangeLog files based on the Git revision history.
2162
+ </p>
2163
+ </div>
2164
+ </td>
2165
+ </tr>
2166
+ </table>
2167
+ <pre class='ruby code syntax'>
2168
+ <span class="PreProc">class</span> <span class="Type">ChangeLog</span>
2169
+
2170
+ </pre>
2171
+ <table class='layout'>
2172
+ <tr>
2173
+ <td class='indentation'>
2174
+ <pre> </pre>
2175
+ </td>
2176
+ <td class='html'>
2177
+ <div class='rdoc comment markup'>
2178
+ <p>
2179
+ Write a changelog based on the Git log.
2180
+ </p>
2181
+ </div>
2182
+ </td>
2183
+ </tr>
2184
+ </table>
2185
+ <pre class='ruby code syntax'>
2186
+ <span class="PreProc">def</span> <span class="Identifier">initialize</span>(path)
2187
+ <span class="Identifier">@subjects_by_id</span> = {}
2188
+ <span class="Identifier">@sorted_ids</span> = []
2189
+ read_log_lines
2190
+ <span class="Type">File</span>.open(path, <span class="Special">&quot;</span><span class="Constant">w</span><span class="Special">&quot;</span>) <span class="Statement">do</span> |<span class="Identifier">file</span>|
2191
+ <span class="Identifier">@log_file</span> = file
2192
+ write_log_file
2193
+ <span class="Statement">end</span>
2194
+ <span class="PreProc">end</span>
2195
+
2196
+ <span class="Statement">protected</span>
2197
+
2198
+ </pre>
2199
+ <table class='layout'>
2200
+ <tr>
2201
+ <td class='indentation'>
2202
+ <pre> </pre>
2203
+ </td>
2204
+ <td class='html'>
2205
+ <div class='rdoc comment markup'>
2206
+ <p>
2207
+ Read all the log lines from Git’s revision history.
2208
+ </p>
2209
+ </div>
2210
+ </td>
2211
+ </tr>
2212
+ </table>
2213
+ <pre class='ruby code syntax'>
2214
+ <span class="PreProc">def</span> <span class="Identifier">read_log_lines</span>
2215
+ <span class="Type">IO</span>.popen(<span class="Special">&quot;</span><span class="Constant">git log --pretty='format:%ci::%an &lt;%ae&gt;::%s'</span><span class="Special">&quot;</span>, <span class="Special">&quot;</span><span class="Constant">r</span><span class="Special">&quot;</span>).each_line <span class="Statement">do</span> |<span class="Identifier">log_line</span>|
2216
+ load_log_line(log_line)
2217
+ <span class="Statement">end</span>
2218
+ <span class="PreProc">end</span>
2219
+
2220
+ </pre>
2221
+ <table class='layout'>
2222
+ <tr>
2223
+ <td class='indentation'>
2224
+ <pre> </pre>
2225
+ </td>
2226
+ <td class='html'>
2227
+ <div class='rdoc comment markup'>
2228
+ <p>
2229
+ Load a single Git log line into memory.
2230
+ </p>
2231
+ </div>
2232
+ </td>
2233
+ </tr>
2234
+ </table>
2235
+ <pre class='ruby code syntax'>
2236
+ <span class="PreProc">def</span> <span class="Identifier">load_log_line</span>(log_line)
2237
+ id, subject = <span class="Type">ChangeLog</span>.parse_log_line(log_line)
2238
+ <span class="Identifier">@sorted_ids</span> &lt;&lt; id
2239
+ <span class="Identifier">@subjects_by_id</span>[id] ||= []
2240
+ <span class="Identifier">@subjects_by_id</span>[id] &lt;&lt; subject
2241
+ <span class="PreProc">end</span>
2242
+
2243
+ </pre>
2244
+ <table class='layout'>
2245
+ <tr>
2246
+ <td class='indentation'>
2247
+ <pre> </pre>
2248
+ </td>
2249
+ <td class='html'>
2250
+ <div class='rdoc comment markup'>
2251
+ <p>
2252
+ Extract the information we need (ChangeLog entry id and subject) from a Git
2253
+ log line.
2254
+ </p>
2255
+ </div>
2256
+ </td>
2257
+ </tr>
2258
+ </table>
2259
+ <pre class='ruby code syntax'>
2260
+ <span class="PreProc">def</span> <span class="Constant">self</span>.<span class="Identifier">parse_log_line</span>(log_line)
2261
+ date, author, subject = log_line.chomp.split(<span class="Special">&quot;</span><span class="Constant">::</span><span class="Special">&quot;</span>)
2262
+ date, time, zone = date.split(<span class="Special">&quot;</span><span class="Constant"> </span><span class="Special">&quot;</span>)
2263
+ id = <span class="Special">&quot;</span><span class="Special">#{</span>date<span class="Special">}</span><span class="Special">\t</span><span class="Special">#{</span>author<span class="Special">}</span><span class="Special">&quot;</span>
2264
+ <span class="Statement">return</span> id, subject
2265
+ <span class="PreProc">end</span>
2266
+
2267
+ </pre>
2268
+ <table class='layout'>
2269
+ <tr>
2270
+ <td class='indentation'>
2271
+ <pre> </pre>
2272
+ </td>
2273
+ <td class='html'>
2274
+ <div class='rdoc comment markup'>
2275
+ <p>
2276
+ Write a ChangeLog file based on the read Git log lines.
2277
+ </p>
2278
+ </div>
2279
+ </td>
2280
+ </tr>
2281
+ </table>
2282
+ <pre class='ruby code syntax'>
2283
+ <span class="PreProc">def</span> <span class="Identifier">write_log_file</span>
2284
+ <span class="Identifier">@sorted_ids</span>.uniq.each <span class="Statement">do</span> |<span class="Identifier">id</span>|
2285
+ write_log_entry(id, <span class="Identifier">@subjects_by_id</span>[id])
2286
+ <span class="Statement">end</span>
2287
+ <span class="PreProc">end</span>
2288
+
2289
+ </pre>
2290
+ <table class='layout'>
2291
+ <tr>
2292
+ <td class='indentation'>
2293
+ <pre> </pre>
2294
+ </td>
2295
+ <td class='html'>
2296
+ <div class='rdoc comment markup'>
2297
+ <p>
2298
+ Write a single ChaneLog entry.
2299
+ </p>
2300
+ </div>
2301
+ </td>
2302
+ </tr>
2303
+ </table>
2304
+ <pre class='ruby code syntax'>
2305
+ <span class="PreProc">def</span> <span class="Identifier">write_log_entry</span>(id, subjects)
2306
+ <span class="Identifier">@log_file</span>.puts <span class="Special">&quot;</span><span class="Special">#{</span>id<span class="Special">}</span><span class="Special">\n\n</span><span class="Special">&quot;</span>
2307
+ <span class="Identifier">@log_file</span>.puts subjects.map { |<span class="Identifier">subject</span>| <span class="Special">&quot;</span><span class="Special">\t</span><span class="Constant">* </span><span class="Special">#{</span>subject<span class="Special">}</span><span class="Special">&quot;</span> }.join(<span class="Special">&quot;</span><span class="Special">\n</span><span class="Special">&quot;</span>)
2308
+ <span class="Identifier">@log_file</span>.puts <span class="Special">&quot;</span><span class="Special">\n</span><span class="Special">&quot;</span>
2309
+ <span class="PreProc">end</span>
2310
+
2311
+ end
2312
+
2313
+ end
2314
+ </pre>
2315
+ </div>
2316
+ </div>
2317
+ </p>
2318
+ <h2>Utility classes</h2>
2319
+ <p>
2320
+ Olag provides a set of utility classes that are useful in implementing
2321
+ well-behaved gems.
2322
+ </p>
2323
+ <h3>Unindeting text</h3>
2324
+ <p>
2325
+ When using "here documents" (<code>&lt;&lt;EOF</code> data), it is nice to be able to indent the
2326
+ data to match the surrounding code. There are other cases where it is useful to
2327
+ "unindent" multi-line text. The following tests demonstrates using the
2328
+ <code>unindent</code> function:
2329
+ </p>
2330
+ <p>
2331
+ <div class="named_with_containers chunk">
2332
+ <div class="chunk name">
2333
+ <a name="test-unindent-text-rb">
2334
+ <span>test/unindent_text.rb</span>
2335
+ </a>
2336
+ </div>
2337
+ <div class="chunk html">
2338
+ <pre class='ruby code syntax'>
2339
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/string_unindent</span><span class="Special">&quot;</span>
2340
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">test/spec</span><span class="Special">&quot;</span>
2341
+
2342
+ </pre>
2343
+ <table class='layout'>
2344
+ <tr>
2345
+ <td class='indentation'>
2346
+ <pre></pre>
2347
+ </td>
2348
+ <td class='html'>
2349
+ <div class='rdoc comment markup'>
2350
+ <p>
2351
+ Test unindenting a multi-line text.
2352
+ </p>
2353
+ </div>
2354
+ </td>
2355
+ </tr>
2356
+ </table>
2357
+ <pre class='ruby code syntax'>
2358
+ <span class="PreProc">class</span> <span class="Type">TestUnindentText</span> &lt; ::<span class="Type">Test</span>::<span class="Type">Unit</span>::<span class="Type">TestCase</span>
2359
+
2360
+ <span class="PreProc">def</span> <span class="Identifier">test_automatic_unindent</span>
2361
+ &lt;&lt;-<span class="Special">EOF</span>.unindent.should == <span class="Special">&quot;</span><span class="Constant">a</span><span class="Special">\n</span><span class="Constant"> b</span><span class="Special">\n</span><span class="Special">&quot;</span>
2362
+ <span class="Constant"> a</span>
2363
+ <span class="Constant"> b</span>
2364
+ <span class="Constant"> </span><span class="Special">EOF</span>
2365
+ <span class="PreProc">end</span>
2366
+
2367
+ <span class="PreProc">def</span> <span class="Identifier">test_invalid_unindent</span>
2368
+ <span class="Special">&quot;</span><span class="Constant"> a</span><span class="Special">\n</span><span class="Constant"> b</span><span class="Special">\n</span><span class="Special">&quot;</span>.unindent.should == <span class="Special">&quot;</span><span class="Constant">a</span><span class="Special">\n</span><span class="Constant"> b</span><span class="Special">\n</span><span class="Special">&quot;</span>
2369
+ <span class="PreProc">end</span>
2370
+
2371
+ <span class="PreProc">def</span> <span class="Identifier">test_integer_unindent</span>
2372
+ <span class="Special">&quot;</span><span class="Constant"> a</span><span class="Special">\n</span><span class="Constant"> b</span><span class="Special">\n</span><span class="Special">&quot;</span>.unindent(<span class="Constant">1</span>).should == <span class="Special">&quot;</span><span class="Constant"> a</span><span class="Special">\n</span><span class="Constant"> b</span><span class="Special">\n</span><span class="Special">&quot;</span>
2373
+ <span class="PreProc">end</span>
2374
+
2375
+ <span class="PreProc">def</span> <span class="Identifier">test_string_unindent</span>
2376
+ <span class="Special">&quot;</span><span class="Constant"> a</span><span class="Special">\n</span><span class="Constant"> b</span><span class="Special">\n</span><span class="Special">&quot;</span>.unindent(<span class="Special">&quot;</span><span class="Constant"> </span><span class="Special">&quot;</span>).should == <span class="Special">&quot;</span><span class="Constant"> a</span><span class="Special">\n</span><span class="Constant"> b</span><span class="Special">\n</span><span class="Special">&quot;</span>
2377
+ <span class="PreProc">end</span>
2378
+
2379
+ <span class="PreProc">end</span>
2380
+ </pre>
2381
+ </div>
2382
+ </div>
2383
+ </p>
2384
+ <p>
2385
+ And here is the implementation extending the built-in String class:
2386
+ </p>
2387
+ <p>
2388
+ <div class="named_with_containers chunk">
2389
+ <div class="chunk name">
2390
+ <a name="lib-olag-string-unindent-rb">
2391
+ <span>lib/olag/string_unindent.rb</span>
2392
+ </a>
2393
+ </div>
2394
+ <div class="chunk html">
2395
+ <table class='layout'>
2396
+ <tr>
2397
+ <td class='indentation'>
2398
+ <pre></pre>
2399
+ </td>
2400
+ <td class='html'>
2401
+ <div class='rdoc comment markup'>
2402
+ <p>
2403
+ Extend the core String class.
2404
+ </p>
2405
+ </div>
2406
+ </td>
2407
+ </tr>
2408
+ </table>
2409
+ <pre class='ruby code syntax'>
2410
+ <span class="PreProc">class</span> <span class="Type">String</span>
2411
+
2412
+ </pre>
2413
+ <table class='layout'>
2414
+ <tr>
2415
+ <td class='indentation'>
2416
+ <pre> </pre>
2417
+ </td>
2418
+ <td class='html'>
2419
+ <div class='rdoc comment markup'>
2420
+ <p>
2421
+ Strip away common indentation from the beginning of each line in this
2422
+ String. By default, detects the indentation from the first line. This can
2423
+ be overriden to the exact (String) indentation to strip, or to the (Fixnum)
2424
+ number of spaces the first line is further-indented from the rest of the
2425
+ text.
2426
+ </p>
2427
+ </div>
2428
+ </td>
2429
+ </tr>
2430
+ </table>
2431
+ <pre class='ruby code syntax'>
2432
+ <span class="PreProc">def</span> <span class="Identifier">unindent</span>(unindentation = <span class="Constant">0</span>)
2433
+ unindentation = <span class="Special">&quot;</span><span class="Constant"> </span><span class="Special">&quot;</span> * (indentation.length - unindentation) <span class="Statement">if</span> <span class="Type">Fixnum</span> === unindentation
2434
+ <span class="Statement">return</span> gsub(<span class="Special">/</span><span class="Special">^</span><span class="Special">#{</span>unindentation<span class="Special">}</span><span class="Special">/</span>, <span class="Special">&quot;&quot;</span>)
2435
+ <span class="PreProc">end</span>
2436
+
2437
+ </pre>
2438
+ <table class='layout'>
2439
+ <tr>
2440
+ <td class='indentation'>
2441
+ <pre> </pre>
2442
+ </td>
2443
+ <td class='html'>
2444
+ <div class='rdoc comment markup'>
2445
+ <p>
2446
+ Extract the indentation from the beginning of this String.
2447
+ </p>
2448
+ </div>
2449
+ </td>
2450
+ </tr>
2451
+ </table>
2452
+ <pre class='ruby code syntax'>
2453
+ <span class="PreProc">def</span> <span class="Identifier">indentation</span>
2454
+ <span class="Statement">return</span> sub(<span class="Special">/</span><span class="Special">[^</span><span class="Constant"> </span><span class="Special">]</span><span class="Special">.</span><span class="Special">*</span><span class="Special">$</span><span class="Special">/m</span>, <span class="Special">&quot;&quot;</span>)
2455
+ <span class="PreProc">end</span>
2456
+
2457
+ end
2458
+ </pre>
2459
+ </div>
2460
+ </div>
2461
+ </p>
2462
+ <h3>Accessing gem data files</h3>
2463
+ <p>
2464
+ Sometimes it is useful to package some files inside a gem, to be read by user
2465
+ code. This is of course trivial for Ruby code files (just use <code>require</code>) but
2466
+ not trivial if you want, say, to include some CSS files in your gem for
2467
+ everyone to use. Olag provides a way to resolve the path of any file in any gem
2468
+ (basically replicating what <code>require</code> does). Here is a simple test of using
2469
+ this functionality:
2470
+ </p>
2471
+ <p>
2472
+ <div class="named_with_containers chunk">
2473
+ <div class="chunk name">
2474
+ <a name="test-access-data-files-rb">
2475
+ <span>test/access_data_files.rb</span>
2476
+ </a>
2477
+ </div>
2478
+ <div class="chunk html">
2479
+ <pre class='ruby code syntax'>
2480
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/data_files</span><span class="Special">&quot;</span>
2481
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">test/spec</span><span class="Special">&quot;</span>
2482
+
2483
+ </pre>
2484
+ <table class='layout'>
2485
+ <tr>
2486
+ <td class='indentation'>
2487
+ <pre></pre>
2488
+ </td>
2489
+ <td class='html'>
2490
+ <div class='rdoc comment markup'>
2491
+ <p>
2492
+ Test accessing data files packages with the gem.
2493
+ </p>
2494
+ </div>
2495
+ </td>
2496
+ </tr>
2497
+ </table>
2498
+ <pre class='ruby code syntax'>
2499
+ <span class="PreProc">class</span> <span class="Type">TestAccessDataFiles</span> &lt; <span class="Type">Test</span>::<span class="Type">Unit</span>::<span class="Type">TestCase</span>
2500
+
2501
+ <span class="PreProc">def</span> <span class="Identifier">test_access_data_file</span>
2502
+ <span class="Type">File</span>.exist?(<span class="Type">Olag</span>::<span class="Type">DataFiles</span>.expand_path(<span class="Special">&quot;</span><span class="Constant">olag/data_files.rb</span><span class="Special">&quot;</span>)).should == <span class="Constant">true</span>
2503
+ <span class="PreProc">end</span>
2504
+
2505
+ <span class="PreProc">def</span> <span class="Identifier">test_access_missing_file</span>
2506
+ <span class="Type">Olag</span>::<span class="Type">DataFiles</span>.expand_path(<span class="Special">&quot;</span><span class="Constant">no-such-file</span><span class="Special">&quot;</span>).should == <span class="Special">&quot;</span><span class="Constant">no-such-file</span><span class="Special">&quot;</span>
2507
+ <span class="PreProc">end</span>
2508
+
2509
+ <span class="PreProc">end</span>
2510
+ </pre>
2511
+ </div>
2512
+ </div>
2513
+ </p>
2514
+ <p>
2515
+ And here is the implementation:
2516
+ </p>
2517
+ <p>
2518
+ <div class="named_with_containers chunk">
2519
+ <div class="chunk name">
2520
+ <a name="lib-olag-data-files-rb">
2521
+ <span>lib/olag/data_files.rb</span>
2522
+ </a>
2523
+ </div>
2524
+ <div class="chunk html">
2525
+ <pre class='ruby code syntax'>
2526
+ <span class="PreProc">module</span> <span class="Type">Olag</span>
2527
+
2528
+ </pre>
2529
+ <table class='layout'>
2530
+ <tr>
2531
+ <td class='indentation'>
2532
+ <pre> </pre>
2533
+ </td>
2534
+ <td class='html'>
2535
+ <div class='rdoc comment markup'>
2536
+ <p>
2537
+ Provide access to data files packaged with the gem.
2538
+ </p>
2539
+ </div>
2540
+ </td>
2541
+ </tr>
2542
+ </table>
2543
+ <pre class='ruby code syntax'>
2544
+ <span class="PreProc">module</span> <span class="Type">DataFiles</span>
2545
+
2546
+ </pre>
2547
+ <table class='layout'>
2548
+ <tr>
2549
+ <td class='indentation'>
2550
+ <pre> </pre>
2551
+ </td>
2552
+ <td class='html'>
2553
+ <div class='rdoc comment markup'>
2554
+ <p>
2555
+ Given the name of a data file packaged in some gem, return the absolute
2556
+ disk path for accessing that data file. This is similar to what
2557
+ <tt>require</tt> does internally, but here we just want the path for
2558
+ reading data, rather than load the Ruby code in the file.
2559
+ </p>
2560
+ </div>
2561
+ </td>
2562
+ </tr>
2563
+ </table>
2564
+ <pre class='ruby code syntax'>
2565
+ <span class="PreProc">def</span> <span class="Constant">self</span>.<span class="Identifier">expand_path</span>(relative_path)
2566
+ <span class="Identifier">$LOAD_PATH</span>.each <span class="Statement">do</span> |<span class="Identifier">load_directory</span>|
2567
+ absolute_path = <span class="Type">File</span>.expand_path(load_directory + <span class="Special">&quot;</span><span class="Constant">/</span><span class="Special">&quot;</span> + relative_path)
2568
+ <span class="Statement">return</span> absolute_path <span class="Statement">if</span> <span class="Type">File</span>.exist?(absolute_path)
2569
+ <span class="Statement">end</span>
2570
+ <span class="Statement">return</span> relative_path <span class="Comment"># This will cause &quot;file not found error&quot; down the line.</span>
2571
+ <span class="PreProc">end</span>
2572
+
2573
+ end
2574
+
2575
+ end
2576
+ </pre>
2577
+ </div>
2578
+ </div>
2579
+ </p>
2580
+ <h3>Simulating objects with Hash tables</h3>
2581
+ <p>
2582
+ Javascript has an interesting convention where <code>hash["key"]</code> and <code>hash.key</code>
2583
+ mean the same thing. This is very useful in cutting down boilerplate code, and
2584
+ it also makes your data serialize to very clean YAML. Unlike OpenStruct, you do
2585
+ not need to define all the members in advance, and you can alternate between
2586
+ the <code>.key</code> and <code>["key"]</code> forms as convenient for any particular piece of code.
2587
+ The down side is that you lose any semblance of type checking - misspelled
2588
+ member names and other errors are silently ignored. Well, that's what we have
2589
+ unit tests for, right? :-)
2590
+ </p>
2591
+ <p>
2592
+ Olag provides an extension to the Hash class that provides the above, for these
2593
+ who have chosen to follow the dark side of the force. Here is a simple test
2594
+ demonstrating using this ability:
2595
+ </p>
2596
+ <p>
2597
+ <div class="named_with_containers chunk">
2598
+ <div class="chunk name">
2599
+ <a name="test-missing-keys-rb">
2600
+ <span>test/missing_keys.rb</span>
2601
+ </a>
2602
+ </div>
2603
+ <div class="chunk html">
2604
+ <pre class='ruby code syntax'>
2605
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/hash_struct</span><span class="Special">&quot;</span>
2606
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">test/spec</span><span class="Special">&quot;</span>
2607
+
2608
+ </pre>
2609
+ <table class='layout'>
2610
+ <tr>
2611
+ <td class='indentation'>
2612
+ <pre></pre>
2613
+ </td>
2614
+ <td class='html'>
2615
+ <div class='rdoc comment markup'>
2616
+ <p>
2617
+ Test accessing missing keys as members.
2618
+ </p>
2619
+ </div>
2620
+ </td>
2621
+ </tr>
2622
+ </table>
2623
+ <pre class='ruby code syntax'>
2624
+ <span class="PreProc">class</span> <span class="Type">TestMissingKeys</span> &lt; ::<span class="Type">Test</span>::<span class="Type">Unit</span>::<span class="Type">TestCase</span>
2625
+
2626
+ <span class="PreProc">def</span> <span class="Identifier">test_read_missing_key</span>
2627
+ {}.missing.should == <span class="Constant">nil</span>
2628
+ <span class="PreProc">end</span>
2629
+
2630
+ <span class="PreProc">def</span> <span class="Identifier">test_set_missing_key</span>
2631
+ hash = {}
2632
+ hash.missing = <span class="Special">&quot;</span><span class="Constant">value</span><span class="Special">&quot;</span>
2633
+ hash.missing.should == <span class="Special">&quot;</span><span class="Constant">value</span><span class="Special">&quot;</span>
2634
+ <span class="PreProc">end</span>
2635
+
2636
+ <span class="PreProc">end</span>
2637
+ </pre>
2638
+ </div>
2639
+ </div>
2640
+ </p>
2641
+ <p>
2642
+ And here is the implementation:
2643
+ </p>
2644
+ <p>
2645
+ <div class="named_with_containers chunk">
2646
+ <div class="chunk name">
2647
+ <a name="lib-olag-hash-struct-rb">
2648
+ <span>lib/olag/hash_struct.rb</span>
2649
+ </a>
2650
+ </div>
2651
+ <div class="chunk html">
2652
+ <table class='layout'>
2653
+ <tr>
2654
+ <td class='indentation'>
2655
+ <pre></pre>
2656
+ </td>
2657
+ <td class='html'>
2658
+ <div class='rdoc comment markup'>
2659
+ <p>
2660
+ Extend the core Hash class.
2661
+ </p>
2662
+ </div>
2663
+ </td>
2664
+ </tr>
2665
+ </table>
2666
+ <pre class='ruby code syntax'>
2667
+ <span class="PreProc">class</span> <span class="Type">Hash</span>
2668
+
2669
+ </pre>
2670
+ <table class='layout'>
2671
+ <tr>
2672
+ <td class='indentation'>
2673
+ <pre> </pre>
2674
+ </td>
2675
+ <td class='html'>
2676
+ <div class='rdoc comment markup'>
2677
+ <p>
2678
+ Provide OpenStruct/JavaScript-like implicit <tt>.key</tt> and
2679
+ <tt>.key=</tt> methods.
2680
+ </p>
2681
+ </div>
2682
+ </td>
2683
+ </tr>
2684
+ </table>
2685
+ <pre class='ruby code syntax'>
2686
+ <span class="PreProc">def</span> <span class="Identifier">method_missing</span>(method, *arguments)
2687
+ method = method.to_s
2688
+ key = method.chomp(<span class="Special">&quot;</span><span class="Constant">=</span><span class="Special">&quot;</span>)
2689
+ <span class="Statement">return</span> method == key ? <span class="Constant">self</span>[key] : <span class="Constant">self</span>[key] = arguments[<span class="Constant">0</span>]
2690
+ <span class="PreProc">end</span>
2691
+
2692
+ end
2693
+ </pre>
2694
+ </div>
2695
+ </div>
2696
+ </p>
2697
+ <h3>Collecting errors</h3>
2698
+ <p>
2699
+ In library code, it is bad practice to terminate the program on an error.
2700
+ Raising an exception is preferrable, but that forces you to abort the
2701
+ processing. In some cases, it is preferrable to collect the error, skip a bit
2702
+ of processing, and continue (if only for detecting additional errors). For
2703
+ example, one would expect a compiler to emit more than just the first syntax
2704
+ error message.
2705
+ </p>
2706
+ <p>
2707
+ Olag provides an error collection class that also automatically formats the
2708
+ error to indicate its location. Here is a simple test that demonstrates
2709
+ collecting errors:
2710
+ </p>
2711
+ <p>
2712
+ <div class="named_with_containers chunk">
2713
+ <div class="chunk name">
2714
+ <a name="test-collect-errors-rb">
2715
+ <span>test/collect_errors.rb</span>
2716
+ </a>
2717
+ </div>
2718
+ <div class="chunk html">
2719
+ <pre class='ruby code syntax'>
2720
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/errors</span><span class="Special">&quot;</span>
2721
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/test</span><span class="Special">&quot;</span>
2722
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">test/spec</span><span class="Special">&quot;</span>
2723
+
2724
+ </pre>
2725
+ <table class='layout'>
2726
+ <tr>
2727
+ <td class='indentation'>
2728
+ <pre></pre>
2729
+ </td>
2730
+ <td class='html'>
2731
+ <div class='rdoc comment markup'>
2732
+ <p>
2733
+ Test collecting errors.
2734
+ </p>
2735
+ </div>
2736
+ </td>
2737
+ </tr>
2738
+ </table>
2739
+ <pre class='ruby code syntax'>
2740
+ <span class="PreProc">class</span> <span class="Type">TestCollectErrors</span> &lt; <span class="Type">Test</span>::<span class="Type">Unit</span>::<span class="Type">TestCase</span>
2741
+
2742
+ <span class="PreProc">include</span> <span class="Type">Test</span>::<span class="Type">WithErrors</span>
2743
+
2744
+ <span class="PreProc">def</span> <span class="Identifier">test_one_error</span>
2745
+ <span class="Identifier">@errors</span> &lt;&lt; <span class="Special">&quot;</span><span class="Constant">Oops</span><span class="Special">&quot;</span>
2746
+ <span class="Identifier">@errors</span>.should == [ <span class="Special">&quot;</span><span class="Special">#{</span><span class="Identifier">$0</span><span class="Special">}</span><span class="Constant">: Oops</span><span class="Special">&quot;</span> ]
2747
+ <span class="PreProc">end</span>
2748
+
2749
+ <span class="PreProc">def</span> <span class="Identifier">test_path_error</span>
2750
+ <span class="Identifier">@errors</span>.in_path(<span class="Special">&quot;</span><span class="Constant">foo</span><span class="Special">&quot;</span>) <span class="Statement">do</span>
2751
+ <span class="Identifier">@errors</span> &lt;&lt; <span class="Special">&quot;</span><span class="Constant">Eeek</span><span class="Special">&quot;</span>
2752
+ <span class="Special">&quot;</span><span class="Constant">result</span><span class="Special">&quot;</span>
2753
+ <span class="Statement">end</span>.should == <span class="Special">&quot;</span><span class="Constant">result</span><span class="Special">&quot;</span>
2754
+ <span class="Identifier">@errors</span> &lt;&lt; <span class="Special">&quot;</span><span class="Constant">Oops</span><span class="Special">&quot;</span>
2755
+ <span class="Identifier">@errors</span>.should == [ <span class="Special">&quot;</span><span class="Special">#{</span><span class="Identifier">$0</span><span class="Special">}</span><span class="Constant">: Eeek in file: foo</span><span class="Special">&quot;</span>, <span class="Special">&quot;</span><span class="Special">#{</span><span class="Identifier">$0</span><span class="Special">}</span><span class="Constant">: Oops</span><span class="Special">&quot;</span> ]
2756
+ <span class="PreProc">end</span>
2757
+
2758
+ <span class="PreProc">def</span> <span class="Identifier">test_line_error</span>
2759
+ <span class="Identifier">@errors</span>.in_path(<span class="Special">&quot;</span><span class="Constant">foo</span><span class="Special">&quot;</span>) <span class="Statement">do</span>
2760
+ <span class="Identifier">@errors</span>.at_line(<span class="Constant">1</span>)
2761
+ <span class="Identifier">@errors</span> &lt;&lt; <span class="Special">&quot;</span><span class="Constant">Eeek</span><span class="Special">&quot;</span>
2762
+ <span class="Statement">end</span>
2763
+ <span class="Identifier">@errors</span> &lt;&lt; <span class="Special">&quot;</span><span class="Constant">Oops</span><span class="Special">&quot;</span>
2764
+ <span class="Identifier">@errors</span>.should == [ <span class="Special">&quot;</span><span class="Special">#{</span><span class="Identifier">$0</span><span class="Special">}</span><span class="Constant">: Eeek in file: foo at line: 1</span><span class="Special">&quot;</span>, <span class="Special">&quot;</span><span class="Special">#{</span><span class="Identifier">$0</span><span class="Special">}</span><span class="Constant">: Oops</span><span class="Special">&quot;</span> ]
2765
+ <span class="PreProc">end</span>
2766
+
2767
+ <span class="PreProc">end</span>
2768
+ </pre>
2769
+ </div>
2770
+ </div>
2771
+ </p>
2772
+ <p>
2773
+ Which uses a mix-in that helps writing tests that use errors:
2774
+ </p>
2775
+ <p>
2776
+ <div class="named_with_containers chunk">
2777
+ <div class="chunk name">
2778
+ <a name="lib-olag-test-with-errors-rb">
2779
+ <span>lib/olag/test/with_errors.rb</span>
2780
+ </a>
2781
+ </div>
2782
+ <div class="chunk html">
2783
+ <pre class='ruby code syntax'>
2784
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/errors</span><span class="Special">&quot;</span>
2785
+
2786
+ <span class="PreProc">module</span> <span class="Type">Test</span>
2787
+
2788
+ </pre>
2789
+ <table class='layout'>
2790
+ <tr>
2791
+ <td class='indentation'>
2792
+ <pre> </pre>
2793
+ </td>
2794
+ <td class='html'>
2795
+ <div class='rdoc comment markup'>
2796
+ <p>
2797
+ Mix-in for tests that collect Errors.
2798
+ </p>
2799
+ </div>
2800
+ </td>
2801
+ </tr>
2802
+ </table>
2803
+ <pre class='ruby code syntax'>
2804
+ <span class="PreProc">module</span> <span class="Type">WithErrors</span>
2805
+
2806
+ </pre>
2807
+ <table class='layout'>
2808
+ <tr>
2809
+ <td class='indentation'>
2810
+ <pre> </pre>
2811
+ </td>
2812
+ <td class='html'>
2813
+ <div class='rdoc comment markup'>
2814
+ <p>
2815
+ Aliasing methods needs to be deferred to when the module is included and be
2816
+ executed in the context of the class.
2817
+ </p>
2818
+ </div>
2819
+ </td>
2820
+ </tr>
2821
+ </table>
2822
+ <pre class='ruby code syntax'>
2823
+ <span class="PreProc">def</span> <span class="Constant">self</span>.<span class="Identifier">included</span>(base)
2824
+ base.class_eval <span class="Statement">do</span>
2825
+
2826
+ alias_method <span class="Constant">:errors_original_setup</span>, <span class="Constant">:setup</span>
2827
+
2828
+ </pre>
2829
+ <table class='layout'>
2830
+ <tr>
2831
+ <td class='indentation'>
2832
+ <pre> </pre>
2833
+ </td>
2834
+ <td class='html'>
2835
+ <div class='rdoc comment markup'>
2836
+ <p>
2837
+ Automatically create an fresh +@errors+ data member for each test.
2838
+ </p>
2839
+ </div>
2840
+ </td>
2841
+ </tr>
2842
+ </table>
2843
+ <pre class='ruby code syntax'>
2844
+ <span class="PreProc">def</span> <span class="Identifier">setup</span>
2845
+ errors_original_setup
2846
+ <span class="Identifier">@errors</span> = <span class="Type">Olag</span>::<span class="Type">Errors</span>.new
2847
+ <span class="PreProc">end</span>
2848
+
2849
+ end
2850
+ end
2851
+
2852
+ end
2853
+
2854
+ end
2855
+ </pre>
2856
+ </div>
2857
+ </div>
2858
+ </p>
2859
+ <p>
2860
+ Here is the actual implementation:
2861
+ </p>
2862
+ <p>
2863
+ <div class="named_with_containers chunk">
2864
+ <div class="chunk name">
2865
+ <a name="lib-olag-errors-rb">
2866
+ <span>lib/olag/errors.rb</span>
2867
+ </a>
2868
+ </div>
2869
+ <div class="chunk html">
2870
+ <pre class='ruby code syntax'>
2871
+ <span class="PreProc">module</span> <span class="Type">Olag</span>
2872
+
2873
+ </pre>
2874
+ <table class='layout'>
2875
+ <tr>
2876
+ <td class='indentation'>
2877
+ <pre> </pre>
2878
+ </td>
2879
+ <td class='html'>
2880
+ <div class='rdoc comment markup'>
2881
+ <p>
2882
+ Collect a list of errors.
2883
+ </p>
2884
+ </div>
2885
+ </td>
2886
+ </tr>
2887
+ </table>
2888
+ <pre class='ruby code syntax'>
2889
+ <span class="PreProc">class</span> <span class="Type">Errors</span> &lt; <span class="Type">Array</span>
2890
+
2891
+ </pre>
2892
+ <table class='layout'>
2893
+ <tr>
2894
+ <td class='indentation'>
2895
+ <pre> </pre>
2896
+ </td>
2897
+ <td class='html'>
2898
+ <div class='rdoc comment markup'>
2899
+ <p>
2900
+ Create an empty errors collection.
2901
+ </p>
2902
+ </div>
2903
+ </td>
2904
+ </tr>
2905
+ </table>
2906
+ <pre class='ruby code syntax'>
2907
+ <span class="PreProc">def</span> <span class="Identifier">initialize</span>
2908
+ <span class="Identifier">@path</span> = <span class="Constant">nil</span>
2909
+ <span class="Identifier">@line</span> = <span class="Constant">nil</span>
2910
+ <span class="PreProc">end</span>
2911
+
2912
+ </pre>
2913
+ <table class='layout'>
2914
+ <tr>
2915
+ <td class='indentation'>
2916
+ <pre> </pre>
2917
+ </td>
2918
+ <td class='html'>
2919
+ <div class='rdoc comment markup'>
2920
+ <p>
2921
+ Associate all errors collected by a block with a specific disk file.
2922
+ </p>
2923
+ </div>
2924
+ </td>
2925
+ </tr>
2926
+ </table>
2927
+ <pre class='ruby code syntax'>
2928
+ <span class="PreProc">def</span> <span class="Identifier">in_path</span>(path, &amp;block)
2929
+ prev_path, prev_line = <span class="Identifier">@path</span>, <span class="Identifier">@line</span>
2930
+ <span class="Identifier">@path</span>, <span class="Identifier">@line</span> = path, <span class="Constant">nil</span>
2931
+ result = block.call
2932
+ <span class="Identifier">@path</span>, <span class="Identifier">@line</span> = prev_path, prev_line
2933
+ <span class="Statement">return</span> result
2934
+ <span class="PreProc">end</span>
2935
+
2936
+ </pre>
2937
+ <table class='layout'>
2938
+ <tr>
2939
+ <td class='indentation'>
2940
+ <pre> </pre>
2941
+ </td>
2942
+ <td class='html'>
2943
+ <div class='rdoc comment markup'>
2944
+ <p>
2945
+ Set the line number for any errors collected from here on.
2946
+ </p>
2947
+ </div>
2948
+ </td>
2949
+ </tr>
2950
+ </table>
2951
+ <pre class='ruby code syntax'>
2952
+ <span class="PreProc">def</span> <span class="Identifier">at_line</span>(line)
2953
+ <span class="Identifier">@line</span> = line
2954
+ <span class="PreProc">end</span>
2955
+
2956
+ </pre>
2957
+ <table class='layout'>
2958
+ <tr>
2959
+ <td class='indentation'>
2960
+ <pre> </pre>
2961
+ </td>
2962
+ <td class='html'>
2963
+ <div class='rdoc comment markup'>
2964
+ <p>
2965
+ Add a single error to the collection, with automatic context annotation
2966
+ (current disk file and line). Other methods (push, += etc.) do not
2967
+ automatically add the context annotation.
2968
+ </p>
2969
+ </div>
2970
+ </td>
2971
+ </tr>
2972
+ </table>
2973
+ <pre class='ruby code syntax'>
2974
+ <span class="PreProc">def</span> <span class="Identifier">&lt;&lt;</span>(message)
2975
+ push(annotate_error_message(message))
2976
+ <span class="PreProc">end</span>
2977
+
2978
+ <span class="Statement">protected</span>
2979
+
2980
+ </pre>
2981
+ <table class='layout'>
2982
+ <tr>
2983
+ <td class='indentation'>
2984
+ <pre> </pre>
2985
+ </td>
2986
+ <td class='html'>
2987
+ <div class='rdoc comment markup'>
2988
+ <p>
2989
+ Annotate an error message with the context (current file and line).
2990
+ </p>
2991
+ </div>
2992
+ </td>
2993
+ </tr>
2994
+ </table>
2995
+ <pre class='ruby code syntax'>
2996
+ <span class="PreProc">def</span> <span class="Identifier">annotate_error_message</span>(message)
2997
+ <span class="Statement">return</span> <span class="Special">&quot;</span><span class="Special">#{</span><span class="Identifier">$0</span><span class="Special">}</span><span class="Constant">: </span><span class="Special">#{</span>message<span class="Special">}</span><span class="Special">&quot;</span> <span class="Statement">unless</span> <span class="Identifier">@path</span>
2998
+ <span class="Statement">return</span> <span class="Special">&quot;</span><span class="Special">#{</span><span class="Identifier">$0</span><span class="Special">}</span><span class="Constant">: </span><span class="Special">#{</span>message<span class="Special">}</span><span class="Constant"> in file: </span><span class="Special">#{</span><span class="Identifier">@path</span><span class="Special">}</span><span class="Special">&quot;</span> <span class="Statement">unless</span> <span class="Identifier">@line</span>
2999
+ <span class="Statement">return</span> <span class="Special">&quot;</span><span class="Special">#{</span><span class="Identifier">$0</span><span class="Special">}</span><span class="Constant">: </span><span class="Special">#{</span>message<span class="Special">}</span><span class="Constant"> in file: </span><span class="Special">#{</span><span class="Identifier">@path</span><span class="Special">}</span><span class="Constant"> at line: </span><span class="Special">#{</span><span class="Identifier">@line</span><span class="Special">}</span><span class="Special">&quot;</span>
3000
+ <span class="PreProc">end</span>
3001
+
3002
+ end
3003
+
3004
+ end
3005
+ </pre>
3006
+ </div>
3007
+ </div>
3008
+ </p>
3009
+ <h3>Testing with a fake file system</h3>
3010
+ <p>
3011
+ Sometimes tests need to muck around with disk files. One way to go about it is
3012
+ to create a temporary disk directory, work in there, and clean it up when done.
3013
+ Another, simpler way is to use the FakeFS file system, which captures all(most)
3014
+ of Ruby's file operations and redirect them to an in-memory fake file system.
3015
+ Here is a mix-in that helps writing tests using FakeFS (we will use it below
3016
+ when running applications inside unit tests):
3017
+ </p>
3018
+ <p>
3019
+ <div class="named_with_containers chunk">
3020
+ <div class="chunk name">
3021
+ <a name="lib-olag-test-with-fakefs-rb">
3022
+ <span>lib/olag/test/with_fakefs.rb</span>
3023
+ </a>
3024
+ </div>
3025
+ <div class="chunk html">
3026
+ <pre class='ruby code syntax'>
3027
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">fakefs/safe</span><span class="Special">&quot;</span>
3028
+
3029
+ <span class="PreProc">module</span> <span class="Type">Test</span>
3030
+
3031
+ </pre>
3032
+ <table class='layout'>
3033
+ <tr>
3034
+ <td class='indentation'>
3035
+ <pre> </pre>
3036
+ </td>
3037
+ <td class='html'>
3038
+ <div class='rdoc comment markup'>
3039
+ <p>
3040
+ Mix-in for tests that use the FakeFS fake file system.
3041
+ </p>
3042
+ </div>
3043
+ </td>
3044
+ </tr>
3045
+ </table>
3046
+ <pre class='ruby code syntax'>
3047
+ <span class="PreProc">module</span> <span class="Type">WithFakeFS</span>
3048
+
3049
+ </pre>
3050
+ <table class='layout'>
3051
+ <tr>
3052
+ <td class='indentation'>
3053
+ <pre> </pre>
3054
+ </td>
3055
+ <td class='html'>
3056
+ <div class='rdoc comment markup'>
3057
+ <p>
3058
+ Aliasing methods needs to be deferred to when the module is included and be
3059
+ executed in the context of the class.
3060
+ </p>
3061
+ </div>
3062
+ </td>
3063
+ </tr>
3064
+ </table>
3065
+ <pre class='ruby code syntax'>
3066
+ <span class="PreProc">def</span> <span class="Constant">self</span>.<span class="Identifier">included</span>(base)
3067
+ base.class_eval <span class="Statement">do</span>
3068
+
3069
+ alias_method <span class="Constant">:fakefs_original_setup</span>, <span class="Constant">:setup</span>
3070
+
3071
+ </pre>
3072
+ <table class='layout'>
3073
+ <tr>
3074
+ <td class='indentation'>
3075
+ <pre> </pre>
3076
+ </td>
3077
+ <td class='html'>
3078
+ <div class='rdoc comment markup'>
3079
+ <p>
3080
+ Automatically create an fresh fake file system for each test.
3081
+ </p>
3082
+ </div>
3083
+ </td>
3084
+ </tr>
3085
+ </table>
3086
+ <pre class='ruby code syntax'>
3087
+ <span class="PreProc">def</span> <span class="Identifier">setup</span>
3088
+ fakefs_original_setup
3089
+ <span class="Type">FakeFS</span>.activate!
3090
+ <span class="Type">FakeFS</span>::<span class="Type">FileSystem</span>.clear
3091
+ <span class="PreProc">end</span>
3092
+
3093
+ alias_method <span class="Constant">:fakefs_original_teardown</span>, <span class="Constant">:teardown</span>
3094
+
3095
+ </pre>
3096
+ <table class='layout'>
3097
+ <tr>
3098
+ <td class='indentation'>
3099
+ <pre> </pre>
3100
+ </td>
3101
+ <td class='html'>
3102
+ <div class='rdoc comment markup'>
3103
+ <p>
3104
+ Automatically clean up the fake file system at the end of each test.
3105
+ </p>
3106
+ </div>
3107
+ </td>
3108
+ </tr>
3109
+ </table>
3110
+ <pre class='ruby code syntax'>
3111
+ <span class="PreProc">def</span> <span class="Identifier">teardown</span>
3112
+ fakefs_original_teardown
3113
+ <span class="Type">FakeFS</span>.deactivate!
3114
+ <span class="PreProc">end</span>
3115
+
3116
+ end
3117
+
3118
+ end
3119
+
3120
+ end
3121
+
3122
+ end
3123
+ </pre>
3124
+ </div>
3125
+ </div>
3126
+ </p>
3127
+ <h3>Testing with a temporary file</h3>
3128
+ <p>
3129
+ When running external programs, actually generating a temporary disk file is
3130
+ sometimes inevitable. Of course, such files need to be cleaned up when the test
3131
+ is done. If we need more than just one such file, it is easier to create a
3132
+ whole temporary directory which is easily cleaned up in one operation.
3133
+ </p>
3134
+ <p>
3135
+ Here is a mix-in that helps writing tests using temporary files and folders:
3136
+ </p>
3137
+ <p>
3138
+ <div class="named_with_containers chunk">
3139
+ <div class="chunk name">
3140
+ <a name="lib-olag-test-with-tempfile-rb">
3141
+ <span>lib/olag/test/with_tempfile.rb</span>
3142
+ </a>
3143
+ </div>
3144
+ <div class="chunk html">
3145
+ <pre class='ruby code syntax'>
3146
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">fileutils</span><span class="Special">&quot;</span>
3147
+
3148
+ <span class="PreProc">module</span> <span class="Type">Test</span>
3149
+
3150
+ </pre>
3151
+ <table class='layout'>
3152
+ <tr>
3153
+ <td class='indentation'>
3154
+ <pre> </pre>
3155
+ </td>
3156
+ <td class='html'>
3157
+ <div class='rdoc comment markup'>
3158
+ <p>
3159
+ Mix-in for tests that write a temporary disk file.
3160
+ </p>
3161
+ </div>
3162
+ </td>
3163
+ </tr>
3164
+ </table>
3165
+ <pre class='ruby code syntax'>
3166
+ <span class="PreProc">module</span> <span class="Type">WithTempfile</span>
3167
+
3168
+ </pre>
3169
+ <table class='layout'>
3170
+ <tr>
3171
+ <td class='indentation'>
3172
+ <pre> </pre>
3173
+ </td>
3174
+ <td class='html'>
3175
+ <div class='rdoc comment markup'>
3176
+ <p>
3177
+ Create a temporary file on the disk. The file will be automatically removed
3178
+ when the test is done.
3179
+ </p>
3180
+ </div>
3181
+ </td>
3182
+ </tr>
3183
+ </table>
3184
+ <pre class='ruby code syntax'>
3185
+ <span class="PreProc">def</span> <span class="Identifier">write_tempfile</span>(path, content, directory = <span class="Special">&quot;</span><span class="Constant">.</span><span class="Special">&quot;</span>)
3186
+ file = <span class="Type">Tempfile</span>.open(path, directory)
3187
+ file.write(content)
3188
+ file.close(<span class="Constant">false</span>)
3189
+ (<span class="Identifier">@tempfiles</span> ||= []) &lt;&lt; (path = file.path)
3190
+ <span class="Statement">return</span> path
3191
+ <span class="PreProc">end</span>
3192
+
3193
+ </pre>
3194
+ <table class='layout'>
3195
+ <tr>
3196
+ <td class='indentation'>
3197
+ <pre> </pre>
3198
+ </td>
3199
+ <td class='html'>
3200
+ <div class='rdoc comment markup'>
3201
+ <p>
3202
+ Create a temporary directory on the disk. The directory will be
3203
+ automatically removed when the test is done. This is very useful for
3204
+ complex file tests that can’t use FakeFS.
3205
+ </p>
3206
+ </div>
3207
+ </td>
3208
+ </tr>
3209
+ </table>
3210
+ <pre class='ruby code syntax'>
3211
+ <span class="PreProc">def</span> <span class="Identifier">create_tempdir</span>(directory = <span class="Special">&quot;</span><span class="Constant">.</span><span class="Special">&quot;</span>)
3212
+ file = <span class="Type">Tempfile</span>.open(<span class="Special">&quot;</span><span class="Constant">dir</span><span class="Special">&quot;</span>, directory)
3213
+ (<span class="Identifier">@tempfiles</span> ||= []) &lt;&lt; (path = file.path)
3214
+ <span class="Type">File</span>.delete(path)
3215
+ <span class="Type">Dir</span>.mkdir(path)
3216
+ <span class="Statement">return</span> path
3217
+ <span class="PreProc">end</span>
3218
+
3219
+ </pre>
3220
+ <table class='layout'>
3221
+ <tr>
3222
+ <td class='indentation'>
3223
+ <pre> </pre>
3224
+ </td>
3225
+ <td class='html'>
3226
+ <div class='rdoc comment markup'>
3227
+ <p>
3228
+ Aliasing methods needs to be deferred to when the module is included and be
3229
+ executed in the context of the class.
3230
+ </p>
3231
+ </div>
3232
+ </td>
3233
+ </tr>
3234
+ </table>
3235
+ <pre class='ruby code syntax'>
3236
+ <span class="PreProc">def</span> <span class="Constant">self</span>.<span class="Identifier">included</span>(base)
3237
+ base.class_eval <span class="Statement">do</span>
3238
+
3239
+ alias_method <span class="Constant">:tempfile_original_teardown</span>, <span class="Constant">:teardown</span>
3240
+
3241
+ </pre>
3242
+ <table class='layout'>
3243
+ <tr>
3244
+ <td class='indentation'>
3245
+ <pre> </pre>
3246
+ </td>
3247
+ <td class='html'>
3248
+ <div class='rdoc comment markup'>
3249
+ <p>
3250
+ Automatically clean up the temporary files when the test is done.
3251
+ </p>
3252
+ </div>
3253
+ </td>
3254
+ </tr>
3255
+ </table>
3256
+ <pre class='ruby code syntax'>
3257
+ <span class="PreProc">def</span> <span class="Identifier">teardown</span>
3258
+ tempfile_original_teardown
3259
+ (<span class="Identifier">@tempfiles</span> || []).each <span class="Statement">do</span> |<span class="Identifier">tempfile</span>|
3260
+ <span class="Type">FileUtils</span>.rm_rf(tempfile) <span class="Statement">if</span> <span class="Type">File</span>.exist?(tempfile)
3261
+ <span class="Statement">end</span>
3262
+ <span class="PreProc">end</span>
3263
+
3264
+ end
3265
+
3266
+ end
3267
+ end
3268
+
3269
+ end
3270
+ </pre>
3271
+ </div>
3272
+ </div>
3273
+ </p>
3274
+ <h3>Testing Rake tasks</h3>
3275
+ <p>
3276
+ Testing Rake tasks is tricky because tests may be run in the context of Rake.
3277
+ Therefore, the best practice is to create a new Rake application and restore
3278
+ the original when the test is done:
3279
+ </p>
3280
+ <p>
3281
+ <div class="named_with_containers chunk">
3282
+ <div class="chunk name">
3283
+ <a name="lib-olag-test-with-rake-rb">
3284
+ <span>lib/olag/test/with_rake.rb</span>
3285
+ </a>
3286
+ </div>
3287
+ <div class="chunk html">
3288
+ <pre class='ruby code syntax'>
3289
+ <span class="PreProc">module</span> <span class="Type">Test</span>
3290
+
3291
+ </pre>
3292
+ <table class='layout'>
3293
+ <tr>
3294
+ <td class='indentation'>
3295
+ <pre> </pre>
3296
+ </td>
3297
+ <td class='html'>
3298
+ <div class='rdoc comment markup'>
3299
+ <p>
3300
+ Mix-in for tests that use Rake.
3301
+ </p>
3302
+ </div>
3303
+ </td>
3304
+ </tr>
3305
+ </table>
3306
+ <pre class='ruby code syntax'>
3307
+ <span class="PreProc">module</span> <span class="Type">WithRake</span>
3308
+
3309
+ </pre>
3310
+ <table class='layout'>
3311
+ <tr>
3312
+ <td class='indentation'>
3313
+ <pre> </pre>
3314
+ </td>
3315
+ <td class='html'>
3316
+ <div class='rdoc comment markup'>
3317
+ <p>
3318
+ Aliasing methods needs to be deferred to when the module is included and be
3319
+ executed in the context of the class.
3320
+ </p>
3321
+ </div>
3322
+ </td>
3323
+ </tr>
3324
+ </table>
3325
+ <pre class='ruby code syntax'>
3326
+ <span class="PreProc">def</span> <span class="Constant">self</span>.<span class="Identifier">included</span>(base)
3327
+ base.class_eval <span class="Statement">do</span>
3328
+
3329
+ alias_method <span class="Constant">:rake_original_setup</span>, <span class="Constant">:setup</span>
3330
+
3331
+ </pre>
3332
+ <table class='layout'>
3333
+ <tr>
3334
+ <td class='indentation'>
3335
+ <pre> </pre>
3336
+ </td>
3337
+ <td class='html'>
3338
+ <div class='rdoc comment markup'>
3339
+ <p>
3340
+ Automatically create a fresh Rake application.
3341
+ </p>
3342
+ </div>
3343
+ </td>
3344
+ </tr>
3345
+ </table>
3346
+ <pre class='ruby code syntax'>
3347
+ <span class="PreProc">def</span> <span class="Identifier">setup</span>
3348
+ rake_original_setup
3349
+ <span class="Identifier">@original_rake</span> = <span class="Type">Rake</span>.application
3350
+ <span class="Identifier">@rake</span> = <span class="Type">Rake</span>::<span class="Type">Application</span>.new
3351
+ <span class="Type">Rake</span>.application = <span class="Identifier">@rake</span>
3352
+ <span class="PreProc">end</span>
3353
+
3354
+ alias_method <span class="Constant">:rake_original_teardown</span>, <span class="Constant">:teardown</span>
3355
+
3356
+ </pre>
3357
+ <table class='layout'>
3358
+ <tr>
3359
+ <td class='indentation'>
3360
+ <pre> </pre>
3361
+ </td>
3362
+ <td class='html'>
3363
+ <div class='rdoc comment markup'>
3364
+ <p>
3365
+ Automatically restore the original Rake application.
3366
+ </p>
3367
+ </div>
3368
+ </td>
3369
+ </tr>
3370
+ </table>
3371
+ <pre class='ruby code syntax'>
3372
+ <span class="PreProc">def</span> <span class="Identifier">teardown</span>
3373
+ rake_original_teardown
3374
+ <span class="Type">Rake</span>.application = <span class="Identifier">@original_rake</span>
3375
+ <span class="PreProc">end</span>
3376
+
3377
+ end
3378
+ end
3379
+
3380
+ end
3381
+
3382
+ end
3383
+ </pre>
3384
+ </div>
3385
+ </div>
3386
+ </p>
3387
+ <h3>Testing in general</h3>
3388
+ <p>
3389
+ Rather than requiring each of the above test mix-in modules on its own, it is
3390
+ convenient to just <code>require "olag/test"</code> and be done:
3391
+ </p>
3392
+ <p>
3393
+ <div class="named_with_containers chunk">
3394
+ <div class="chunk name">
3395
+ <a name="lib-olag-test-rb">
3396
+ <span>lib/olag/test.rb</span>
3397
+ </a>
3398
+ </div>
3399
+ <div class="chunk html">
3400
+ <pre class='ruby code syntax'>
3401
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/test/with_errors</span><span class="Special">&quot;</span>
3402
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/test/with_fakefs</span><span class="Special">&quot;</span>
3403
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/test/with_rake</span><span class="Special">&quot;</span>
3404
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/test/with_tempfile</span><span class="Special">&quot;</span>
3405
+
3406
+ </pre>
3407
+ <table class='layout'>
3408
+ <tr>
3409
+ <td class='indentation'>
3410
+ <pre></pre>
3411
+ </td>
3412
+ <td class='html'>
3413
+ <div class='rdoc comment markup'>
3414
+ <p>
3415
+ Enhance the global test module with additional mix-in modules.
3416
+ </p>
3417
+ </div>
3418
+ </td>
3419
+ </tr>
3420
+ </table>
3421
+ <pre class='ruby code syntax'>
3422
+ <span class="PreProc">module</span> <span class="Type">Test</span>
3423
+ <span class="PreProc">end</span>
3424
+ </pre>
3425
+ </div>
3426
+ </div>
3427
+ </p>
3428
+ <h2>Applications</h2>
3429
+ <p>
3430
+ Writing an application requires a lot of boilerplate. Olag provides an
3431
+ Application base class that handles standard command line flags, execution from
3432
+ within tests, and errors collection.
3433
+ </p>
3434
+ <p>
3435
+ Here is a simple test for running such an application from unit tests:
3436
+ </p>
3437
+ <p>
3438
+ <div class="named_with_containers chunk">
3439
+ <div class="chunk name">
3440
+ <a name="test-run-application-rb">
3441
+ <span>test/run_application.rb</span>
3442
+ </a>
3443
+ </div>
3444
+ <div class="chunk html">
3445
+ <pre class='ruby code syntax'>
3446
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/application</span><span class="Special">&quot;</span>
3447
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/test</span><span class="Special">&quot;</span>
3448
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">test/spec</span><span class="Special">&quot;</span>
3449
+
3450
+ </pre>
3451
+ <table class='layout'>
3452
+ <tr>
3453
+ <td class='indentation'>
3454
+ <pre></pre>
3455
+ </td>
3456
+ <td class='html'>
3457
+ <div class='rdoc comment markup'>
3458
+ <p>
3459
+ An application that emits an error when run.
3460
+ </p>
3461
+ </div>
3462
+ </td>
3463
+ </tr>
3464
+ </table>
3465
+ <pre class='ruby code syntax'>
3466
+ <span class="PreProc">class</span> <span class="Type">ErrorApplication</span> &lt; <span class="Type">Olag</span>::<span class="Type">Application</span>
3467
+
3468
+ </pre>
3469
+ <table class='layout'>
3470
+ <tr>
3471
+ <td class='indentation'>
3472
+ <pre> </pre>
3473
+ </td>
3474
+ <td class='html'>
3475
+ <div class='rdoc comment markup'>
3476
+ <p>
3477
+ Run the error application.
3478
+ </p>
3479
+ </div>
3480
+ </td>
3481
+ </tr>
3482
+ </table>
3483
+ <pre class='ruby code syntax'>
3484
+ <span class="PreProc">def</span> <span class="Identifier">run</span>
3485
+ <span class="Statement">super</span> { <span class="Identifier">@errors</span> &lt;&lt; <span class="Special">&quot;</span><span class="Constant">Oops!</span><span class="Special">&quot;</span> }
3486
+ <span class="PreProc">end</span>
3487
+
3488
+ end
3489
+
3490
+ </pre>
3491
+ <table class='layout'>
3492
+ <tr>
3493
+ <td class='indentation'>
3494
+ <pre></pre>
3495
+ </td>
3496
+ <td class='html'>
3497
+ <div class='rdoc comment markup'>
3498
+ <p>
3499
+ Test running a Olag Application.
3500
+ </p>
3501
+ </div>
3502
+ </td>
3503
+ </tr>
3504
+ </table>
3505
+ <pre class='ruby code syntax'>
3506
+ <span class="PreProc">class</span> <span class="Type">TestRunApplication</span> &lt; <span class="Type">Test</span>::<span class="Type">Unit</span>::<span class="Type">TestCase</span>
3507
+
3508
+ <span class="PreProc">include</span> <span class="Type">Test</span>::<span class="Type">WithFakeFS</span>
3509
+
3510
+ <span class="PreProc">def</span> <span class="Identifier">test_do_nothing</span>
3511
+ <span class="Type">Olag</span>::<span class="Type">Application</span>.with_argv([]) { <span class="Type">Olag</span>::<span class="Type">Application</span>.new(<span class="Constant">true</span>).run }.should == <span class="Constant">0</span>
3512
+ <span class="PreProc">end</span>
3513
+
3514
+ <span class="PreProc">def</span> <span class="Identifier">test_extra_arguments</span>
3515
+ <span class="Type">Olag</span>::<span class="Type">Application</span>.with_argv(<span class="Special">%w(</span><span class="Constant">-e stderr dummy</span><span class="Special">)</span>) { <span class="Type">Olag</span>::<span class="Type">Application</span>.new(<span class="Constant">true</span>).run }.should == <span class="Constant">1</span>
3516
+ <span class="Type">File</span>.read(<span class="Special">&quot;</span><span class="Constant">stderr</span><span class="Special">&quot;</span>).should.include?(<span class="Special">&quot;</span><span class="Constant">Expects no command line file arguments</span><span class="Special">&quot;</span>)
3517
+ <span class="PreProc">end</span>
3518
+
3519
+ <span class="PreProc">def</span> <span class="Identifier">test_print_version</span>
3520
+ <span class="Type">Olag</span>::<span class="Type">Application</span>.with_argv(<span class="Special">%w(</span><span class="Constant">-o nested/stdout -v -h</span><span class="Special">)</span>) { <span class="Type">Olag</span>::<span class="Type">Application</span>.new(<span class="Constant">true</span>).run }.should == <span class="Constant">0</span>
3521
+ <span class="Type">File</span>.read(<span class="Special">&quot;</span><span class="Constant">nested/stdout</span><span class="Special">&quot;</span>).should == <span class="Special">&quot;</span><span class="Special">#{</span><span class="Identifier">$0</span><span class="Special">}</span><span class="Constant">: Version: </span><span class="Special">#{</span><span class="Type">Olag</span>::<span class="Type">VERSION</span><span class="Special">}</span><span class="Special">\n</span><span class="Special">&quot;</span>
3522
+ <span class="PreProc">end</span>
3523
+
3524
+ <span class="PreProc">def</span> <span class="Identifier">test_print_help</span>
3525
+ <span class="Type">Olag</span>::<span class="Type">Application</span>.with_argv(<span class="Special">%w(</span><span class="Constant">-o stdout -h -v</span><span class="Special">)</span>) { <span class="Type">Olag</span>::<span class="Type">Application</span>.new(<span class="Constant">true</span>).run }.should == <span class="Constant">0</span>
3526
+ <span class="Type">File</span>.read(<span class="Special">&quot;</span><span class="Constant">stdout</span><span class="Special">&quot;</span>).should.include?(<span class="Special">&quot;</span><span class="Constant">DESCRIPTION:</span><span class="Special">&quot;</span>)
3527
+ <span class="PreProc">end</span>
3528
+
3529
+ <span class="PreProc">def</span> <span class="Identifier">test_print_errors</span>
3530
+ <span class="Type">Olag</span>::<span class="Type">Application</span>.with_argv(<span class="Special">%w(</span><span class="Constant">-e stderr</span><span class="Special">)</span>) { <span class="Type">ErrorApplication</span>.new(<span class="Constant">true</span>).run }.should == <span class="Constant">1</span>
3531
+ <span class="Type">File</span>.read(<span class="Special">&quot;</span><span class="Constant">stderr</span><span class="Special">&quot;</span>).should.include?(<span class="Special">&quot;</span><span class="Constant">Oops!</span><span class="Special">&quot;</span>)
3532
+ <span class="PreProc">end</span>
3533
+
3534
+ <span class="PreProc">end</span>
3535
+ </pre>
3536
+ </div>
3537
+ </div>
3538
+ </p>
3539
+ <p>
3540
+ And here is the implementation:
3541
+ </p>
3542
+ <p>
3543
+ <div class="named_with_containers chunk">
3544
+ <div class="chunk name">
3545
+ <a name="lib-olag-application-rb">
3546
+ <span>lib/olag/application.rb</span>
3547
+ </a>
3548
+ </div>
3549
+ <div class="chunk html">
3550
+ <pre class='ruby code syntax'>
3551
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">fileutils</span><span class="Special">&quot;</span>
3552
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/errors</span><span class="Special">&quot;</span>
3553
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/globals</span><span class="Special">&quot;</span>
3554
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/string_unindent.rb</span><span class="Special">&quot;</span>
3555
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">olag/version</span><span class="Special">&quot;</span>
3556
+ <span class="PreProc">require</span> <span class="Special">&quot;</span><span class="Constant">optparse</span><span class="Special">&quot;</span>
3557
+
3558
+ <span class="PreProc">module</span> <span class="Type">Olag</span>
3559
+
3560
+ </pre>
3561
+ <table class='layout'>
3562
+ <tr>
3563
+ <td class='indentation'>
3564
+ <pre> </pre>
3565
+ </td>
3566
+ <td class='html'>
3567
+ <div class='rdoc comment markup'>
3568
+ <p>
3569
+ Base class for Olag applications.
3570
+ </p>
3571
+ </div>
3572
+ </td>
3573
+ </tr>
3574
+ </table>
3575
+ <pre class='ruby code syntax'>
3576
+ <span class="PreProc">class</span> <span class="Type">Application</span>
3577
+
3578
+ </pre>
3579
+ <table class='layout'>
3580
+ <tr>
3581
+ <td class='indentation'>
3582
+ <pre> </pre>
3583
+ </td>
3584
+ <td class='html'>
3585
+ <div class='rdoc comment markup'>
3586
+ <p>
3587
+ Create a Olag application.
3588
+ </p>
3589
+ </div>
3590
+ </td>
3591
+ </tr>
3592
+ </table>
3593
+ <pre class='ruby code syntax'>
3594
+ <span class="PreProc">def</span> <span class="Identifier">initialize</span>(is_test = <span class="Constant">nil</span>)
3595
+ <span class="Identifier">@errors</span> = <span class="Type">Errors</span>.new
3596
+ <span class="Identifier">@is_test</span> = !!is_test
3597
+ <span class="PreProc">end</span>
3598
+
3599
+ </pre>
3600
+ <table class='layout'>
3601
+ <tr>
3602
+ <td class='indentation'>
3603
+ <pre> </pre>
3604
+ </td>
3605
+ <td class='html'>
3606
+ <div class='rdoc comment markup'>
3607
+ <p>
3608
+ Run the Olag application, returning its status.
3609
+ </p>
3610
+ </div>
3611
+ </td>
3612
+ </tr>
3613
+ </table>
3614
+ <pre class='ruby code syntax'>
3615
+ <span class="PreProc">def</span> <span class="Identifier">run</span>(*arguments, &amp;block)
3616
+ parse_options
3617
+ <span class="Statement">yield</span>(*arguments) <span class="Statement">if</span> block_given?
3618
+ <span class="Statement">return</span> print_errors
3619
+ <span class="PreProc">rescue</span> <span class="Type">ExitException</span> =&gt; exception
3620
+ <span class="Statement">return</span> exception.status
3621
+ <span class="PreProc">end</span>
3622
+
3623
+ </pre>
3624
+ <table class='layout'>
3625
+ <tr>
3626
+ <td class='indentation'>
3627
+ <pre> </pre>
3628
+ </td>
3629
+ <td class='html'>
3630
+ <div class='rdoc comment markup'>
3631
+ <p>
3632
+ Execute a block with an overriden ARGV, typically for running an
3633
+ application.
3634
+ </p>
3635
+ </div>
3636
+ </td>
3637
+ </tr>
3638
+ </table>
3639
+ <pre class='ruby code syntax'>
3640
+ <span class="PreProc">def</span> <span class="Constant">self</span>.<span class="Identifier">with_argv</span>(argv)
3641
+ <span class="Statement">return</span> <span class="Type">Globals</span>.without_changes <span class="Statement">do</span>
3642
+ <span class="Identifier">ARGV</span>.replace(argv)
3643
+ <span class="Statement">yield</span>
3644
+ <span class="Statement">end</span>
3645
+ <span class="PreProc">end</span>
3646
+
3647
+ <span class="Statement">protected</span>
3648
+
3649
+ </pre>
3650
+ <table class='layout'>
3651
+ <tr>
3652
+ <td class='indentation'>
3653
+ <pre> </pre>
3654
+ </td>
3655
+ <td class='html'>
3656
+ <div class='rdoc comment markup'>
3657
+ <p>
3658
+ Parse the command line options of the program.
3659
+ </p>
3660
+ </div>
3661
+ </td>
3662
+ </tr>
3663
+ </table>
3664
+ <pre class='ruby code syntax'>
3665
+ <span class="PreProc">def</span> <span class="Identifier">parse_options</span>
3666
+ parser = <span class="Type">OptionParser</span>.new <span class="Statement">do</span> |<span class="Identifier">options</span>|
3667
+ (<span class="Identifier">@options</span> = options).banner = banner + <span class="Special">&quot;</span><span class="Special">\n\n</span><span class="Constant">OPTIONS:</span><span class="Special">\n\n</span><span class="Special">&quot;</span>
3668
+ define_flags
3669
+ <span class="Statement">end</span>
3670
+ parser.parse!
3671
+ parse_arguments
3672
+ <span class="PreProc">end</span>
3673
+
3674
+ </pre>
3675
+ <table class='layout'>
3676
+ <tr>
3677
+ <td class='indentation'>
3678
+ <pre> </pre>
3679
+ </td>
3680
+ <td class='html'>
3681
+ <div class='rdoc comment markup'>
3682
+ <p>
3683
+ Parse remaining command-line file arguments. This is expected to be
3684
+ overriden by the concrete application sub-class. By default assumes there
3685
+ are no such arguments.
3686
+ </p>
3687
+ </div>
3688
+ </td>
3689
+ </tr>
3690
+ </table>
3691
+ <pre class='ruby code syntax'>
3692
+ <span class="PreProc">def</span> <span class="Identifier">parse_arguments</span>
3693
+ <span class="Statement">return</span> <span class="Statement">if</span> <span class="Identifier">ARGV</span>.size == <span class="Constant">0</span>
3694
+ <span class="Identifier">$stderr</span>.puts(<span class="Special">&quot;</span><span class="Special">#{</span><span class="Identifier">$0</span><span class="Special">}</span><span class="Constant">: Expects no command line file arguments.</span><span class="Special">&quot;</span>)
3695
+ <span class="Statement">exit</span>(<span class="Constant">1</span>)
3696
+ <span class="PreProc">end</span>
3697
+
3698
+ </pre>
3699
+ <table class='layout'>
3700
+ <tr>
3701
+ <td class='indentation'>
3702
+ <pre> </pre>
3703
+ </td>
3704
+ <td class='html'>
3705
+ <div class='rdoc comment markup'>
3706
+ <p>
3707
+ Define application flags. This is expected to be overriden by the concrete
3708
+ application sub-class.
3709
+ </p>
3710
+ </div>
3711
+ </td>
3712
+ </tr>
3713
+ </table>
3714
+ <pre class='ruby code syntax'>
3715
+ <span class="PreProc">def</span> <span class="Identifier">define_flags</span>
3716
+ define_help_flag
3717
+ define_version_flag
3718
+ define_redirect_flag(<span class="Special">&quot;</span><span class="Constant">$stdout</span><span class="Special">&quot;</span>, <span class="Special">&quot;</span><span class="Constant">output</span><span class="Special">&quot;</span>, <span class="Special">&quot;</span><span class="Constant">w</span><span class="Special">&quot;</span>)
3719
+ define_redirect_flag(<span class="Special">&quot;</span><span class="Constant">$stderr</span><span class="Special">&quot;</span>, <span class="Special">&quot;</span><span class="Constant">error</span><span class="Special">&quot;</span>, <span class="Special">&quot;</span><span class="Constant">w</span><span class="Special">&quot;</span>)
3720
+ <span class="Comment">#! Most scripts do not use this, but they can add it.</span>
3721
+ <span class="Comment">#! define_redirect_flag(&quot;$stdin&quot;, &quot;input&quot;, &quot;r&quot;)</span>
3722
+ <span class="PreProc">end</span>
3723
+
3724
+ </pre>
3725
+ <table class='layout'>
3726
+ <tr>
3727
+ <td class='indentation'>
3728
+ <pre> </pre>
3729
+ </td>
3730
+ <td class='html'>
3731
+ <div class='rdoc comment markup'>
3732
+ <p>
3733
+ Define the standard help flag.
3734
+ </p>
3735
+ </div>
3736
+ </td>
3737
+ </tr>
3738
+ </table>
3739
+ <pre class='ruby code syntax'>
3740
+ <span class="PreProc">def</span> <span class="Identifier">define_help_flag</span>
3741
+ <span class="Identifier">@options</span>.on(<span class="Special">&quot;</span><span class="Constant">-h</span><span class="Special">&quot;</span>, <span class="Special">&quot;</span><span class="Constant">--help</span><span class="Special">&quot;</span>, <span class="Special">&quot;</span><span class="Constant">Print this help message and exit.</span><span class="Special">&quot;</span>) <span class="Statement">do</span>
3742
+ puts(<span class="Identifier">@options</span>)
3743
+ print_additional_help
3744
+ <span class="Statement">exit</span>(<span class="Constant">0</span>)
3745
+ <span class="Statement">end</span>
3746
+ <span class="PreProc">end</span>
3747
+
3748
+ </pre>
3749
+ <table class='layout'>
3750
+ <tr>
3751
+ <td class='indentation'>
3752
+ <pre> </pre>
3753
+ </td>
3754
+ <td class='html'>
3755
+ <div class='rdoc comment markup'>
3756
+ <p>
3757
+ Print additional help message. This includes both the command line file
3758
+ arguments, if any, and a short description of the program.
3759
+ </p>
3760
+ </div>
3761
+ </td>
3762
+ </tr>
3763
+ </table>
3764
+ <pre class='ruby code syntax'>
3765
+ <span class="PreProc">def</span> <span class="Identifier">print_additional_help</span>
3766
+ arguments_name, arguments_description = arguments
3767
+ puts(format(<span class="Special">&quot;</span><span class="Constant"> %-33s%s</span><span class="Special">&quot;</span>, arguments_name, arguments_description)) <span class="Statement">if</span> arguments_name
3768
+ print(<span class="Special">&quot;</span><span class="Special">\n</span><span class="Constant">DESCRIPTION:</span><span class="Special">\n\n</span><span class="Special">&quot;</span>)
3769
+ print(description)
3770
+ <span class="PreProc">end</span>
3771
+
3772
+ </pre>
3773
+ <table class='layout'>
3774
+ <tr>
3775
+ <td class='indentation'>
3776
+ <pre> </pre>
3777
+ </td>
3778
+ <td class='html'>
3779
+ <div class='rdoc comment markup'>
3780
+ <p>
3781
+ Return the banner line of the help message. This is expected to be
3782
+ overriden by the concrete application sub-class. By default returns the
3783
+ path name of thje executed program.
3784
+ </p>
3785
+ </div>
3786
+ </td>
3787
+ </tr>
3788
+ </table>
3789
+ <pre class='ruby code syntax'>
3790
+ <span class="PreProc">def</span> <span class="Identifier">banner</span>
3791
+ <span class="Statement">return</span> <span class="Identifier">$0</span>
3792
+ <span class="PreProc">end</span>
3793
+
3794
+ </pre>
3795
+ <table class='layout'>
3796
+ <tr>
3797
+ <td class='indentation'>
3798
+ <pre> </pre>
3799
+ </td>
3800
+ <td class='html'>
3801
+ <div class='rdoc comment markup'>
3802
+ <p>
3803
+ Return the name and description of any final command-line file arguments,
3804
+ if any. This is expected to be overriden by the concrete application
3805
+ sub-class. By default, assume there are no final command-line file
3806
+ arguments (however, `parse_options` does not enforce this by default).
3807
+ </p>
3808
+ </div>
3809
+ </td>
3810
+ </tr>
3811
+ </table>
3812
+ <pre class='ruby code syntax'>
3813
+ <span class="PreProc">def</span> <span class="Identifier">arguments</span>
3814
+ <span class="Statement">return</span> <span class="Constant">nil</span>, <span class="Constant">nil</span>
3815
+ <span class="PreProc">end</span>
3816
+
3817
+ </pre>
3818
+ <table class='layout'>
3819
+ <tr>
3820
+ <td class='indentation'>
3821
+ <pre> </pre>
3822
+ </td>
3823
+ <td class='html'>
3824
+ <div class='rdoc comment markup'>
3825
+ <p>
3826
+ Return a short description of the program. This is expected to be overriden
3827
+ by the concrete application sub-class. By default, provide
3828
+ </p>
3829
+ </div>
3830
+ </td>
3831
+ </tr>
3832
+ </table>
3833
+ <pre class='ruby code syntax'>
3834
+ <span class="PreProc">def</span> <span class="Identifier">description</span>
3835
+ <span class="Statement">return</span> <span class="Special">&quot;</span><span class="Constant">Sample description</span><span class="Special">\n</span><span class="Special">&quot;</span>
3836
+ <span class="PreProc">end</span>
3837
+
3838
+ </pre>
3839
+ <table class='layout'>
3840
+ <tr>
3841
+ <td class='indentation'>
3842
+ <pre> </pre>
3843
+ </td>
3844
+ <td class='html'>
3845
+ <div class='rdoc comment markup'>
3846
+ <p>
3847
+ Define the standard version flag.
3848
+ </p>
3849
+ </div>
3850
+ </td>
3851
+ </tr>
3852
+ </table>
3853
+ <pre class='ruby code syntax'>
3854
+ <span class="PreProc">def</span> <span class="Identifier">define_version_flag</span>
3855
+ version_number = version
3856
+ <span class="Identifier">@options</span>.on(<span class="Special">&quot;</span><span class="Constant">-v</span><span class="Special">&quot;</span>, <span class="Special">&quot;</span><span class="Constant">--version</span><span class="Special">&quot;</span>, <span class="Special">&quot;</span><span class="Constant">Print the version number (</span><span class="Special">#{</span>version_number<span class="Special">}</span><span class="Constant">) and exit.</span><span class="Special">&quot;</span>) <span class="Statement">do</span>
3857
+ puts(<span class="Special">&quot;</span><span class="Special">#{</span><span class="Identifier">$0</span><span class="Special">}</span><span class="Constant">: Version: </span><span class="Special">#{</span>version_number<span class="Special">}</span><span class="Special">&quot;</span>)
3858
+ <span class="Statement">exit</span>(<span class="Constant">0</span>)
3859
+ <span class="Statement">end</span>
3860
+ <span class="PreProc">end</span>
3861
+
3862
+ </pre>
3863
+ <table class='layout'>
3864
+ <tr>
3865
+ <td class='indentation'>
3866
+ <pre> </pre>
3867
+ </td>
3868
+ <td class='html'>
3869
+ <div class='rdoc comment markup'>
3870
+ <p>
3871
+ Define a flag redirecting one of the standard IO files.
3872
+ </p>
3873
+ </div>
3874
+ </td>
3875
+ </tr>
3876
+ </table>
3877
+ <pre class='ruby code syntax'>
3878
+ <span class="PreProc">def</span> <span class="Identifier">define_redirect_flag</span>(variable, name, mode)
3879
+ <span class="Identifier">@options</span>.on(<span class="Special">&quot;</span><span class="Constant">-</span><span class="Special">#{</span>name[<span class="Constant">0</span>,<span class="Constant">1</span>]<span class="Special">}</span><span class="Special">&quot;</span>, <span class="Special">&quot;</span><span class="Constant">--</span><span class="Special">#{</span>name<span class="Special">}</span><span class="Constant"> FILE</span><span class="Special">&quot;</span>, <span class="Type">String</span>, <span class="Special">&quot;</span><span class="Constant">Redirect standard </span><span class="Special">#{</span>name<span class="Special">}</span><span class="Constant"> to a file.</span><span class="Special">&quot;</span>) <span class="Statement">do</span> |<span class="Identifier">file</span>|
3880
+ <span class="Statement">eval</span>(<span class="Special">&quot;</span><span class="Special">#{</span>variable<span class="Special">}</span><span class="Constant"> = Application::redirect_file(</span><span class="Special">#{</span>variable<span class="Special">}</span><span class="Constant">, file, mode)</span><span class="Special">&quot;</span>)
3881
+ <span class="Statement">end</span>
3882
+ <span class="PreProc">end</span>
3883
+
3884
+ </pre>
3885
+ <table class='layout'>
3886
+ <tr>
3887
+ <td class='indentation'>
3888
+ <pre> </pre>
3889
+ </td>
3890
+ <td class='html'>
3891
+ <div class='rdoc comment markup'>
3892
+ <p>
3893
+ Redirect a standard file.
3894
+ </p>
3895
+ </div>
3896
+ </td>
3897
+ </tr>
3898
+ </table>
3899
+ <pre class='ruby code syntax'>
3900
+ <span class="PreProc">def</span> <span class="Constant">self</span>.<span class="Identifier">redirect_file</span>(default, file, mode)
3901
+ <span class="Statement">return</span> default <span class="Statement">if</span> file.nil? || file == <span class="Special">&quot;</span><span class="Constant">-</span><span class="Special">&quot;</span>
3902
+ <span class="Type">FileUtils</span>.mkdir_p(<span class="Type">File</span>.dirname(<span class="Type">File</span>.expand_path(file))) <span class="Statement">if</span> mode == <span class="Special">&quot;</span><span class="Constant">w</span><span class="Special">&quot;</span>
3903
+ <span class="Statement">return</span> <span class="Type">File</span>.open(file, mode)
3904
+ <span class="PreProc">end</span>
3905
+
3906
+ </pre>
3907
+ <table class='layout'>
3908
+ <tr>
3909
+ <td class='indentation'>
3910
+ <pre> </pre>
3911
+ </td>
3912
+ <td class='html'>
3913
+ <div class='rdoc comment markup'>
3914
+ <p>
3915
+ Return the application’s version. This is expected to be overriden by the
3916
+ concrete application sub-class. In the base class, we just return Olag’s
3917
+ version which only useful for Olag’s tests.
3918
+ </p>
3919
+ </div>
3920
+ </td>
3921
+ </tr>
3922
+ </table>
3923
+ <pre class='ruby code syntax'>
3924
+ <span class="PreProc">def</span> <span class="Identifier">version</span>
3925
+ <span class="Statement">return</span> <span class="Type">Olag</span>::<span class="Type">VERSION</span>
3926
+ <span class="PreProc">end</span>
3927
+
3928
+ </pre>
3929
+ <table class='layout'>
3930
+ <tr>
3931
+ <td class='indentation'>
3932
+ <pre> </pre>
3933
+ </td>
3934
+ <td class='html'>
3935
+ <div class='rdoc comment markup'>
3936
+ <p>
3937
+ Print all the collected errors.
3938
+ </p>
3939
+ </div>
3940
+ </td>
3941
+ </tr>
3942
+ </table>
3943
+ <pre class='ruby code syntax'>
3944
+ <span class="PreProc">def</span> <span class="Identifier">print_errors</span>
3945
+ <span class="Identifier">@errors</span>.each <span class="Statement">do</span> |<span class="Identifier">error</span>|
3946
+ <span class="Identifier">$stderr</span>.puts(error)
3947
+ <span class="Statement">end</span>
3948
+ <span class="Statement">return</span> <span class="Identifier">@errors</span>.size
3949
+ <span class="PreProc">end</span>
3950
+
3951
+ </pre>
3952
+ <table class='layout'>
3953
+ <tr>
3954
+ <td class='indentation'>
3955
+ <pre> </pre>
3956
+ </td>
3957
+ <td class='html'>
3958
+ <div class='rdoc comment markup'>
3959
+ <p>
3960
+ Exit the application, unless we are running inside a test.
3961
+ </p>
3962
+ </div>
3963
+ </td>
3964
+ </tr>
3965
+ </table>
3966
+ <pre class='ruby code syntax'>
3967
+ <span class="PreProc">def</span> <span class="Identifier">exit</span>(status)
3968
+ <span class="Type">Kernel</span>.exit(status) <span class="Statement">unless</span> <span class="Identifier">@is_test</span>
3969
+ <span class="Statement">raise</span> <span class="Type">ExitException</span>.new(status)
3970
+ <span class="PreProc">end</span>
3971
+
3972
+ end
3973
+
3974
+ </pre>
3975
+ <table class='layout'>
3976
+ <tr>
3977
+ <td class='indentation'>
3978
+ <pre> </pre>
3979
+ </td>
3980
+ <td class='html'>
3981
+ <div class='rdoc comment markup'>
3982
+ <p>
3983
+ Exception used to exit when running inside tests.
3984
+ </p>
3985
+ </div>
3986
+ </td>
3987
+ </tr>
3988
+ </table>
3989
+ <pre class='ruby code syntax'>
3990
+ <span class="PreProc">class</span> <span class="Type">ExitException</span> &lt; <span class="Type">Exception</span>
3991
+
3992
+ </pre>
3993
+ <table class='layout'>
3994
+ <tr>
3995
+ <td class='indentation'>
3996
+ <pre> </pre>
3997
+ </td>
3998
+ <td class='html'>
3999
+ <div class='rdoc comment markup'>
4000
+ <p>
4001
+ The exit status.
4002
+ </p>
4003
+ </div>
4004
+ </td>
4005
+ </tr>
4006
+ </table>
4007
+ <pre class='ruby code syntax'>
4008
+ <span class="Statement">attr_reader</span> <span class="Constant">:status</span>
4009
+
4010
+ </pre>
4011
+ <table class='layout'>
4012
+ <tr>
4013
+ <td class='indentation'>
4014
+ <pre> </pre>
4015
+ </td>
4016
+ <td class='html'>
4017
+ <div class='rdoc comment markup'>
4018
+ <p>
4019
+ Create a new exception to indicate exiting the program with some status.
4020
+ </p>
4021
+ </div>
4022
+ </td>
4023
+ </tr>
4024
+ </table>
4025
+ <pre class='ruby code syntax'>
4026
+ <span class="PreProc">def</span> <span class="Identifier">initialize</span>(status)
4027
+ <span class="Identifier">@status</span> = status
4028
+ <span class="PreProc">end</span>
4029
+
4030
+ end
4031
+
4032
+ end
4033
+ </pre>
4034
+ </div>
4035
+ </div>
4036
+ </p>
4037
+ <p>
4038
+ It makes use of the following utility class, for saving and restoring the
4039
+ global state when running an application in a test:
4040
+ </p>
4041
+ <p>
4042
+ <div class="named_with_containers chunk">
4043
+ <div class="chunk name">
4044
+ <a name="lib-olag-globals-rb">
4045
+ <span>lib/olag/globals.rb</span>
4046
+ </a>
4047
+ </div>
4048
+ <div class="chunk html">
4049
+ <pre class='ruby code syntax'>
4050
+ <span class="PreProc">module</span> <span class="Type">Olag</span>
4051
+
4052
+ </pre>
4053
+ <table class='layout'>
4054
+ <tr>
4055
+ <td class='indentation'>
4056
+ <pre> </pre>
4057
+ </td>
4058
+ <td class='html'>
4059
+ <div class='rdoc comment markup'>
4060
+ <p>
4061
+ Save and restore the global variables when running an application inside a
4062
+ test.
4063
+ </p>
4064
+ </div>
4065
+ </td>
4066
+ </tr>
4067
+ </table>
4068
+ <pre class='ruby code syntax'>
4069
+ <span class="PreProc">class</span> <span class="Type">Globals</span>
4070
+
4071
+ </pre>
4072
+ <table class='layout'>
4073
+ <tr>
4074
+ <td class='indentation'>
4075
+ <pre> </pre>
4076
+ </td>
4077
+ <td class='html'>
4078
+ <div class='rdoc comment markup'>
4079
+ <p>
4080
+ Run some code without affecting the global state.
4081
+ </p>
4082
+ </div>
4083
+ </td>
4084
+ </tr>
4085
+ </table>
4086
+ <pre class='ruby code syntax'>
4087
+ <span class="PreProc">def</span> <span class="Constant">self</span>.<span class="Identifier">without_changes</span>(&amp;block)
4088
+ state = <span class="Type">Globals</span>.new
4089
+ <span class="Statement">begin</span>
4090
+ <span class="Statement">return</span> block.call
4091
+ <span class="Statement">ensure</span>
4092
+ state.restore
4093
+ <span class="Statement">end</span>
4094
+ <span class="PreProc">end</span>
4095
+
4096
+ </pre>
4097
+ <table class='layout'>
4098
+ <tr>
4099
+ <td class='indentation'>
4100
+ <pre> </pre>
4101
+ </td>
4102
+ <td class='html'>
4103
+ <div class='rdoc comment markup'>
4104
+ <p>
4105
+ Restore the relevant global variables.
4106
+ </p>
4107
+ </div>
4108
+ </td>
4109
+ </tr>
4110
+ </table>
4111
+ <pre class='ruby code syntax'>
4112
+ <span class="PreProc">def</span> <span class="Identifier">restore</span>
4113
+ <span class="Identifier">$stdin</span> = <span class="Type">Globals</span>.restore_file(<span class="Identifier">$stdin</span>, <span class="Identifier">@original_stdin</span>)
4114
+ <span class="Identifier">$stdout</span> = <span class="Type">Globals</span>.restore_file(<span class="Identifier">$stdout</span>, <span class="Identifier">@original_stdout</span>)
4115
+ <span class="Identifier">$stderr</span> = <span class="Type">Globals</span>.restore_file(<span class="Identifier">$stderr</span>, <span class="Identifier">@original_stderr</span>)
4116
+ <span class="Identifier">ARGV</span>.replace(<span class="Identifier">@original_argv</span>)
4117
+ <span class="PreProc">end</span>
4118
+
4119
+ <span class="Statement">protected</span>
4120
+
4121
+ </pre>
4122
+ <table class='layout'>
4123
+ <tr>
4124
+ <td class='indentation'>
4125
+ <pre> </pre>
4126
+ </td>
4127
+ <td class='html'>
4128
+ <div class='rdoc comment markup'>
4129
+ <p>
4130
+ Take a snapshot of the relevant global variables.
4131
+ </p>
4132
+ </div>
4133
+ </td>
4134
+ </tr>
4135
+ </table>
4136
+ <pre class='ruby code syntax'>
4137
+ <span class="PreProc">def</span> <span class="Identifier">initialize</span>
4138
+ <span class="Identifier">@original_stdin</span> = <span class="Identifier">$stdin</span>
4139
+ <span class="Identifier">@original_stdout</span> = <span class="Identifier">$stdout</span>
4140
+ <span class="Identifier">@original_stderr</span> = <span class="Identifier">$stderr</span>
4141
+ <span class="Identifier">@original_argv</span> = <span class="Identifier">ARGV</span>.dup
4142
+ <span class="PreProc">end</span>
4143
+
4144
+ </pre>
4145
+ <table class='layout'>
4146
+ <tr>
4147
+ <td class='indentation'>
4148
+ <pre> </pre>
4149
+ </td>
4150
+ <td class='html'>
4151
+ <div class='rdoc comment markup'>
4152
+ <p>
4153
+ Restore a specific global file variable to its original state.
4154
+ </p>
4155
+ </div>
4156
+ </td>
4157
+ </tr>
4158
+ </table>
4159
+ <pre class='ruby code syntax'>
4160
+ <span class="PreProc">def</span> <span class="Constant">self</span>.<span class="Identifier">restore_file</span>(current, original)
4161
+ current.close <span class="Statement">unless</span> current == original
4162
+ <span class="Statement">return</span> original
4163
+ <span class="PreProc">end</span>
4164
+
4165
+ end
4166
+
4167
+ end
4168
+ </pre>
4169
+ </div>
4170
+ </div>
4171
+ </p>
4172
+ <h2>License</h2>
4173
+ <p>
4174
+ Olag is published under the MIT license:
4175
+ </p>
4176
+ <p>
4177
+ <div class="named_with_containers chunk">
4178
+ <div class="chunk name">
4179
+ <a name="license">
4180
+ <span>LICENSE</span>
4181
+ </a>
4182
+ </div>
4183
+ <div class="chunk html">
4184
+ <div class='rdoc doc markup'>
4185
+ <p>
4186
+ Copyright © 2010-2011 Oren Ben-Kiki
4187
+ </p>
4188
+ <p>
4189
+ Permission is hereby granted, free of charge, to any person obtaining a
4190
+ copy of this software and associated documentation files (the
4191
+ “Software”), to deal in the Software without restriction, including
4192
+ without limitation the rights to use, copy, modify, merge, publish,
4193
+ distribute, sublicense, and/or sell copies of the Software, and to permit
4194
+ persons to whom the Software is furnished to do so, subject to the
4195
+ following conditions:
4196
+ </p>
4197
+ <p>
4198
+ The above copyright notice and this permission notice shall be included in
4199
+ all copies or substantial portions of the Software.
4200
+ </p>
4201
+ <p>
4202
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
4203
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4204
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4205
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4206
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
4207
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
4208
+ DEALINGS IN THE SOFTWARE.
4209
+ </p>
4210
+ </div>
4211
+ </div>
4212
+ </div>
4213
+ </p>
4214
+ </div>
4215
+ <script type="text/javascript">
4216
+ /*
4217
+ * Quick-and-dirty JS for inserting a table of content inside a DIV with the id
4218
+ * "contents". The table of content is a series of nested UL and LI elements,
4219
+ * prefixed with an H1 containing the text "0 Contents". This H1 comes in
4220
+ * addition to the single static H1 expected by HTML best practices. It looks
4221
+ * "right" and should not confuse search engines etc. since they do not execute
4222
+ * Javascript code.
4223
+ */
4224
+ function inject_contents() {
4225
+ var contents = document.getElementById("contents");
4226
+ var lists = contents_lists();
4227
+ contents.appendChild(contents_header()); // TRICKY: Must be done after contents_lists().
4228
+ contents.appendChild(lists);
4229
+ }
4230
+
4231
+ /*
4232
+ * Create a table of contents H1.
4233
+ */
4234
+ function contents_header() {
4235
+ var h = document.createElement("h1");
4236
+ var text = document.createTextNode("Contents");
4237
+ h.appendChild(text);
4238
+ return h;
4239
+ }
4240
+
4241
+ /*
4242
+ * Create nested UL/LI lists for the table of content.
4243
+ */
4244
+ function contents_lists() {
4245
+ var container;
4246
+ var indices = [];
4247
+ var h_elements = all_h_elements();
4248
+ for (var e in h_elements) {
4249
+ h = h_elements[e];
4250
+ var level = h.tagName.substring(1, 2) - 1;
4251
+ container = pop_container(container, indices, level);
4252
+ container = push_container(container, indices, level);
4253
+ var id = indices.join(".");
4254
+ container.appendChild(list_element(id, h));
4255
+ h.insertBefore(header_anchor(id), h.firstChild);
4256
+ }
4257
+ return pop_container(container, indices, 1);
4258
+ }
4259
+
4260
+ /*
4261
+ * Get a list of all H elements in the DOM. We skip the single H1 element;
4262
+ * otherwise it would just have the index "1" which would be prefixed to all
4263
+ * other headers.
4264
+ */
4265
+ function all_h_elements() {
4266
+ var elements = document.getElementsByTagName("*");
4267
+ var h_elements = [];
4268
+ for (var e in elements) {
4269
+ var h = elements[e];
4270
+ if (/^h[2-9]$/i.test(h.tagName)) h_elements.push(h);
4271
+ }
4272
+ return h_elements;
4273
+ }
4274
+
4275
+ /*
4276
+ * Pop indices (and UL containers) until reaching up to a given level.
4277
+ */
4278
+ function pop_container(container, indices, level) {
4279
+ while (indices.length > level) {
4280
+ container = container.parentNode;
4281
+ indices.pop();
4282
+ }
4283
+ return container;
4284
+ }
4285
+
4286
+ /*
4287
+ * Push indices (and UL containers) until reaching doen to a given level.
4288
+ */
4289
+ function push_container(container, indices, level) {
4290
+ while (indices.length < level) {
4291
+ // TRICKY: push a 0 for the very last new level, so the ++ at the end
4292
+ // will turn it into a 1.
4293
+ indices.push(indices.level < level - 1);
4294
+ var ul = document.createElement("ul");
4295
+ if (container) {
4296
+ container.appendChild(ul);
4297
+ }
4298
+ container = ul;
4299
+ }
4300
+ indices[indices.length - 1]++;
4301
+ return container;
4302
+ }
4303
+
4304
+ /*
4305
+ * Create a LI for an H element with some id.
4306
+ */
4307
+ function list_element(id, h) {
4308
+ var a = document.createElement("a");
4309
+ a.href = "#" + id;
4310
+ a.innerHTML = id + "&nbsp;" + h.innerHTML;
4311
+ var li = document.createElement("li");
4312
+ li.appendChild(a);
4313
+ return li;
4314
+ }
4315
+
4316
+ /*
4317
+ * Create an anchor for an H element with some id.
4318
+ */
4319
+ function header_anchor(id) {
4320
+ var text = document.createTextNode(id + " ");
4321
+ var a = document.createElement("a");
4322
+ a.id = id;
4323
+ a.appendChild(text);
4324
+ return a;
4325
+ }
4326
+
4327
+ /* Only invoke it after all helper functions are defined. */
4328
+ inject_contents();
4329
+ /*
4330
+ * Quick-and-dirty JS for inserting a "+"/"-" control for chunk visibility next
4331
+ * to each chunk's name. By default, all chunks are hidden.
4332
+ */
4333
+ function inject_chunk_controls() {
4334
+ var name_div;
4335
+ foreach_chunk_elements(function(div) {
4336
+ name_div = div;
4337
+ }, function(html_div) {
4338
+ var control_span = document.createElement("span");
4339
+ var hide = function() {
4340
+ control_span.innerHTML = "+";
4341
+ html_div.style.display = "none";
4342
+ }
4343
+ var show = function() {
4344
+ control_span.innerHTML = "&#8211;"; // Vertical bar.
4345
+ html_div.style.display = "block";
4346
+ }
4347
+ name_div.onclick = function() {
4348
+ html_div.style.display == "block" ? hide() : show();
4349
+ }
4350
+ hide(); // Initializes html_div.style.display
4351
+ control_span.className = "control chunk";
4352
+ name_div.insertBefore(control_span, name_div.firstChild);
4353
+ })
4354
+ }
4355
+
4356
+ /*
4357
+ * Loop on all DIV elements that contain a chunk name, or that contain chunk
4358
+ * HTML. Assumes that they come in pairs - name first, HTML second.
4359
+ */
4360
+ function foreach_chunk_elements(name_lambda, html_lambda) {
4361
+ var div_elements = document.getElementsByTagName("div");
4362
+ for (var e in div_elements) {
4363
+ var div = div_elements[e];
4364
+ classes = " " + div.className + " ";
4365
+ if (!/ chunk /.test(classes)) continue;
4366
+ if (/ name /.test(classes)) name_lambda(div);
4367
+ if (/ html /.test(classes)) html_lambda(div);
4368
+ }
4369
+ }
4370
+
4371
+ /* Only invoke it after all helper functions are defined. */
4372
+ inject_chunk_controls();
4373
+ (function(h,o,f){var u=!+"\v1";var y=function(){return null;};var m=0;var q="plaintext";var l=function(A){function z(){}z.prototype=A;return new z();};var p=false;var i=function(A,C,z){for(var B=0;B<A.length;B++){if(A[B]===C){return true;}if(z&&typeof(A[B])==="string"&&typeof(C)==="string"&&A[B].toUpperCase()===C.toUpperCase()){return true;}}return false;};var e=function(z,A){if(!A){return z;}for(var B in A){z[B]=A[B];}return z;};var x=function(z){return z.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&");};var j=function(C,B,A,z){return function(G){var F=C;if(B===1){A.reverse();}for(var D=0,E,H;D<A.length;D++){H=G[F+(D*B)];E=A[A.length-1-D];if(H===f){if(E.optional!==f&&E.optional){F-=B;}else{return false;}}else{if(H.name===E.token&&(E.values===f||i(E.values,H.value,z))){continue;}else{if(E.optional!==f&&E.optional){F-=B;}else{return false;}}}}return true;};};var c=function(B,A,C,z){return function(F){var D=B,E;var G=false;while((E=F[--D])!==f){if(E.name===C.token&&i(C.values,E.value)){if(E.name===A.token&&i(A.values,E.value,z)){G=true;break;}return false;}if(E.name===A.token&&i(A.values,E.value,z)){G=true;break;}}if(!G){return false;}D=B;while((E=F[++D])!==f){if(E.name===A.token&&i(A.values,E.value,z)){if(E.name===C.token&&i(C.values,E.value,z)){G=true;break;}return false;}if(E.name===C.token&&i(C.values,E.value,z)){G=true;break;}}return G;};};var w=function(){var z=function(A){return function(C){var B=o.createElement("span");B.className="sunlight-"+A;B.appendChild(C.createTextNode(C.tokens[C.index].value));return C.addNode(B)||true;};};return{handleToken:function(A){return z(A.tokens[A.index].name)(A);},handle_default:function(A){return A.addNode(A.createTextNode(A.tokens[A.index].value));},handle_ident:function(A){var B=function(D,E){D=D||[];for(var C=0;C<D.length;C++){if(typeof(D[C])==="function"){if(D[C](A)){return z("named-ident")(A);}}else{if(E&&E(D[C])(A.tokens)){return z("named-ident")(A);}}}return false;};return B(A.language.namedIdentRules.custom)||B(A.language.namedIdentRules.follows,function(C){return j(A.index-1,-1,C.slice(0),A.language.caseInsensitive);})||B(A.language.namedIdentRules.precedes,function(C){return j(A.index+1,1,C.slice(0),A.language.caseInsensitive);})||B(A.language.namedIdentRules.between,function(C){return c(A.index,C.opener,C.closer,A.language.caseInsensitive);})||z("ident")(A);}};}();var r=function(E){E=E.replace(/\r\n/g,"\n").replace(/\r/g,"\n");var C=0;var H=1;var A=1;var z=E.length;var B=f;var G=z>0?E.charAt(0):B;var F=false;var D=function(J){if(J===0){return"";}J=J||1;var K="",I=1;while(I<=J&&E.charAt(C+I)!==""){K+=E.charAt(C+I++);}return K===""?B:K;};return{toString:function(){return"length: "+z+", index: "+C+", line: "+H+", column: "+A+", current: ["+G+"]";},peek:function(I){return D(I);},read:function(I){var K=D(I);if(K!==B){C+=K.length;A+=K.length;if(F){H++;A=1;F=false;}var J=K.substring(0,K.length-1).replace(/[^\n]/g,"").length;if(J>0){H+=J;A=1;}if(K.charAt(K.length-1)==="\n"){F=true;}G=K.charAt(K.length-1);}else{C=z;G=B;}return K;},getLine:function(){return H;},getColumn:function(){return A;},isEof:function(){return C>=z;},EOF:B,current:function(){return G;}};};var b=function(B,G,C,z){G=G||[];var F=B.reader.current();if(B.language.caseInsensitive){F=F.toUpperCase();}if(!G[F]){return null;}G=G[F];for(var E=0,A,H;E<G.length;E++){A=G[E].value;H=F+B.reader.peek(A.length);if(A===H||G[E].regex.test(H)){var I=B.reader.getLine(),D=B.reader.getColumn();return B.createToken(C,B.reader.current()+B.reader[z?"peek":"read"](A.length-1),I,D);}}return null;};var v=function(){var z=function(I,J){var G=I[2]||[];var F=I[1].length;var K=typeof(I[1])==="string"?new RegExp(x(I[1])):I[1].regex;var H=I[3]||false;return function(P,L,N,M,R,O){var Q=false,N=N||"";O=O?1:0;var S=function(V){var T;var W=P.reader.current();for(var U=0;U<G.length;U++){T=(V?W:"")+P.reader.peek(G[U].length-V);if(T===G[U]){N+=P.reader.read(T.length-V);return true;}}T=(V?W:"")+P.reader.peek(F-V);if(K.test(T)){Q=true;return false;}N+=V?W:P.reader.read();return true;};if(!O||S(true)){while(P.reader.peek()!==P.reader.EOF&&S(false)){}}if(O){N+=P.reader.current();P.reader.read();}else{N+=H||P.reader.peek()===P.reader.EOF?"":P.reader.read(F);}if(!Q){P.continuation=L;}return P.createToken(J,N,M,R);};};var D=function(H){var N=function(){return H.language.identFirstLetter&&H.language.identFirstLetter.test(H.reader.current());};var J=function(){return b(H,H.language.keywords,"keyword");};var O=function(){if(H.language.customTokens===f){return null;}for(var R in H.language.customTokens){var Q=b(H,H.language.customTokens[R],R);if(Q!==null){return Q;}}return null;};var M=function(){return b(H,H.language.operators,"operator");};var I=function(){var Q=H.reader.current();if(H.language.punctuation.test(x(Q))){return H.createToken("punctuation",Q,H.reader.getLine(),H.reader.getColumn());}return null;};var G=function(S){if(!N()){return null;}var U=H.reader.current();var R=H.reader.peek();var Q=H.reader.getLine(),T=H.reader.getColumn();while(R!==H.reader.EOF){if(!H.language.identAfterFirstLetter.test(R)){break;}U+=H.reader.read();R=H.reader.peek();}return H.createToken(S?"namedIdent":"ident",U,Q,T);};var P=function(){if(H.defaultData.text===""){H.defaultData.line=H.reader.getLine();H.defaultData.column=H.reader.getColumn();}H.defaultData.text+=H.reader.current();return null;};var F=function(){var X=H.reader.current();for(var W in H.language.scopes){var R=H.language.scopes[W];for(var T=0,V,S,U,Q;T<R.length;T++){V=R[T][0];if(V!==X+H.reader.peek(V.length-1)){continue;}S=H.reader.getLine(),U=H.reader.getColumn();H.reader.read(V.length-1);Q=z(R[T],W);return Q(H,Q,V,S,U);}}return null;};var K=function(){return H.language.numberParser(H);};var L=function(){var S=H.language.customParseRules;if(S===f){return null;}for(var R=0,Q;R<S.length;R++){Q=S[R](H);if(Q!==null){return Q;}}return null;};return L()||O()||J()||F()||G()||K()||M()||I()||P();};var E=function(I,K,F){var J=[];var H={reader:r(I),language:K,token:function(L){return J[L];},getAllTokens:function(){return J.slice(0);},count:function(){return J.length;},defaultData:{text:"",line:1,column:1},createToken:function(M,O,L,N){return{name:M,line:L,value:u?O.replace(/\n/g,"\r"):O,column:N};}};if(F){J.push(F(H,F,"",H.reader.getLine(),H.reader.getColumn(),true));}while(!H.reader.isEof()){var G=D(H);if(G!==null){if(H.defaultData.text!==""){J.push(H.createToken("default",H.defaultData.text,H.defaultData.line,H.defaultData.column));H.defaultData.text="";}if(G[0]!==f){J=J.concat(G);}else{J.push(G);}}H.reader.read();}if(H.defaultData.text!==""){J.push(H.createToken("default",H.defaultData.text,H.defaultData.line,H.defaultData.column));}return{tokens:J,continuation:H.continuation};};var B=function(I,L,J,H){var F=[];var G=E(I,L,J.continuation);var K=function(){var M=String.fromCharCode(160);var N=new Array(H.tabWidth+1).join(M);return function(O){return O.split(" ").join(M).split("\t").join(N);};}();return{tokens:(J.tokens||[]).concat(G.tokens),index:J.index?J.index+1:0,language:L,continuation:G.continuation,addNode:function(M){F.push(M);},createTextNode:function(M){return o.createTextNode(K(M));},getNodes:function(){return F;}};};var C=function(N,G,M){if(!p){p=function(){var P=null;if(o.defaultView&&o.defaultView.getComputedStyle){P=o.defaultView.getComputedStyle;}else{if(typeof(o.body.currentStyle)!=="undefined"){P=function(R,Q){return R.currentStyle;};}else{P=y;}}return function(Q,R){return P(Q,null)[R];};}();}M=M||{};var J=k[G];if(J===f){J=k[q];}var O=B(N,J,M,this.options);var L=J.analyzer;for(var K=M.index?M.index+1:0,I,H,F;K<O.tokens.length;K++){O.index=K;I=O.tokens[K].name;H="handle_"+I;L[H]?L[H](O):L.handleToken(O);}return O;};return{highlight:function(G,F){return C.call(this,G,F);},highlightNode:function A(Q){var J;if((J=Q.className.match(/(?:\s|^)sunlight-highlight-(\S+)(?:\s|$)/))===null||/(?:\s|^)sunlight-highlighted(?:\s|$)/.test(Q.className)){return;}var V=J[1];var K=0;for(var S=0,T,O,R,L;S<Q.childNodes.length;S++){if(Q.childNodes[S].nodeType===3){T=o.createElement("span");T.className="sunlight-highlighted sunlight-"+V;L=C.call(this,Q.childNodes[S].nodeValue,V,L);m++;K=K||m;O=L.getNodes();for(R=0;R<O.length;R++){T.appendChild(O[R]);}Q.replaceChild(T,Q.childNodes[S]);}else{A.call(this,Q.childNodes[S]);}}Q.className+=" sunlight-highlighted";if(this.options.lineNumbers===true||(p&&this.options.lineNumbers==="automatic"&&p(Q,"display")==="block")){var M=o.createElement("div"),F=o.createElement("pre");var P=Q.innerHTML.replace(/[^\n]/g,"").length-/\n$/.test(Q.lastChild.innerHTML);var G,W,N=this.options.lineHighlight.length>0;if(N){G=o.createElement("div");G.className="sunlight-line-highlight-overlay";}M.className="sunlight-container";F.className="sunlight-line-number-margin";for(var U=this.options.lineNumberStart,I=o.createTextNode(u?"\r":"\n"),H,X;U<=this.options.lineNumberStart+P;U++){H=o.createElement("a");X=(Q.id?Q.id:"sunlight-"+K)+"-line-"+U;H.setAttribute("name",X);H.setAttribute("href","#"+X);H.appendChild(o.createTextNode(U));F.appendChild(H);F.appendChild(I.cloneNode(false));if(N){W=o.createElement("div");if(i(this.options.lineHighlight,U)){W.className="sunlight-line-highlight-active";}G.appendChild(W);}}M.appendChild(F);Q.parentNode.insertBefore(M,Q);Q.parentNode.removeChild(Q);M.appendChild(Q);if(N){M.appendChild(G);}}}};}();var g=function(z){this.options=e(e({},a),z);};g.prototype=v;var d=function(C,z,B){B=B||1;var A=C[z+B];if(A!==f&&A.name==="default"){A=C[z+(B*2)];}return A;};var s=function(F,E,z){var A={};for(var B=0,D,C;B<F.length;B++){D=z?F[B].toUpperCase():F[B];C=D.charAt(0);if(!A[C]){A[C]=[];}A[C].push({value:D,regex:new RegExp(x(D)+E,z?"i":"")});}return A;};var t=function(C){var F=C.reader.current(),E,A=C.reader.getLine(),D=C.reader.getColumn();if(!/\d/.test(F)){if(F!=="."||!/\d/.test(C.reader.peek())){return null;}E=F+C.reader.read();}else{E=F;}var B,z=false;while((B=C.reader.peek())!==C.reader.EOF){if(!/[A-Za-z0-9]/.test(B)){if(B==="."&&!z){E+=C.reader.read();z=true;continue;}break;}E+=C.reader.read();}return C.createToken("number",E,A,D);};var a={tabWidth:4,lineNumbers:"automatic",lineNumberStart:1,lineHighlight:[]};var k={};var n={analyzer:l(w),customTokens:[],namedIdentRules:{},punctuation:/[^\w\s]/,numberParser:t,caseInsensitive:false};h.Sunlight={version:"1.3",Highlighter:g,createAnalyzer:function(){return l(w);},globalOptions:a,highlightAll:function(B){var A=new g(B);var z=o.getElementsByTagName("*");for(var C=0;C<z.length;C++){A.highlightNode(z[C]);}},registerLanguage:function(z,B){if(!z){throw'Languages must be registered with an identifier, e.g. "php" for PHP';}B=e(e({},n),B);B.name=z;B.keywords=s(B.keywords||[],"\\b",B.caseInsensitive);B.operators=s(B.operators||[],"",B.caseInsensitive);for(var A in B.customTokens){B.customTokens[A]=s(B.customTokens[A].values,B.customTokens[A].boundary,B.caseInsensitive);}k[B.name]=B;},util:{escapeSequences:["\\n","\\t","\\r","\\\\","\\v","\\f"],contains:i,matchWord:b,createHashMap:s,createBetweenRule:c,createProceduralRule:j,getNextNonWsToken:function(A,z){return d(A,z,1);},getPreviousNonWsToken:function(A,z){return d(A,z,-1);},whitespace:{token:"default",optional:true}}};h.Sunlight.registerLanguage(q,{punctuation:/(?!x)x/,numberParser:y});}(window,document));
4374
+ (function(c,a,d){if(c===d||c.registerLanguage===d){throw"Include sunlight.js before including language files";}var b=[];c.registerLanguage("ruby",{keywords:["BEGIN","END","__ENCODING__","__END__","__FILE__","__LINE__","alias","and","begin","break","case","class","def","defined?","do","else","elsif","end","ensure","false","for","if","in","module","next","nil","not","or","redo","rescue","retry","return","self","super","then","true","undef","unless","until","when","while","yield"],customTokens:{"function":{values:["Array","Float","Integer","String","at_exit","autoload","binding","caller","catch","chop!","chop","chomp!","chomp","eval","exec","exit!","exit","fail","fork","format","gets","global_variables","gsub!","gsub","iterator?","lambda","load","local_variables","loop","open","p","print","printf","proc","putc","puts","raise","rand","readline","readlines","require","select","sleep","split","sprintf","srand","sub!","sub","syscall","system","test","trace_var","trap","untrace_var"],boundary:"\\W"},specialOperator:{values:["defined?","eql?","equal?"],boundary:"\\W"}},customParseRules:[function(g){var f=g.reader.peek();if(g.reader.current()!=="/"||f==="/"||f==="*"){return null;}var l=function(){var n=g.token(g.count()-1);var m=null;if(g.defaultData.text!==""){m=g.createToken("default",g.defaultData.text);}if(!m){m=n;}if(m===d){return true;}if(m.name==="default"&&m.value.indexOf("\n")>-1){return true;}if(n.name==="keyword"||n.name==="ident"||n.name==="number"){return false;}return true;}();if(!l){return null;}var k="/";var e=g.reader.getLine();var i=g.reader.getColumn();var j,h;while(g.reader.peek()!==g.reader.EOF){j=g.reader.peek(2);if(j==="\\/"||j==="\\\\"){k+=g.reader.read(2);continue;}k+=(h=g.reader.read());if(h==="/"){break;}}while(g.reader.peek()!==g.reader.EOF){if(!/[A-Za-z]/.test(g.reader.peek())){break;}k+=g.reader.read();}return g.createToken("regexLiteral",k,e,i);},function(g){if(g.reader.current()!=="<"||g.reader.peek()!=="<"){return null;}var e=c.util.getPreviousNonWsToken(g.getAllTokens(),g.count()-1);if(e&&(e.name==="ident"||e.name==="number"||e.name==="string")){return null;}var o=g.reader.getLine(),i=g.reader.getColumn();var m="<<",j="";g.reader.read(2);var l=g.reader.current();var f="";if(l==="-"){g.reader.read();m+=l;l=g.reader.current();}if(c.util.contains(['"',"'","`"],l)){f=l;}else{j=l;}m+=l;var n;while((n=g.reader.peek())!==g.reader.EOF){if(n==="\n"||(f===""&&/\W/.test(n))){break;}if(n==="\\"){var k=g.reader.peek(2);if(f!==""&&c.util.contains(["\\"+f,"\\\\"],k)){m+=k;j+=g.reader.read(2);continue;}}m+=g.reader.read();if(f!==""&&n===f){break;}j+=n;}b.push(j);var h=g.createToken("heredocDeclaration",m,o,i);return h;},function(g){if(b.length===0){return null;}if(g.defaultData.text.replace(/[^\n]/g,"").length===0){return null;}var j=[],k,f,h,i=g.reader.current();while(b.length>0&&g.reader.peek()!==g.reader.EOF){k=b.shift();f=g.reader.getLine(),h=g.reader.getColumn();while(g.reader.peek()!==g.reader.EOF){var e=g.reader.peek(k.length+2);if(e==="\n"+k||e==="\n"+k+"\n"){i+=g.reader.read(k.length+2);break;}i+=g.reader.read();}j.push(g.createToken("heredoc",i,f,h));i="";}return j.length>0?j:null;},function(h){if(h.reader.current()!=="%"){return null;}var k="%",j=1,l=false;var g=h.reader.peek();if(g==="q"||g==="Q"||g==="r"){j++;if(g==="r"){l=true;}}if(/[A-Za-z0-9=]$/.test(h.reader.peek(j))){return null;}var e=h.reader.getLine(),i=h.reader.getColumn();k+=h.reader.read(j);var f=k.charAt(k.length-1);switch(f){case"(":f=")";break;case"[":f="]";break;case"{":f="}";break;}while((g=h.reader.peek())!==h.reader.EOF){if(g==="\\"&&c.util.contains(["\\"+f,"\\\\"],h.reader.peek(2))){k+=h.reader.read(2);continue;}k+=h.reader.read();if(g===f){break;}}if(l){while(h.reader.peek()!==h.reader.EOF){if(!/[A-Za-z]/.test(h.reader.peek())){break;}k+=h.reader.read();}}return h.createToken(l?"regexLiteral":"rawString",k,e,i);},function(h){if(h.reader.current()!=="="||h.reader.peek(5)!=="begin"){return null;}if((h.count()===0&&h.defaultData.text==="")||h.defaultData.text.charAt(h.defaultData.text.length-1)!=="\n"){return null;}var j="=begin";var f=h.reader.getLine();var i=h.reader.getColumn();h.reader.read(5);var e=false,g;while((g=h.reader.peek())!==h.reader.EOF){if(!e&&h.reader.peek(5)==="\n=end"){e=true;j+=h.reader.read(5);continue;}if(e&&g==="\n"){break;}j+=h.reader.read();}return h.createToken("docComment",j,f,i);}],scopes:{string:[['"','"',c.util.escapeSequences.concat(['\\"'])],["'","'",["\\'","\\\\"]]],comment:[["#","\n",null,true]],subshellCommand:[["`","`",["\\`"]]],globalVariable:[["$",{length:1,regex:/[\W]/},null,true]],instanceVariable:[["@",{length:1,regex:/[\W]/},null,true]]},identFirstLetter:/[A-Za-z_]/,identAfterFirstLetter:/\w/,namedIdentRules:{follows:[[{token:"keyword",values:["class","def"]},c.util.whitespace],[{token:"keyword",values:["class"]},c.util.whitespace,{token:"ident"},c.util.whitespace,{token:"operator",values:["<","<<"]},c.util.whitespace]],precedes:[[c.util.whitespace,{token:"operator",values:["::"]}],[c.util.whitespace,{token:"operator",values:["."]},c.util.whitespace,{token:"ident",values:["new"]},c.util.whitespace,{token:"punctuation",values:["("]}]]},operators:["?","...","..",".","::",":","[]","+=","+","-=","-","**=","*=","**","*","/=","/","%=","%","&&=","&=","&&","&","||=","|=","||","|","^=","^","~","<=>","<<=","<<","<=","<",">>=",">>",">=",">","!~","!=","!","=>","===","==","=~","="]});}(window.Sunlight,document));
4375
+ Sunlight.globalOptions.lineNumbers = false;
4376
+ Sunlight.highlightAll();
4377
+ </script>
4378
+ </body>
4379
+ </html>