olag 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ChangeLog +24 -0
- data/LICENSE +19 -0
- data/README.rdoc +17 -0
- data/Rakefile +24 -0
- data/codnar.html +4379 -0
- data/doc/root.html +26 -0
- data/doc/system.markdown +283 -0
- data/lib/olag/application.rb +167 -0
- data/lib/olag/change_log.rb +59 -0
- data/lib/olag/data_files.rb +20 -0
- data/lib/olag/errors.rb +44 -0
- data/lib/olag/gem_specification.rb +77 -0
- data/lib/olag/globals.rb +43 -0
- data/lib/olag/hash_struct.rb +12 -0
- data/lib/olag/rake.rb +263 -0
- data/lib/olag/string_unindent.rb +19 -0
- data/lib/olag/test.rb +8 -0
- data/lib/olag/test/with_errors.rb +26 -0
- data/lib/olag/test/with_fakefs.rb +36 -0
- data/lib/olag/test/with_rake.rb +34 -0
- data/lib/olag/test/with_tempfile.rb +49 -0
- data/lib/olag/update_version.rb +53 -0
- data/lib/olag/version.rb +8 -0
- data/test/access_data_files.rb +15 -0
- data/test/collect_errors.rb +33 -0
- data/test/missing_keys.rb +17 -0
- data/test/run_application.rb +44 -0
- data/test/unindent_text.rb +26 -0
- metadata +243 -0
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">"</span><span class="Constant">/lib</span><span class="Special">"</span>)
|
652
|
+
|
653
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">olag/rake</span><span class="Special">"</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-<version>.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">"</span><span class="Constant">olag</span><span class="Special">"</span>
|
745
|
+
spec.version = <span class="Type">Olag</span>::<span class="Type">VERSION</span>
|
746
|
+
spec.author = <span class="Special">"</span><span class="Constant">Oren Ben-Kiki</span><span class="Special">"</span>
|
747
|
+
spec.email = <span class="Special">"</span><span class="Constant">rubygems-oren@ben-kiki.org</span><span class="Special">"</span>
|
748
|
+
spec.homepage = <span class="Special">"</span><span class="Constant"><a href="https://rubygems.org/gems/olag">https://rubygems.org/gems/olag</a></span><span class="Special">"</span>
|
749
|
+
spec.summary = <span class="Special">"</span><span class="Constant">Olag - Oren's Library/Application Gem framework</span><span class="Special">"</span>
|
750
|
+
spec.description = (<<-<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">""</span>).chomp.gsub(<span class="Special">"</span><span class="Special">\n</span><span class="Special">"</span>, <span class="Special">"</span><span class="Constant"> </span><span class="Special">"</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>(&block)
|
875
|
+
original_initialize(&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">"</span><span class="Constant">lib/</span><span class="Special">#{</span>name<span class="Special">}</span><span class="Constant">/version.rb</span><span class="Special">"</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">"</span><span class="Constant">olag</span><span class="Special">"</span>) <span class="Statement">unless</span> <span class="Constant">self</span>.name == <span class="Special">"</span><span class="Constant">olag</span><span class="Special">"</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">"</span><span class="Constant">{lib,doc}/**/*</span><span class="Special">"</span>)
|
962
|
+
<span class="Constant">self</span>.files << <span class="Special">"</span><span class="Constant">Rakefile</span><span class="Special">"</span> << <span class="Special">"</span><span class="Constant">codnar.html</span><span class="Special">"</span>
|
963
|
+
setup_file_member(<span class="Constant">:executables</span>, <span class="Special">"</span><span class="Constant">bin/*</span><span class="Special">"</span>) { |<span class="Identifier">path</span>| path.sub(<span class="Special">"</span><span class="Constant">bin/</span><span class="Special">"</span>, <span class="Special">""</span>) }
|
964
|
+
setup_file_member(<span class="Constant">:test_files</span>, <span class="Special">"</span><span class="Constant">test/**/*</span><span class="Special">"</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, &block)
|
987
|
+
old_value = instance_variable_get(<span class="Special">"</span><span class="Constant">@</span><span class="Special">#{</span>member<span class="Special">}</span><span class="Special">"</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!(&block) <span class="Statement">if</span> block
|
990
|
+
instance_variable_set(<span class="Special">"</span><span class="Constant">@</span><span class="Special">#{</span>member<span class="Special">}</span><span class="Special">"</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">"</span><span class="Constant">README.rdoc</span><span class="Special">"</span>, <span class="Special">"</span><span class="Constant">LICENSE</span><span class="Special">"</span>, <span class="Special">"</span><span class="Constant">ChangeLog</span><span class="Special">"</span> ]
|
1011
|
+
<span class="Constant">self</span>.rdoc_options << <span class="Special">"</span><span class="Constant">--title</span><span class="Special">"</span> << <span class="Special">"</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">"</span> \
|
1012
|
+
<< <span class="Special">"</span><span class="Constant">--main</span><span class="Special">"</span> << <span class="Special">"</span><span class="Constant">README.rdoc</span><span class="Special">"</span> \
|
1013
|
+
<< <span class="Special">"</span><span class="Constant">--line-numbers</span><span class="Special">"</span> \
|
1014
|
+
<< <span class="Special">"</span><span class="Constant">--all</span><span class="Special">"</span> \
|
1015
|
+
<< <span class="Special">"</span><span class="Constant">--quiet</span><span class="Special">"</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">"</span><span class="Constant">codnar/rake</span><span class="Special">"</span>
|
1039
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">olag/change_log</span><span class="Special">"</span>
|
1040
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">olag/gem_specification</span><span class="Special">"</span>
|
1041
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">olag/update_version</span><span class="Special">"</span>
|
1042
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">olag/version</span><span class="Special">"</span>
|
1043
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">rake/clean</span><span class="Special">"</span>
|
1044
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">rake/gempackagetask</span><span class="Special">"</span>
|
1045
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">rake/rdoctask</span><span class="Special">"</span>
|
1046
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">rake/testtask</span><span class="Special">"</span>
|
1047
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">rcov/rcovtask</span><span class="Special">"</span>
|
1048
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">reek/rake/task</span><span class="Special">"</span>
|
1049
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">roodi</span><span class="Special">"</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> => <span class="Constant">:all</span>)
|
1092
|
+
define_all_task
|
1093
|
+
<span class="Type">CLOBBER</span> << <span class="Special">"</span><span class="Constant">saikuro</span><span class="Special">"</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">"</span><span class="Constant">Version, verify, document, package</span><span class="Special">"</span>, <span class="Constant">:all</span> => [ <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">"</span><span class="Constant">Test, coverage, analyze code</span><span class="Special">"</span>, <span class="Constant">:verify</span> => [ <span class="Constant">:coverage</span>, <span class="Constant">:analyze</span> ])
|
1293
|
+
define_desc_class(<span class="Special">"</span><span class="Constant">Test code covarage with RCov</span><span class="Special">"</span>, <span class="Type">Rcov</span>::<span class="Type">RcovTask</span>, <span class="Special">"</span><span class="Constant">coverage</span><span class="Special">"</span>) { |<span class="Identifier">task</span>| <span class="Type">Rake</span>.configure_coverage_task(task) }
|
1294
|
+
define_desc_class(<span class="Special">"</span><span class="Constant">Test code without coverage</span><span class="Special">"</span>, ::<span class="Type">Rake</span>::<span class="Type">TestTask</span>, <span class="Special">"</span><span class="Constant">test</span><span class="Special">"</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">"</span><span class="Constant">test/*.rb</span><span class="Special">"</span>]
|
1318
|
+
task.libs << <span class="Special">"</span><span class="Constant">lib</span><span class="Special">"</span> << <span class="Special">"</span><span class="Constant">test/lib</span><span class="Special">"</span>
|
1319
|
+
task.rcov_opts << <span class="Special">"</span><span class="Constant">--failure-threshold</span><span class="Special">"</span> << <span class="Special">"</span><span class="Constant">100</span><span class="Special">"</span> \
|
1320
|
+
<< <span class="Special">"</span><span class="Constant">--exclude</span><span class="Special">"</span> << <span class="Special">"</span><span class="Constant">^/</span><span class="Special">"</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">"</span><span class="Constant">test/*.rb</span><span class="Special">"</span>]
|
1359
|
+
task.libs << <span class="Special">"</span><span class="Constant">lib</span><span class="Special">"</span> << <span class="Special">"</span><span class="Constant">test/lib</span><span class="Special">"</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">"</span><span class="Constant">Analyze source code</span><span class="Special">"</span>, <span class="Constant">:analyze</span> => [ <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">"</span><span class="Constant">Check for smelly code with Reek</span><span class="Special">"</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">"</span><span class="Constant">Check for smelly code with Roodi</span><span class="Special">"</span>, <span class="Constant">:roodi</span>) { run_roodi_task }
|
1412
|
+
define_desc_task(<span class="Special">"</span><span class="Constant">Check for duplicated code with Flay</span><span class="Special">"</span>, <span class="Constant">:flay</span>) { run_flay_task }
|
1413
|
+
define_desc_task(<span class="Special">"</span><span class="Constant">Check for complex code with Saikuro</span><span class="Special">"</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 << <span class="Special">"</span><span class="Constant">--quiet</span><span class="Special">"</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">"</span><span class="Constant">roodi.config</span><span class="Special">"</span> <span class="Statement">if</span> <span class="Type">File</span>.exist?(<span class="Special">"</span><span class="Constant">roodi.config</span><span class="Special">"</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">"</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">"</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">"</span><span class="Constant">flay </span><span class="Special">"</span> + dirs.join(<span class="Special">'</span><span class="Constant"> </span><span class="Special">'</span>), <span class="Special">"</span><span class="Constant">r</span><span class="Special">"</span>).read.chomp
|
1480
|
+
<span class="Statement">return</span> <span class="Statement">if</span> result == <span class="Special">"</span><span class="Constant">Total score (lower is better) = 0</span><span class="Special">\n</span><span class="Special">"</span>
|
1481
|
+
puts(result)
|
1482
|
+
<span class="Statement">raise</span> <span class="Special">"</span><span class="Constant">Flay found code duplication.</span><span class="Special">"</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">"</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"> > /dev/null</span><span class="Special">"</span>)
|
1504
|
+
result = <span class="Type">File</span>.read(<span class="Special">"</span><span class="Constant">saikuro/index_cyclo.html</span><span class="Special">"</span>)
|
1505
|
+
<span class="Statement">raise</span> <span class="Special">"</span><span class="Constant">Saikuro found complicated code.</span><span class="Special">"</span> <span class="Statement">if</span> result.include?(<span class="Special">"</span><span class="Constant">Errors and Warnings</span><span class="Special">"</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">"</span><span class="Constant">Generate all documentation</span><span class="Special">"</span>
|
1553
|
+
task <span class="Constant">:doc</span> => [ <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">"</span><span class="Constant">LICENSE</span><span class="Special">"</span>, <span class="Special">"</span><span class="Constant">README.rdoc</span><span class="Special">"</span> ]
|
1576
|
+
task.main = <span class="Special">"</span><span class="Constant">README.rdoc</span><span class="Special">"</span>
|
1577
|
+
task.rdoc_dir = <span class="Special">"</span><span class="Constant">rdoc</span><span class="Special">"</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">"</span><span class="Constant">ChangeLog|codnar\.html</span><span class="Special">"</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">"</span><span class="Constant">Rakefile|.*\.rb|bin/.*</span><span class="Special">"</span>,
|
1668
|
+
<span class="Special">"</span><span class="Constant">classify_source_code:ruby</span><span class="Special">"</span>,
|
1669
|
+
<span class="Special">"</span><span class="Constant">format_code_gvim_css:ruby</span><span class="Special">"</span>,
|
1670
|
+
<span class="Special">"</span><span class="Constant">classify_shell_comments</span><span class="Special">"</span>,
|
1671
|
+
<span class="Special">"</span><span class="Constant">format_rdoc_comments</span><span class="Special">"</span>,
|
1672
|
+
<span class="Special">"</span><span class="Constant">chunk_by_vim_regions</span><span class="Special">"</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">"</span><span class="Constant">.*\.html</span><span class="Special">"</span>,
|
1691
|
+
<span class="Special">"</span><span class="Constant">split_html_documentation</span><span class="Special">"</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">"</span><span class="Constant">.*\.markdown|.*\.md</span><span class="Special">"</span>,
|
1710
|
+
<span class="Special">"</span><span class="Constant">split_markdown_documentation</span><span class="Special">"</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">"</span><span class="Constant">LICENSE|.*\.rdoc</span><span class="Special">"</span>,
|
1729
|
+
<span class="Special">"</span><span class="Constant">split_rdoc_documentation</span><span class="Special">"</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">"</span><span class="Constant">doc/root.html</span><span class="Special">"</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">"</span><span class="Constant">No Codnar configuration for file: </span><span class="Special">#{</span>file<span class="Special">}</span><span class="Special">"</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">"</span><span class="Constant">^(</span><span class="Special">#{</span>regexp<span class="Special">}</span><span class="Constant">)$</span><span class="Special">"</span>)
|
1801
|
+
<span class="Statement">rescue</span>
|
1802
|
+
<span class="Statement">abort</span>(<span class="Special">"</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">"</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">"</span><span class="Constant">Git commit process</span><span class="Special">"</span>, <span class="Constant">:commit</span> => [ <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">"</span><span class="Constant">Update version file from Git</span><span class="Special">"</span>, <span class="Constant">:version</span>) { update_version_file }
|
1869
|
+
define_desc_task(<span class="Special">"</span><span class="Constant">Perform the 1st (main) Git commit</span><span class="Special">"</span>, <span class="Constant">:first_commit</span>) { run_git_first_commit }
|
1870
|
+
define_desc_task(<span class="Special">"</span><span class="Constant">Perform the 2nd (amend) Git commit</span><span class="Special">"</span>, <span class="Constant">:second_commit</span>) { run_git_second_commit }
|
1871
|
+
define_desc_task(<span class="Special">"</span><span class="Constant">Update ChangeLog from Git</span><span class="Special">"</span>, <span class="Constant">:changelog</span>) { <span class="Type">Olag</span>::<span class="Type">ChangeLog</span>.new(<span class="Special">"</span><span class="Constant">ChangeLog</span><span class="Special">"</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">"</span><span class="Constant">Updated gem version; re-run rake</span><span class="Special">"</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">"</span><span class="Constant">Git 1st commit failed</span><span class="Special">"</span> <span class="Statement">unless</span> system(<span class="Special">"</span><span class="Constant">set +x; git commit</span><span class="Special">"</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">"</span><span class="Constant">Git 2nd commit failed</span><span class="Special">"</span> <span class="Statement">unless</span> system(<span class="Special">"</span><span class="Constant">set +x; EDITOR=true git commit --amend ChangeLog</span><span class="Special">"</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">"</span><span class="Constant">0.1.10</span><span class="Special">"</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
|
+
"<em>major</em>.<em>minor</em>.<em>commits</em>"</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">"</span><span class="Constant">w</span><span class="Special">"</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">"</span><span class="Special">#{</span>path<span class="Special">}</span><span class="Constant">: Does not contain a valid VERSION line.</span><span class="Special">"</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">"'</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">"'</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">"</span><span class="Constant">git rev-list --all | wc -l</span><span class="Special">"</span>).read.chomp.to_i
|
2124
|
+
git_status = <span class="Type">IO</span>.popen(<span class="Special">"</span><span class="Constant">git status</span><span class="Special">"</span>).read
|
2125
|
+
git_commits += <span class="Constant">1</span> <span class="Statement">unless</span> git_status.include?(<span class="Special">"</span><span class="Constant">working directory clean</span><span class="Special">"</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">"</span><span class="Constant">w</span><span class="Special">"</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">"</span><span class="Constant">git log --pretty='format:%ci::%an <%ae>::%s'</span><span class="Special">"</span>, <span class="Special">"</span><span class="Constant">r</span><span class="Special">"</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> << id
|
2239
|
+
<span class="Identifier">@subjects_by_id</span>[id] ||= []
|
2240
|
+
<span class="Identifier">@subjects_by_id</span>[id] << 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">"</span><span class="Constant">::</span><span class="Special">"</span>)
|
2262
|
+
date, time, zone = date.split(<span class="Special">"</span><span class="Constant"> </span><span class="Special">"</span>)
|
2263
|
+
id = <span class="Special">"</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">"</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">"</span><span class="Special">#{</span>id<span class="Special">}</span><span class="Special">\n\n</span><span class="Special">"</span>
|
2307
|
+
<span class="Identifier">@log_file</span>.puts subjects.map { |<span class="Identifier">subject</span>| <span class="Special">"</span><span class="Special">\t</span><span class="Constant">* </span><span class="Special">#{</span>subject<span class="Special">}</span><span class="Special">"</span> }.join(<span class="Special">"</span><span class="Special">\n</span><span class="Special">"</span>)
|
2308
|
+
<span class="Identifier">@log_file</span>.puts <span class="Special">"</span><span class="Special">\n</span><span class="Special">"</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><<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">"</span><span class="Constant">olag/string_unindent</span><span class="Special">"</span>
|
2340
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">test/spec</span><span class="Special">"</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> < ::<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
|
+
<<-<span class="Special">EOF</span>.unindent.should == <span class="Special">"</span><span class="Constant">a</span><span class="Special">\n</span><span class="Constant"> b</span><span class="Special">\n</span><span class="Special">"</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">"</span><span class="Constant"> a</span><span class="Special">\n</span><span class="Constant"> b</span><span class="Special">\n</span><span class="Special">"</span>.unindent.should == <span class="Special">"</span><span class="Constant">a</span><span class="Special">\n</span><span class="Constant"> b</span><span class="Special">\n</span><span class="Special">"</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">"</span><span class="Constant"> a</span><span class="Special">\n</span><span class="Constant"> b</span><span class="Special">\n</span><span class="Special">"</span>.unindent(<span class="Constant">1</span>).should == <span class="Special">"</span><span class="Constant"> a</span><span class="Special">\n</span><span class="Constant"> b</span><span class="Special">\n</span><span class="Special">"</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">"</span><span class="Constant"> a</span><span class="Special">\n</span><span class="Constant"> b</span><span class="Special">\n</span><span class="Special">"</span>.unindent(<span class="Special">"</span><span class="Constant"> </span><span class="Special">"</span>).should == <span class="Special">"</span><span class="Constant"> a</span><span class="Special">\n</span><span class="Constant"> b</span><span class="Special">\n</span><span class="Special">"</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">"</span><span class="Constant"> </span><span class="Special">"</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">""</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">""</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">"</span><span class="Constant">olag/data_files</span><span class="Special">"</span>
|
2481
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">test/spec</span><span class="Special">"</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> < <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">"</span><span class="Constant">olag/data_files.rb</span><span class="Special">"</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">"</span><span class="Constant">no-such-file</span><span class="Special">"</span>).should == <span class="Special">"</span><span class="Constant">no-such-file</span><span class="Special">"</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">"</span><span class="Constant">/</span><span class="Special">"</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 "file not found error" 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">"</span><span class="Constant">olag/hash_struct</span><span class="Special">"</span>
|
2606
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">test/spec</span><span class="Special">"</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> < ::<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">"</span><span class="Constant">value</span><span class="Special">"</span>
|
2633
|
+
hash.missing.should == <span class="Special">"</span><span class="Constant">value</span><span class="Special">"</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">"</span><span class="Constant">=</span><span class="Special">"</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">"</span><span class="Constant">olag/errors</span><span class="Special">"</span>
|
2721
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">olag/test</span><span class="Special">"</span>
|
2722
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">test/spec</span><span class="Special">"</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> < <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> << <span class="Special">"</span><span class="Constant">Oops</span><span class="Special">"</span>
|
2746
|
+
<span class="Identifier">@errors</span>.should == [ <span class="Special">"</span><span class="Special">#{</span><span class="Identifier">$0</span><span class="Special">}</span><span class="Constant">: Oops</span><span class="Special">"</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">"</span><span class="Constant">foo</span><span class="Special">"</span>) <span class="Statement">do</span>
|
2751
|
+
<span class="Identifier">@errors</span> << <span class="Special">"</span><span class="Constant">Eeek</span><span class="Special">"</span>
|
2752
|
+
<span class="Special">"</span><span class="Constant">result</span><span class="Special">"</span>
|
2753
|
+
<span class="Statement">end</span>.should == <span class="Special">"</span><span class="Constant">result</span><span class="Special">"</span>
|
2754
|
+
<span class="Identifier">@errors</span> << <span class="Special">"</span><span class="Constant">Oops</span><span class="Special">"</span>
|
2755
|
+
<span class="Identifier">@errors</span>.should == [ <span class="Special">"</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">"</span>, <span class="Special">"</span><span class="Special">#{</span><span class="Identifier">$0</span><span class="Special">}</span><span class="Constant">: Oops</span><span class="Special">"</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">"</span><span class="Constant">foo</span><span class="Special">"</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> << <span class="Special">"</span><span class="Constant">Eeek</span><span class="Special">"</span>
|
2762
|
+
<span class="Statement">end</span>
|
2763
|
+
<span class="Identifier">@errors</span> << <span class="Special">"</span><span class="Constant">Oops</span><span class="Special">"</span>
|
2764
|
+
<span class="Identifier">@errors</span>.should == [ <span class="Special">"</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">"</span>, <span class="Special">"</span><span class="Special">#{</span><span class="Identifier">$0</span><span class="Special">}</span><span class="Constant">: Oops</span><span class="Special">"</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">"</span><span class="Constant">olag/errors</span><span class="Special">"</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> < <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, &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"><<</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">"</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">"</span> <span class="Statement">unless</span> <span class="Identifier">@path</span>
|
2998
|
+
<span class="Statement">return</span> <span class="Special">"</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">"</span> <span class="Statement">unless</span> <span class="Identifier">@line</span>
|
2999
|
+
<span class="Statement">return</span> <span class="Special">"</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">"</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">"</span><span class="Constant">fakefs/safe</span><span class="Special">"</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">"</span><span class="Constant">fileutils</span><span class="Special">"</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">"</span><span class="Constant">.</span><span class="Special">"</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> ||= []) << (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">"</span><span class="Constant">.</span><span class="Special">"</span>)
|
3212
|
+
file = <span class="Type">Tempfile</span>.open(<span class="Special">"</span><span class="Constant">dir</span><span class="Special">"</span>, directory)
|
3213
|
+
(<span class="Identifier">@tempfiles</span> ||= []) << (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">"</span><span class="Constant">olag/test/with_errors</span><span class="Special">"</span>
|
3402
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">olag/test/with_fakefs</span><span class="Special">"</span>
|
3403
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">olag/test/with_rake</span><span class="Special">"</span>
|
3404
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">olag/test/with_tempfile</span><span class="Special">"</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">"</span><span class="Constant">olag/application</span><span class="Special">"</span>
|
3447
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">olag/test</span><span class="Special">"</span>
|
3448
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">test/spec</span><span class="Special">"</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> < <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> << <span class="Special">"</span><span class="Constant">Oops!</span><span class="Special">"</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> < <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">"</span><span class="Constant">stderr</span><span class="Special">"</span>).should.include?(<span class="Special">"</span><span class="Constant">Expects no command line file arguments</span><span class="Special">"</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">"</span><span class="Constant">nested/stdout</span><span class="Special">"</span>).should == <span class="Special">"</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">"</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">"</span><span class="Constant">stdout</span><span class="Special">"</span>).should.include?(<span class="Special">"</span><span class="Constant">DESCRIPTION:</span><span class="Special">"</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">"</span><span class="Constant">stderr</span><span class="Special">"</span>).should.include?(<span class="Special">"</span><span class="Constant">Oops!</span><span class="Special">"</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">"</span><span class="Constant">fileutils</span><span class="Special">"</span>
|
3552
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">olag/errors</span><span class="Special">"</span>
|
3553
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">olag/globals</span><span class="Special">"</span>
|
3554
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">olag/string_unindent.rb</span><span class="Special">"</span>
|
3555
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">olag/version</span><span class="Special">"</span>
|
3556
|
+
<span class="PreProc">require</span> <span class="Special">"</span><span class="Constant">optparse</span><span class="Special">"</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, &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> => 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">"</span><span class="Special">\n\n</span><span class="Constant">OPTIONS:</span><span class="Special">\n\n</span><span class="Special">"</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">"</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">"</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">"</span><span class="Constant">$stdout</span><span class="Special">"</span>, <span class="Special">"</span><span class="Constant">output</span><span class="Special">"</span>, <span class="Special">"</span><span class="Constant">w</span><span class="Special">"</span>)
|
3719
|
+
define_redirect_flag(<span class="Special">"</span><span class="Constant">$stderr</span><span class="Special">"</span>, <span class="Special">"</span><span class="Constant">error</span><span class="Special">"</span>, <span class="Special">"</span><span class="Constant">w</span><span class="Special">"</span>)
|
3720
|
+
<span class="Comment">#! Most scripts do not use this, but they can add it.</span>
|
3721
|
+
<span class="Comment">#! define_redirect_flag("$stdin", "input", "r")</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">"</span><span class="Constant">-h</span><span class="Special">"</span>, <span class="Special">"</span><span class="Constant">--help</span><span class="Special">"</span>, <span class="Special">"</span><span class="Constant">Print this help message and exit.</span><span class="Special">"</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">"</span><span class="Constant"> %-33s%s</span><span class="Special">"</span>, arguments_name, arguments_description)) <span class="Statement">if</span> arguments_name
|
3768
|
+
print(<span class="Special">"</span><span class="Special">\n</span><span class="Constant">DESCRIPTION:</span><span class="Special">\n\n</span><span class="Special">"</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">"</span><span class="Constant">Sample description</span><span class="Special">\n</span><span class="Special">"</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">"</span><span class="Constant">-v</span><span class="Special">"</span>, <span class="Special">"</span><span class="Constant">--version</span><span class="Special">"</span>, <span class="Special">"</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">"</span>) <span class="Statement">do</span>
|
3857
|
+
puts(<span class="Special">"</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">"</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">"</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">"</span>, <span class="Special">"</span><span class="Constant">--</span><span class="Special">#{</span>name<span class="Special">}</span><span class="Constant"> FILE</span><span class="Special">"</span>, <span class="Type">String</span>, <span class="Special">"</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">"</span>) <span class="Statement">do</span> |<span class="Identifier">file</span>|
|
3880
|
+
<span class="Statement">eval</span>(<span class="Special">"</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">"</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">"</span><span class="Constant">-</span><span class="Special">"</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">"</span><span class="Constant">w</span><span class="Special">"</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> < <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>(&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 + " " + 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 = "–"; // 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>
|