bahuvrihi-tap 0.11.2 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/bin/rap +2 -3
  2. data/bin/tap +1 -1
  3. data/cmd/console.rb +2 -2
  4. data/cmd/manifest.rb +2 -2
  5. data/cmd/run.rb +7 -9
  6. data/cmd/server.rb +5 -5
  7. data/doc/Class Reference +17 -20
  8. data/doc/Tutorial +5 -7
  9. data/lib/tap.rb +2 -0
  10. data/lib/tap/app.rb +21 -31
  11. data/lib/tap/constants.rb +2 -2
  12. data/lib/tap/declarations.rb +85 -97
  13. data/lib/tap/declarations/declaration_task.rb +58 -0
  14. data/lib/tap/declarations/description.rb +24 -0
  15. data/lib/tap/env.rb +20 -16
  16. data/lib/tap/exe.rb +2 -2
  17. data/lib/tap/file_task.rb +224 -410
  18. data/lib/tap/generator/arguments.rb +9 -0
  19. data/lib/tap/generator/base.rb +105 -28
  20. data/lib/tap/generator/destroy.rb +29 -12
  21. data/lib/tap/generator/generate.rb +55 -39
  22. data/lib/tap/generator/generators/command/templates/command.erb +3 -3
  23. data/lib/tap/generator/generators/config/config_generator.rb +34 -3
  24. data/lib/tap/generator/generators/root/root_generator.rb +6 -9
  25. data/lib/tap/generator/generators/root/templates/Rakefile +4 -4
  26. data/lib/tap/generator/generators/task/templates/test.erb +1 -1
  27. data/lib/tap/root.rb +211 -156
  28. data/lib/tap/support/aggregator.rb +6 -9
  29. data/lib/tap/support/audit.rb +278 -357
  30. data/lib/tap/support/constant_manifest.rb +24 -21
  31. data/lib/tap/support/dependency.rb +1 -1
  32. data/lib/tap/support/executable.rb +26 -48
  33. data/lib/tap/support/join.rb +44 -19
  34. data/lib/tap/support/joins/sync_merge.rb +3 -5
  35. data/lib/tap/support/parser.rb +1 -1
  36. data/lib/tap/task.rb +195 -150
  37. data/lib/tap/tasks/dump.rb +2 -2
  38. data/lib/tap/test/extensions.rb +11 -13
  39. data/lib/tap/test/file_test.rb +71 -129
  40. data/lib/tap/test/file_test_class.rb +4 -1
  41. data/lib/tap/test/tap_test.rb +26 -154
  42. metadata +15 -22
  43. data/lib/tap/patches/optparse/summarize.rb +0 -62
  44. data/lib/tap/support/assignments.rb +0 -173
  45. data/lib/tap/support/class_configuration.rb +0 -182
  46. data/lib/tap/support/configurable.rb +0 -113
  47. data/lib/tap/support/configurable_class.rb +0 -271
  48. data/lib/tap/support/configuration.rb +0 -170
  49. data/lib/tap/support/instance_configuration.rb +0 -173
  50. data/lib/tap/support/lazydoc.rb +0 -386
  51. data/lib/tap/support/lazydoc/attributes.rb +0 -48
  52. data/lib/tap/support/lazydoc/comment.rb +0 -503
  53. data/lib/tap/support/lazydoc/config.rb +0 -17
  54. data/lib/tap/support/lazydoc/definition.rb +0 -36
  55. data/lib/tap/support/lazydoc/document.rb +0 -152
  56. data/lib/tap/support/lazydoc/method.rb +0 -24
  57. data/lib/tap/support/tdoc.rb +0 -409
  58. data/lib/tap/support/tdoc/tdoc_html_generator.rb +0 -38
  59. data/lib/tap/support/tdoc/tdoc_html_template.rb +0 -42
  60. data/lib/tap/support/validation.rb +0 -479
@@ -1,48 +0,0 @@
1
- require 'tap/support/lazydoc'
2
-
3
- module Tap
4
- module Support
5
- module Lazydoc
6
- # Attributes adds methods to declare class-level accessors
7
- # for Lazydoc attributes. The source_file for the class must
8
- # be set manually.
9
- #
10
- # # ConstName::key value
11
- # class ConstName
12
- # class << self
13
- # include Lazydoc::Attributes
14
- # end
15
- #
16
- # self.source_file = __FILE__
17
- # lazy_attr :key
18
- # end
19
- #
20
- # ConstName::key.subject # => 'value'
21
- #
22
- module Attributes
23
-
24
- # The source_file for self. Must be set independently.
25
- attr_accessor :source_file
26
-
27
- # Returns the lazydoc for source_file
28
- def lazydoc(resolve=true)
29
- lazydoc = Lazydoc[source_file]
30
- lazydoc.resolve if resolve
31
- lazydoc
32
- end
33
-
34
- # Creates a lazy attribute accessor for the specified attribute.
35
- def lazy_attr(key, attribute=key)
36
- instance_eval %Q{
37
- def #{key}
38
- lazydoc[to_s]['#{attribute}'] ||= Lazydoc::Comment.new
39
- end
40
-
41
- def #{key}=(comment)
42
- Lazydoc[source_file][to_s]['#{attribute}'] = comment
43
- end}
44
- end
45
- end
46
- end
47
- end
48
- end
@@ -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