olag 0.1.10

Sign up to get free protection for your applications and to get access to all the features.
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>