tap 0.11.1 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/History +35 -1
  2. data/MIT-LICENSE +1 -1
  3. data/README +16 -15
  4. data/bin/tap +1 -1
  5. data/cmd/console.rb +4 -3
  6. data/cmd/manifest.rb +2 -2
  7. data/cmd/run.rb +12 -15
  8. data/doc/Class Reference +120 -117
  9. data/doc/Command Reference +27 -27
  10. data/doc/Syntax Reference +55 -111
  11. data/doc/Tutorial +69 -26
  12. data/lib/tap.rb +3 -8
  13. data/lib/tap/app.rb +122 -146
  14. data/lib/tap/constants.rb +2 -2
  15. data/lib/tap/env.rb +178 -252
  16. data/lib/tap/exe.rb +67 -30
  17. data/lib/tap/file_task.rb +224 -411
  18. data/lib/tap/generator/arguments.rb +13 -0
  19. data/lib/tap/generator/base.rb +112 -30
  20. data/lib/tap/generator/destroy.rb +36 -13
  21. data/lib/tap/generator/generate.rb +69 -48
  22. data/lib/tap/generator/generators/command/templates/command.erb +3 -3
  23. data/lib/tap/generator/generators/config/config_generator.rb +82 -10
  24. data/lib/tap/generator/generators/generator/generator_generator.rb +16 -6
  25. data/lib/tap/generator/generators/generator/templates/task.erb +2 -2
  26. data/lib/tap/generator/generators/generator/templates/test.erb +26 -0
  27. data/lib/tap/generator/generators/root/root_generator.rb +24 -13
  28. data/lib/tap/generator/generators/root/templates/Rakefile +4 -4
  29. data/lib/tap/generator/generators/root/templates/{tapfile → Rapfile} +6 -6
  30. data/lib/tap/generator/generators/root/templates/gemspec +0 -1
  31. data/lib/tap/generator/generators/task/task_generator.rb +3 -3
  32. data/lib/tap/generator/generators/task/templates/test.erb +1 -1
  33. data/lib/tap/generator/manifest.rb +7 -1
  34. data/lib/tap/generator/preview.rb +76 -0
  35. data/lib/tap/root.rb +222 -156
  36. data/lib/tap/spec.rb +41 -0
  37. data/lib/tap/support/aggregator.rb +25 -28
  38. data/lib/tap/support/audit.rb +278 -357
  39. data/lib/tap/support/constant.rb +2 -1
  40. data/lib/tap/support/constant_manifest.rb +28 -25
  41. data/lib/tap/support/dependency.rb +1 -1
  42. data/lib/tap/support/executable.rb +52 -183
  43. data/lib/tap/support/executable_queue.rb +50 -20
  44. data/lib/tap/support/gems.rb +1 -1
  45. data/lib/tap/support/intern.rb +0 -6
  46. data/lib/tap/support/join.rb +49 -83
  47. data/lib/tap/support/joins.rb +0 -3
  48. data/lib/tap/support/joins/switch.rb +13 -11
  49. data/lib/tap/support/joins/sync_merge.rb +25 -50
  50. data/lib/tap/support/manifest.rb +1 -0
  51. data/lib/tap/support/node.rb +140 -20
  52. data/lib/tap/support/parser.rb +56 -42
  53. data/lib/tap/support/schema.rb +183 -157
  54. data/lib/tap/support/templater.rb +9 -1
  55. data/lib/tap/support/versions.rb +39 -0
  56. data/lib/tap/task.rb +150 -177
  57. data/lib/tap/tasks/dump.rb +4 -4
  58. data/lib/tap/tasks/load.rb +29 -29
  59. data/lib/tap/test.rb +66 -53
  60. data/lib/tap/test/env_vars.rb +3 -3
  61. data/lib/tap/test/extensions.rb +11 -17
  62. data/lib/tap/test/file_test.rb +74 -132
  63. data/lib/tap/test/file_test_class.rb +4 -1
  64. data/lib/tap/test/regexp_escape.rb +2 -2
  65. data/lib/tap/test/script_test.rb +2 -2
  66. data/lib/tap/test/subset_test.rb +6 -6
  67. data/lib/tap/test/tap_test.rb +28 -154
  68. metadata +30 -51
  69. data/bin/rap +0 -118
  70. data/cgi/run.rb +0 -97
  71. data/lib/tap/declarations.rb +0 -229
  72. data/lib/tap/generator/generators/config/templates/doc.erb +0 -12
  73. data/lib/tap/generator/generators/config/templates/nodoc.erb +0 -8
  74. data/lib/tap/generator/generators/file_task/file_task_generator.rb +0 -27
  75. data/lib/tap/generator/generators/file_task/templates/file.txt +0 -11
  76. data/lib/tap/generator/generators/file_task/templates/result.yml +0 -6
  77. data/lib/tap/generator/generators/file_task/templates/task.erb +0 -33
  78. data/lib/tap/generator/generators/file_task/templates/test.erb +0 -29
  79. data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +0 -5
  80. data/lib/tap/patches/optparse/summarize.rb +0 -62
  81. data/lib/tap/support/assignments.rb +0 -173
  82. data/lib/tap/support/class_configuration.rb +0 -182
  83. data/lib/tap/support/combinator.rb +0 -125
  84. data/lib/tap/support/configurable.rb +0 -113
  85. data/lib/tap/support/configurable_class.rb +0 -271
  86. data/lib/tap/support/configuration.rb +0 -170
  87. data/lib/tap/support/gems/rake.rb +0 -111
  88. data/lib/tap/support/instance_configuration.rb +0 -173
  89. data/lib/tap/support/joins/fork.rb +0 -19
  90. data/lib/tap/support/joins/merge.rb +0 -22
  91. data/lib/tap/support/joins/sequence.rb +0 -21
  92. data/lib/tap/support/lazy_attributes.rb +0 -45
  93. data/lib/tap/support/lazydoc.rb +0 -386
  94. data/lib/tap/support/lazydoc/comment.rb +0 -503
  95. data/lib/tap/support/lazydoc/config.rb +0 -17
  96. data/lib/tap/support/lazydoc/definition.rb +0 -36
  97. data/lib/tap/support/lazydoc/document.rb +0 -152
  98. data/lib/tap/support/lazydoc/method.rb +0 -24
  99. data/lib/tap/support/tdoc.rb +0 -409
  100. data/lib/tap/support/tdoc/tdoc_html_generator.rb +0 -38
  101. data/lib/tap/support/tdoc/tdoc_html_template.rb +0 -42
  102. data/lib/tap/support/validation.rb +0 -479
  103. data/lib/tap/tasks/rake.rb +0 -57
@@ -1,503 +0,0 @@
1
- require 'strscan'
2
-
3
- module Tap
4
- module Support
5
- module Lazydoc
6
- # Comment represents a code comment parsed by Lazydoc. Comments consist
7
- # of a subject and content.
8
- #
9
- # sample_comment = %Q{
10
- # # this is the content
11
- # #
12
- # # content may stretch across
13
- # # multiple lines
14
- # this is the subject
15
- # }
16
- #
17
- # Normally the subject is the first non-comment line following the content,
18
- # although in some cases the subject will be manually set to something else
19
- # (as in a Lazydoc constant attribute). The content is an array of comment
20
- # fragments organized by line:
21
- #
22
- # c = Comment.parse(sample_comment)
23
- # c.subject # => "this is the subject"
24
- # c.content
25
- # # => [
26
- # # ["this is the content"],
27
- # # [""],
28
- # # ["content may stretch across", "multiple lines"]]
29
- #
30
- # Comments may be initialized to the subject line and then resolved later:
31
- #
32
- # doc = %Q{
33
- # module Sample
34
- # # this is the content of the comment
35
- # # for method_one
36
- # def method_one
37
- # end
38
- #
39
- # # this is the content of the comment
40
- # # for method_two
41
- # def method_two
42
- # end
43
- # end}
44
- #
45
- # c1 = Comment.new(4).resolve(doc)
46
- # c1.subject # => " def method_one"
47
- # c1.content # => [["this is the content of the comment", "for method_one"]]
48
- #
49
- # c2 = Comment.new(9).resolve(doc)
50
- # c2.subject # => " def method_two"
51
- # c2.content # => [["this is the content of the comment", "for method_two"]]
52
- #
53
- # A Regexp (or Proc) may be used in place of a line number; during resolve,
54
- # the lines will be scanned and the first matching line will be used.
55
- #
56
- # c3 = Comment.new(/def method_two/).resolve(doc)
57
- # c3.subject # => " def method_two"
58
- # c3.content # => [["this is the content of the comment", "for method_two"]]
59
- #
60
- class Comment
61
-
62
- class << self
63
-
64
- # Parses the input string into a comment. Takes a string or a
65
- # StringScanner and returns the comment.
66
- #
67
- # comment_string = %Q{
68
- # # comments spanning multiple
69
- # # lines are collected
70
- # #
71
- # # while indented lines
72
- # # are preserved individually
73
- # #
74
- # this is the subject line
75
- #
76
- # # this line is not parsed
77
- # }
78
- #
79
- # c = Comment.parse(comment_string)
80
- # c.content
81
- # # => [
82
- # # ['comments spanning multiple', 'lines are collected'],
83
- # # [''],
84
- # # [' while indented lines'],
85
- # # [' are preserved individually'],
86
- # # [''],
87
- # # []]
88
- # c.subject # => "this is the subject line"
89
- #
90
- # Parsing may be manually ended by providing a block; parse yields
91
- # each line fragment to the block and stops parsing when the block
92
- # returns true. Note that no subject will be parsed under these
93
- # circumstances.
94
- #
95
- # c = Comment.parse(comment_string) {|frag| frag.strip.empty? }
96
- # c.content
97
- # # => [
98
- # # ['comments spanning multiple', 'lines are collected']]
99
- # c.subject # => nil
100
- #
101
- # Subject parsing may also be suppressed by setting parse_subject
102
- # to false.
103
- def parse(str, parse_subject=true) # :yields: fragment
104
- scanner = case str
105
- when StringScanner then str
106
- when String then StringScanner.new(str)
107
- else raise TypeError, "can't convert #{str.class} into StringScanner or String"
108
- end
109
-
110
- comment = Comment.new
111
- while scanner.scan(/\r?\n?[ \t]*#[ \t]?(([ \t]*).*?)\r?$/)
112
- fragment = scanner[1]
113
- indent = scanner[2]
114
-
115
- # collect continuous description line
116
- # fragments and join into a single line
117
- if block_given? && yield(fragment)
118
- # break on comment if the description end is reached
119
- parse_subject = false
120
- break
121
- else
122
- categorize(fragment, indent) {|f| comment.push(f) }
123
- end
124
- end
125
-
126
- if parse_subject
127
- scanner.skip(/\s+/)
128
- unless scanner.peek(1) == '#'
129
- comment.subject = scanner.scan(/.+?$/)
130
- comment.subject.strip! unless comment.subject == nil
131
- end
132
- end
133
-
134
- comment
135
- end
136
-
137
- # Scan determines if and how to add a line fragment to a comment and
138
- # yields the appropriate fragments to the block. Returns true if
139
- # fragments are yielded and false otherwise.
140
- #
141
- # Content may be built from an array of lines using scan like so:
142
- #
143
- # lines = [
144
- # "# comments spanning multiple",
145
- # "# lines are collected",
146
- # "#",
147
- # "# while indented lines",
148
- # "# are preserved individually",
149
- # "# ",
150
- # "not a comment line",
151
- # "# skipped since the loop breaks",
152
- # "# at the first non-comment line"]
153
- #
154
- # c = Comment.new
155
- # lines.each do |line|
156
- # break unless Comment.scan(line) do |fragment|
157
- # c.push(fragment)
158
- # end
159
- # end
160
- #
161
- # c.content
162
- # # => [
163
- # # ['comments spanning multiple', 'lines are collected'],
164
- # # [''],
165
- # # [' while indented lines'],
166
- # # [' are preserved individually'],
167
- # # [''],
168
- # # []]
169
- #
170
- def scan(line) # :yields: fragment
171
- return false unless line =~ /^[ \t]*#[ \t]?(([ \t]*).*?)\r?$/
172
- categorize($1, $2) do |fragment|
173
- yield(fragment)
174
- end
175
- true
176
- end
177
-
178
- # Splits a line of text along whitespace breaks into fragments of cols
179
- # width. Tabs in the line will be expanded into tabsize spaces;
180
- # fragments are rstripped of whitespace.
181
- #
182
- # Comment.wrap("some line that will wrap", 10) # => ["some line", "that will", "wrap"]
183
- # Comment.wrap(" line that will wrap ", 10) # => [" line", "that will", "wrap"]
184
- # Comment.wrap(" ", 10) # => []
185
- #
186
- # The wrapping algorithm is slightly modified from:
187
- # http://blog.macromates.com/2006/wrapping-text-with-regular-expressions/
188
- def wrap(line, cols=80, tabsize=2)
189
- line = line.gsub(/\t/, " " * tabsize) unless tabsize == nil
190
- line.gsub(/(.{1,#{cols}})( +|$\r?\n?)|(.{1,#{cols}})/, "\\1\\3\n").split(/\s*?\n/)
191
- end
192
-
193
- private
194
-
195
- # utility method used by scan to categorize and yield
196
- # the appropriate objects to add the fragment to a
197
- # comment
198
- def categorize(fragment, indent) # :nodoc:
199
- case
200
- when fragment == indent
201
- # empty comment line
202
- yield [""]
203
- yield []
204
- when indent.empty?
205
- # continuation line
206
- yield fragment.rstrip
207
- else
208
- # indented line
209
- yield [fragment.rstrip]
210
- yield []
211
- end
212
- end
213
- end
214
-
215
- # An array of comment fragments organized into lines
216
- attr_reader :content
217
-
218
- # The subject of the comment (normally set to the next
219
- # non-comment line after the content ends; ie the line
220
- # that would receive the comment in RDoc documentation)
221
- attr_accessor :subject
222
-
223
- # Returns the line number for the subject line, if known.
224
- # Although normally an integer, line_number may be
225
- # set to a Regexp or Proc to dynamically determine
226
- # the subject line during resolve
227
- attr_accessor :line_number
228
-
229
- def initialize(line_number=nil)
230
- @content = []
231
- @subject = nil
232
- @line_number = line_number
233
- end
234
-
235
- # Alias for subject
236
- def value
237
- subject
238
- end
239
-
240
- # Alias for subject=
241
- def value=(value)
242
- self.subject = value
243
- end
244
-
245
- # Pushes the fragment onto the last line array of content. If the
246
- # fragment is an array itself then it will be pushed onto content
247
- # as a new line.
248
- #
249
- # c = Comment.new
250
- # c.push "some line"
251
- # c.push "fragments"
252
- # c.push ["a", "whole", "new line"]
253
- #
254
- # c.content
255
- # # => [
256
- # # ["some line", "fragments"],
257
- # # ["a", "whole", "new line"]]
258
- #
259
- def push(fragment)
260
- content << [] if content.empty?
261
-
262
- case fragment
263
- when Array
264
- if content[-1].empty?
265
- content[-1] = fragment
266
- else
267
- content.push fragment
268
- end
269
- else
270
- content[-1].push fragment
271
- end
272
- end
273
-
274
- # Alias for push.
275
- def <<(fragment)
276
- push(fragment)
277
- end
278
-
279
- # Scans the comment line using Comment.scan and pushes the appropriate
280
- # fragments onto self. Used to build a content by scanning down a set
281
- # of lines.
282
- #
283
- # lines = [
284
- # "# comment spanning multiple",
285
- # "# lines",
286
- # "#",
287
- # "# indented line one",
288
- # "# indented line two",
289
- # "# ",
290
- # "not a comment line"]
291
- #
292
- # c = Comment.new
293
- # lines.each {|line| c.append(line) }
294
- #
295
- # c.content
296
- # # => [
297
- # # ['comment spanning multiple', 'lines'],
298
- # # [''],
299
- # # [' indented line one'],
300
- # # [' indented line two'],
301
- # # [''],
302
- # # []]
303
- #
304
- def append(line)
305
- Comment.scan(line) {|f| push(f) }
306
- end
307
-
308
- # Unshifts the fragment to the first line array of content. If the
309
- # fragment is an array itself then it will be unshifted onto content
310
- # as a new line.
311
- #
312
- # c = Comment.new
313
- # c.unshift "some line"
314
- # c.unshift "fragments"
315
- # c.unshift ["a", "whole", "new line"]
316
- #
317
- # c.content
318
- # # => [
319
- # # ["a", "whole", "new line"],
320
- # # ["fragments", "some line"]]
321
- #
322
- def unshift(fragment)
323
- content << [] if content.empty?
324
-
325
- case fragment
326
- when Array
327
- if content[0].empty?
328
- content[0] = fragment
329
- else
330
- content.unshift fragment
331
- end
332
- else
333
- content[0].unshift fragment
334
- end
335
- end
336
-
337
- # Scans the comment line using Comment.scan and unshifts the appropriate
338
- # fragments onto self. Used to build a content by scanning up a set of
339
- # lines.
340
- #
341
- # lines = [
342
- # "# comment spanning multiple",
343
- # "# lines",
344
- # "#",
345
- # "# indented line one",
346
- # "# indented line two",
347
- # "# ",
348
- # "not a comment line"]
349
- #
350
- # c = Comment.new
351
- # lines.reverse_each {|line| c.prepend(line) }
352
- #
353
- # c.content
354
- # # => [
355
- # # ['comment spanning multiple', 'lines'],
356
- # # [''],
357
- # # [' indented line one'],
358
- # # [' indented line two'],
359
- # # ['']]
360
- #
361
- def prepend(line)
362
- Comment.scan(line) {|f| unshift(f) }
363
- end
364
-
365
- # Builds the subject and content of self using lines; resolve sets
366
- # the subject to the line at line_number, and parses content up
367
- # from there. Any previously set subject and content is overridden.
368
- # Returns self.
369
- #
370
- # document = %Q{
371
- # module Sample
372
- # # this is the content of the comment
373
- # # for method_one
374
- # def method_one
375
- # end
376
- #
377
- # # this is the content of the comment
378
- # # for method_two
379
- # def method_two
380
- # end
381
- # end}
382
- #
383
- # c = Comment.new 4
384
- # c.resolve(document)
385
- # c.subject # => " def method_one"
386
- # c.content # => [["this is the content of the comment", "for method_one"]]
387
- #
388
- # Lines may be an array or a string; string inputs are split into an
389
- # array along newline boundaries.
390
- #
391
- # === dynamic line numbers
392
- # The line_number used by resolve may be determined dynamically from
393
- # lines by setting line_number to a Regexp and Proc. In the case
394
- # of a Regexp, the first line matching the regexp is used:
395
- #
396
- # c = Comment.new(/def method/)
397
- # c.resolve(document)
398
- # c.line_number = 4
399
- # c.subject # => " def method_one"
400
- # c.content # => [["this is the content of the comment", "for method_one"]]
401
- #
402
- # Procs are called with lines and are expected to return the
403
- # actual line number.
404
- #
405
- # c = Comment.new lambda {|lines| 9 }
406
- # c.resolve(document)
407
- # c.line_number = 9
408
- # c.subject # => " def method_two"
409
- # c.content # => [["this is the content of the comment", "for method_two"]]
410
- #
411
- # As shown in the examples, in both cases the dynamically determined
412
- # line_number overwrites the Regexp or Proc.
413
- def resolve(lines)
414
- lines = lines.split(/\r?\n/) if lines.kind_of?(String)
415
-
416
- # resolve late-evaluation line numbers
417
- n = case line_number
418
- when Regexp then match_index(line_number, lines)
419
- when Proc then line_number.call(lines)
420
- else line_number
421
- end
422
-
423
- # quietly exit if a line number was not found
424
- return self unless n.kind_of?(Integer)
425
-
426
- unless n < lines.length
427
- raise RangeError, "line_number outside of lines: #{line_number} (#{lines.length})"
428
- end
429
-
430
- self.line_number = n
431
- self.subject = lines[n]
432
- self.content.clear
433
-
434
- # remove whitespace lines
435
- n -= 1
436
- n -= 1 while n >=0 && lines[n].strip.empty?
437
-
438
- # put together the comment
439
- while n >= 0
440
- break unless prepend(lines[n])
441
- n -= 1
442
- end
443
-
444
- self
445
- end
446
-
447
- # Removes leading and trailing lines from content that are
448
- # empty ([]) or whitespace (['']). Returns self.
449
- def trim
450
- content.shift while !content.empty? && (content[0].empty? || content[0].join.strip.empty?)
451
- content.pop while !content.empty? && (content[-1].empty? || content[-1].join.strip.empty?)
452
- self
453
- end
454
-
455
- # True if all lines in content are empty.
456
- def empty?
457
- !content.find {|line| !line.empty?}
458
- end
459
-
460
- # Returns content as a string where line fragments are joined by
461
- # fragment_sep and lines are joined by line_sep.
462
- def to_s(fragment_sep=" ", line_sep="\n", strip=true)
463
- lines = content.collect {|line| line.join(fragment_sep)}
464
-
465
- # strip leading an trailing whitespace lines
466
- if strip
467
- lines.shift while !lines.empty? && lines[0].empty?
468
- lines.pop while !lines.empty? && lines[-1].empty?
469
- end
470
-
471
- line_sep ? lines.join(line_sep) : lines
472
- end
473
-
474
- # Like to_s, but wraps the content to the specified number of cols
475
- # and expands tabs to tabsize spaces.
476
- def wrap(cols=80, tabsize=2, line_sep="\n", fragment_sep=" ", strip=true)
477
- lines = Comment.wrap(to_s(fragment_sep, "\n", strip), cols, tabsize)
478
- line_sep ? lines.join(line_sep) : lines
479
- end
480
-
481
- # Returns true if another is a Comment with the same
482
- # line_number, subject, and content as self
483
- def ==(another)
484
- another.kind_of?(Comment) &&
485
- self.line_number == another.line_number &&
486
- self.subject == another.subject &&
487
- self.content == another.content
488
- end
489
-
490
- private
491
-
492
- # utility method used to by resolve to find the index
493
- # of a line matching a regexp line_number.
494
- def match_index(regexp, lines) # :nodoc:
495
- lines.each_with_index do |line, index|
496
- return index if line =~ regexp
497
- end
498
- nil
499
- end
500
- end
501
- end
502
- end
503
- end