bahuvrihi-tap 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. data/History +69 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +119 -0
  4. data/bin/tap +114 -0
  5. data/cmd/console.rb +42 -0
  6. data/cmd/destroy.rb +16 -0
  7. data/cmd/generate.rb +16 -0
  8. data/cmd/run.rb +126 -0
  9. data/doc/Class Reference +362 -0
  10. data/doc/Command Reference +153 -0
  11. data/doc/Tutorial +237 -0
  12. data/lib/tap.rb +32 -0
  13. data/lib/tap/app.rb +720 -0
  14. data/lib/tap/constants.rb +8 -0
  15. data/lib/tap/env.rb +640 -0
  16. data/lib/tap/file_task.rb +547 -0
  17. data/lib/tap/generator/base.rb +109 -0
  18. data/lib/tap/generator/destroy.rb +37 -0
  19. data/lib/tap/generator/generate.rb +61 -0
  20. data/lib/tap/generator/generators/command/command_generator.rb +21 -0
  21. data/lib/tap/generator/generators/command/templates/command.erb +32 -0
  22. data/lib/tap/generator/generators/config/config_generator.rb +26 -0
  23. data/lib/tap/generator/generators/config/templates/doc.erb +12 -0
  24. data/lib/tap/generator/generators/config/templates/nodoc.erb +8 -0
  25. data/lib/tap/generator/generators/file_task/file_task_generator.rb +27 -0
  26. data/lib/tap/generator/generators/file_task/templates/file.txt +11 -0
  27. data/lib/tap/generator/generators/file_task/templates/result.yml +6 -0
  28. data/lib/tap/generator/generators/file_task/templates/task.erb +33 -0
  29. data/lib/tap/generator/generators/file_task/templates/test.erb +29 -0
  30. data/lib/tap/generator/generators/root/root_generator.rb +55 -0
  31. data/lib/tap/generator/generators/root/templates/Rakefile +86 -0
  32. data/lib/tap/generator/generators/root/templates/gemspec +27 -0
  33. data/lib/tap/generator/generators/root/templates/tapfile +8 -0
  34. data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +3 -0
  35. data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +5 -0
  36. data/lib/tap/generator/generators/root/templates/test/tapfile_test.rb +15 -0
  37. data/lib/tap/generator/generators/task/task_generator.rb +27 -0
  38. data/lib/tap/generator/generators/task/templates/task.erb +14 -0
  39. data/lib/tap/generator/generators/task/templates/test.erb +21 -0
  40. data/lib/tap/generator/manifest.rb +14 -0
  41. data/lib/tap/patches/rake/rake_test_loader.rb +8 -0
  42. data/lib/tap/patches/rake/testtask.rb +55 -0
  43. data/lib/tap/patches/ruby19/backtrace_filter.rb +51 -0
  44. data/lib/tap/patches/ruby19/parsedate.rb +16 -0
  45. data/lib/tap/root.rb +581 -0
  46. data/lib/tap/support/aggregator.rb +55 -0
  47. data/lib/tap/support/assignments.rb +172 -0
  48. data/lib/tap/support/audit.rb +418 -0
  49. data/lib/tap/support/batchable.rb +47 -0
  50. data/lib/tap/support/batchable_class.rb +107 -0
  51. data/lib/tap/support/class_configuration.rb +194 -0
  52. data/lib/tap/support/command_line.rb +98 -0
  53. data/lib/tap/support/comment.rb +270 -0
  54. data/lib/tap/support/configurable.rb +114 -0
  55. data/lib/tap/support/configurable_class.rb +296 -0
  56. data/lib/tap/support/configuration.rb +122 -0
  57. data/lib/tap/support/constant.rb +70 -0
  58. data/lib/tap/support/constant_utils.rb +127 -0
  59. data/lib/tap/support/declarations.rb +111 -0
  60. data/lib/tap/support/executable.rb +111 -0
  61. data/lib/tap/support/executable_queue.rb +82 -0
  62. data/lib/tap/support/framework.rb +71 -0
  63. data/lib/tap/support/framework_class.rb +199 -0
  64. data/lib/tap/support/instance_configuration.rb +147 -0
  65. data/lib/tap/support/lazydoc.rb +428 -0
  66. data/lib/tap/support/manifest.rb +89 -0
  67. data/lib/tap/support/run_error.rb +39 -0
  68. data/lib/tap/support/shell_utils.rb +71 -0
  69. data/lib/tap/support/summary.rb +30 -0
  70. data/lib/tap/support/tdoc.rb +404 -0
  71. data/lib/tap/support/tdoc/tdoc_html_generator.rb +38 -0
  72. data/lib/tap/support/tdoc/tdoc_html_template.rb +42 -0
  73. data/lib/tap/support/templater.rb +180 -0
  74. data/lib/tap/support/validation.rb +410 -0
  75. data/lib/tap/support/versions.rb +97 -0
  76. data/lib/tap/task.rb +259 -0
  77. data/lib/tap/tasks/dump.rb +56 -0
  78. data/lib/tap/tasks/rake.rb +93 -0
  79. data/lib/tap/test.rb +37 -0
  80. data/lib/tap/test/env_vars.rb +29 -0
  81. data/lib/tap/test/file_methods.rb +377 -0
  82. data/lib/tap/test/script_methods.rb +144 -0
  83. data/lib/tap/test/subset_methods.rb +420 -0
  84. data/lib/tap/test/tap_methods.rb +237 -0
  85. data/lib/tap/workflow.rb +187 -0
  86. metadata +145 -0
@@ -0,0 +1,428 @@
1
+ require 'tap/support/comment'
2
+
3
+ module Tap
4
+ module Support
5
+
6
+ # Lazydoc scans source files to pull out documentation. Lazydoc can find two
7
+ # types of documentation, constant attributes and code comments.
8
+ #
9
+ # === Constant Attributes
10
+ #
11
+ # Constant attributes are designated the same as constants in Ruby, but with
12
+ # an extra 'key' constant that must consist of only lowercase letters and/or
13
+ # underscores. This format assures that attributes are sytactically invalid
14
+ # outside of comments.
15
+ #
16
+ # When Lazydoc finds an attribute it parses a Comment value where the subject
17
+ # is the remainder of the line, and comment lines are parsed down until a
18
+ # non-comment line, an end key, or a new attribute is reached.
19
+ #
20
+ # str = %Q{
21
+ # # Const::Name::key subject for key
22
+ # # comment for key
23
+ # # parsed until a non-comment line
24
+ #
25
+ # # Const::Name::another subject for another
26
+ # # comment for another
27
+ # # parsed to an end key
28
+ # # Const::Name::another-
29
+ # #
30
+ # # ignored comment
31
+ # }
32
+ #
33
+ # lazydoc = Lazydoc.new
34
+ # lazydoc.resolve(str)
35
+ #
36
+ # lazydoc.to_hash {|comment| [comment.subject, comment.to_s] }
37
+ # # => {'Const::Name' => {
38
+ # # 'key' => ['subject for key', 'comment for key parsed until a non-comment line'],
39
+ # # 'another' => ['subject for another', 'comment for another parsed to an end key']
40
+ # # }}
41
+ #
42
+ # A constant name does not need to be specified; when no constant name is
43
+ # specified, Lazydoc will store the key as a default for the document. To
44
+ # turn off attribute parsing for a section of documentation, use start/stop
45
+ # keys:
46
+ #
47
+ # str = %Q{
48
+ # # :::-
49
+ # # Const::Name::not_parsed
50
+ # # :::+
51
+ #
52
+ # # Const::Name::parsed subject
53
+ # }
54
+ #
55
+ # lazydoc = Lazydoc.new
56
+ # lazydoc.resolve(str)
57
+ # lazydoc.to_hash {|comment| comment.subject } # => {'Const::Name' => {'parsed' => 'subject'}}
58
+ #
59
+ # ==== startdoc
60
+ #
61
+ # Lazydoc is completely separate from RDoc, but the syntax of Lazydoc was developed
62
+ # with RDoc in mind. To hide attributes in one line, make use of the RDoc
63
+ # <tt>:startdoc:</tt> document modifier like this (spaces added to keep them in the
64
+ # example):
65
+ #
66
+ # # :start doc::Const::Name::one hidden in RDoc
67
+ # # * This line is visible in RDoc.
68
+ # # :start doc::Const::Name::one-
69
+ # #
70
+ # #--
71
+ # # Const::Name::two
72
+ # # You can hide attribute comments like this.
73
+ # # Const::Name::two-
74
+ # #++
75
+ # #
76
+ # # * This line is also visible in RDoc.
77
+ #
78
+ # Here is the same text, actually in RDoc:
79
+ #
80
+ # :startdoc::Const::Name::one hidden in RDoc
81
+ # * This line is visible in RDoc.
82
+ # :startdoc::Const::Name::one-
83
+ #
84
+ #--
85
+ # Const::Name::two
86
+ # You can hide attribute comments like this.
87
+ # Const::Name::two-
88
+ #++
89
+ #
90
+ # * This line is also visible in RDoc.
91
+ #
92
+ # === Code Comments
93
+ #
94
+ # Code comments are lines marked for parsing if and when a Lazydoc gets resolved.
95
+ # Unlike constant attributes, the line is the subject of a code comment and
96
+ # comment lines are parsed up from it (effectively mimicking the behavior of
97
+ # RDoc).
98
+ #
99
+ # str = %Q{
100
+ # # comment lines for
101
+ # # the method
102
+ # def method
103
+ # end
104
+ #
105
+ # # as in RDoc, the comment can be
106
+ # # separated from the method
107
+ #
108
+ # def another_method
109
+ # end
110
+ # }
111
+ #
112
+ # lazydoc = Lazydoc.new
113
+ # lazydoc.register(3)
114
+ # lazydoc.register(9)
115
+ # lazydoc.resolve(str)
116
+ #
117
+ # lazydoc.code_comments.collect {|comment| [comment.subject, comment.to_s] }
118
+ # # => [
119
+ # # ['def method', 'comment lines for the method'],
120
+ # # ['def another_method', 'as in RDoc, the comment can be separated from the method']]
121
+ #
122
+ class Lazydoc
123
+
124
+ # A regexp matching an attribute start or end. For the match:
125
+ #
126
+ # $1:: const_name
127
+ # $3:: key
128
+ # $4:: end flag
129
+ #
130
+ ATTRIBUTE_REGEXP = /(::|([A-Z][A-z]*::)+)([a-z_]+)(-?)/
131
+
132
+ # A regexp matching constants.
133
+ CONSTANT_REGEXP = /(::|([A-Z][A-z]*::)+)/
134
+
135
+ class << self
136
+
137
+ # A hash of (source_file, lazydoc) pairs tracking the
138
+ # Lazydoc instance for the given source file.
139
+ def registry
140
+ @registry ||= []
141
+ end
142
+
143
+ # Returns the lazydoc in registry for the specified source file.
144
+ # If no such lazydoc exists, one will be created for it.
145
+ def [](source_file)
146
+ source_file = File.expand_path(source_file.to_s)
147
+ lazydoc = registry.find {|doc| doc.source_file == source_file }
148
+ if lazydoc == nil
149
+ lazydoc = new(source_file)
150
+ registry << lazydoc
151
+ end
152
+ lazydoc
153
+ end
154
+
155
+ # Register the specified line numbers to the lazydoc for source_file.
156
+ # Returns a CodeComment corresponding to the line.
157
+ def register(source_file, line_number)
158
+ Lazydoc[source_file].register(line_number)
159
+ end
160
+
161
+ # Resolves all lazydocs which include the specified code comments.
162
+ def resolve(code_comments)
163
+ registry.each do |doc|
164
+ next if (code_comments & doc.code_comments).empty?
165
+ doc.resolve
166
+ end
167
+ end
168
+
169
+ # Scans the specified file for attributes keyed by key and stores
170
+ # the resulting comments in the corresponding lazydoc.
171
+ # Returns the lazydoc.
172
+ def scan_doc(source_file, key)
173
+ lazydoc = nil
174
+ scan(File.read(source_file), key) do |const_name, attr_key, comment|
175
+ lazydoc = self[source_file] unless lazydoc
176
+ lazydoc.attributes(const_name)[attr_key] = comment
177
+ end
178
+ lazydoc
179
+ end
180
+
181
+ # Scans the string or StringScanner for attributes matching the key;
182
+ # keys may be patterns, they are incorporated into a regexp. Yields
183
+ # each (const_name, key, value) triplet to the mandatory block and
184
+ # skips regions delimited by the stop and start keys <tt>:-</tt>
185
+ # and <tt>:+</tt>.
186
+ #
187
+ # str = %Q{
188
+ # Const::Name::key value
189
+ # ::alt alt_value
190
+ #
191
+ # Ignored::Attribute::not_matched value
192
+ # :::-
193
+ # Also::Ignored::key value
194
+ # :::+
195
+ # Another::key another value
196
+ # }
197
+ #
198
+ # results = []
199
+ # Lazydoc.scan(str, 'key|alt') do |const_name, key, value|
200
+ # results << [const_name, key, value]
201
+ # end
202
+ #
203
+ # results
204
+ # # => [
205
+ # # ['Const::Name', 'key', 'value'],
206
+ # # ['', 'alt', 'alt_value'],
207
+ # # ['Another', 'key', 'another value']]
208
+ #
209
+ # Returns the StringScanner used during scanning.
210
+ def scan(str, key) # :yields: const_name, key, value
211
+ scanner = case str
212
+ when StringScanner then str
213
+ when String then StringScanner.new(str)
214
+ else raise TypeError, "can't convert #{str.class} into StringScanner or String"
215
+ end
216
+
217
+ regexp = /(#{key})([ \r\t-].*$|$)/
218
+ while !scanner.eos?
219
+ break if scanner.skip_until(CONSTANT_REGEXP) == nil
220
+ const_name = scanner[1]
221
+
222
+ case
223
+ when scanner.scan(regexp)
224
+ yield(const_name.chomp('::'), scanner[1], scanner[2].strip)
225
+ when scanner.scan(/:-/)
226
+ scanner.skip_until(/:\+/)
227
+ end
228
+ end
229
+
230
+ scanner
231
+ end
232
+
233
+ # Parses constant attributes from the string or StringScanner. Yields
234
+ # each (const_name, key, comment) triplet to the mandatory block
235
+ # and skips regions delimited by the stop and start keys <tt>:-</tt>
236
+ # and <tt>:+</tt>.
237
+ #
238
+ # str = %Q{
239
+ # # Const::Name::key subject for key
240
+ # # comment for key
241
+ #
242
+ # # :::-
243
+ # # Ignored::key value
244
+ # # :::+
245
+ #
246
+ # # Ignored text before attribute ::another subject for another
247
+ # # comment for another
248
+ # }
249
+ #
250
+ # results = []
251
+ # Lazydoc.parse(str) do |const_name, key, comment|
252
+ # results << [const_name, key, comment.subject, comment.to_s]
253
+ # end
254
+ #
255
+ # results
256
+ # # => [
257
+ # # ['Const::Name', 'key', 'subject for key', 'comment for key'],
258
+ # # ['', 'another', 'subject for another', 'comment for another']]
259
+ #
260
+ # Returns the StringScanner used during scanning.
261
+ def parse(str) # :yields: const_name, key, comment
262
+ scanner = case str
263
+ when StringScanner then str
264
+ when String then StringScanner.new(str)
265
+ else raise TypeError, "can't convert #{str.class} into StringScanner or String"
266
+ end
267
+
268
+ scan(scanner, '[a-z_]+') do |const_name, key, value|
269
+ comment = Comment.parse(scanner, false) do |line|
270
+ if line =~ /::/ && line =~ ATTRIBUTE_REGEXP
271
+ # rewind to capture the next attribute unless an end is specified.
272
+ scanner.unscan unless !$4.empty? && $1.chomp("::") == const_name && $3 == key
273
+ true
274
+ else false
275
+ end
276
+ end
277
+ comment.subject = value
278
+ yield(const_name, key, comment)
279
+ end
280
+ end
281
+ end
282
+
283
+ include Enumerable
284
+
285
+ # The source file for self, used in resolving comments and
286
+ # attributes.
287
+ attr_reader :source_file
288
+
289
+ # An array of Comment objects identifying lines resolved or
290
+ # to-be-resolved for self.
291
+ attr_reader :code_comments
292
+
293
+ # A hash of (const_name, attributes) pairs tracking the constant
294
+ # attributes resolved or to-be-resolved for self. Attributes
295
+ # are hashes of (key, comment) pairs.
296
+ attr_reader :const_attrs
297
+
298
+ def initialize(source_file=nil)
299
+ self.source_file = source_file
300
+ @code_comments = []
301
+ @const_attrs = {}
302
+ @resolved = false
303
+ end
304
+
305
+ # Sets the source file for self. Expands the source file path if necessary.
306
+ def source_file=(source_file)
307
+ @source_file = source_file == nil ? nil : File.expand_path(source_file)
308
+ end
309
+
310
+ # Returns the attributes for the specified const_name.
311
+ def attributes(const_name)
312
+ const_attrs[const_name] ||= {}
313
+ end
314
+
315
+ # Returns default document attributes (ie attributes(''))
316
+ def default_attributes
317
+ attributes('')
318
+ end
319
+
320
+ # Returns the attributes for const_name merged to default_attributes.
321
+ # Set merge_defaults to false to get just the attributes for const_name.
322
+ def [](const_name, merge_defaults=true)
323
+ merge_defaults ? default_attributes.merge(attributes(const_name)) : attributes(const_name)
324
+ end
325
+
326
+ # Yields each (const_name, attributes) pair to the block; const_names where
327
+ # the attributes are empty are skipped.
328
+ def each
329
+ const_attrs.each_pair do |const_name, attrs|
330
+ yield(const_name, attrs) unless attrs.empty?
331
+ end
332
+ end
333
+
334
+ # Returns true if the attributes for const_name are not empty.
335
+ def has_const?(const_name)
336
+ const_attrs.each_pair do |constname, attrs|
337
+ next unless constname == const_name
338
+ return !attrs.empty?
339
+ end
340
+
341
+ false
342
+ end
343
+
344
+ # Returns an array of the constant names in self, for which
345
+ # the constant attributes are not empty.
346
+ def const_names
347
+ names = []
348
+ const_attrs.each_pair do |const_name, attrs|
349
+ names << const_name unless attrs.empty?
350
+ end
351
+ names
352
+ end
353
+
354
+ # Register the specified line number to self. Returns a
355
+ # Comment object corresponding to the line.
356
+ def register(line_number)
357
+ comment = code_comments.find {|c| c.line_number == line_number }
358
+
359
+ if comment == nil
360
+ comment = Comment.new(line_number)
361
+ code_comments << comment
362
+ end
363
+
364
+ comment
365
+ end
366
+
367
+ # Returns true if the code_comments for source_file are frozen.
368
+ def resolved?
369
+ @resolved
370
+ end
371
+
372
+ attr_writer :resolved
373
+
374
+ def resolve(str=nil, comment_regexp=nil) # :yields: comment, match
375
+ return(false) if resolved?
376
+
377
+ if str == nil
378
+ raise ArgumentError, "no source file specified" unless source_file && File.exists?(source_file)
379
+ str = File.read(source_file)
380
+ end
381
+
382
+ Lazydoc.parse(str) do |const_name, key, comment|
383
+ attributes(const_name)[key] = comment
384
+ end
385
+
386
+ lines = str.split(/\r?\n/)
387
+ lines.each_with_index do |line, line_number|
388
+ next unless line =~ comment_regexp
389
+ comment = register(line_number)
390
+ yield(comment, $~) if block_given?
391
+ end unless comment_regexp == nil
392
+
393
+ code_comments.collect! do |comment|
394
+ line_number = comment.line_number
395
+ comment.subject = lines[line_number] if comment.subject == nil
396
+
397
+ # remove whitespace lines
398
+ line_number -= 1
399
+ while lines[line_number].strip.empty?
400
+ line_number -= 1
401
+ end
402
+
403
+ # put together the comment
404
+ while line_number >= 0
405
+ break unless comment.prepend(lines[line_number])
406
+ line_number -= 1
407
+ end
408
+
409
+ comment
410
+ end
411
+
412
+ @resolved = true
413
+ end
414
+
415
+ def to_hash
416
+ const_hash = {}
417
+ const_names.sort.each do |const_name|
418
+ attr_hash = {}
419
+ self[const_name, false].each_pair do |key, comment|
420
+ attr_hash[key] = (block_given? ? yield(comment) : comment)
421
+ end
422
+ const_hash[const_name] = attr_hash
423
+ end
424
+ const_hash
425
+ end
426
+ end
427
+ end
428
+ end
@@ -0,0 +1,89 @@
1
+ module Tap
2
+ module Support
3
+ class Manifest
4
+
5
+ class << self
6
+ def glob_method(name)
7
+ "manifest_glob_#{name}".to_sym
8
+ end
9
+
10
+ def map_method(name)
11
+ "manifest_map_#{name}".to_sym
12
+ end
13
+ end
14
+
15
+ DEFAULT_MAP_METHOD = :manifest_map
16
+
17
+ attr_reader :entries
18
+ attr_reader :map_method
19
+ attr_reader :paths
20
+ attr_reader :path_index
21
+
22
+ def initialize(name, source)
23
+ @entries = []
24
+
25
+ @map_method = Manifest.map_method(name)
26
+ @map_method = DEFAULT_MAP_METHOD if !source.respond_to?(@map_method)
27
+
28
+ @paths = source.send(Manifest.glob_method(name)).uniq
29
+ @path_index = 0
30
+ end
31
+
32
+ def complete?
33
+ @path_index == paths.length
34
+ end
35
+
36
+ def each_path
37
+ return(false) if complete?
38
+
39
+ n_to_skip = @path_index
40
+ paths.each do |context, path|
41
+ if n_to_skip > 0
42
+ n_to_skip -= 1
43
+ next
44
+ end
45
+
46
+ @path_index += 1
47
+ yield(context, path)
48
+ end
49
+
50
+ true
51
+ end
52
+
53
+ # Checks that the manifest does not already assign key a conflicting path,
54
+ # then adds the (key, path) pair to manifest.
55
+ def store(entry)
56
+ existing_key, existing_path = entries.find {|(key, path)| key == entry[0] }
57
+
58
+ if existing_key && existing_path != entry[1]
59
+ raise ManifestConflict, "multiple paths for key '#{existing_key}': ['#{existing_path}', '#{entry[1]}']"
60
+ end
61
+
62
+ entries << entry
63
+ end
64
+
65
+ def keys
66
+ entries.collect {|(key, value)| key }
67
+ end
68
+
69
+ def values
70
+ entries.collect {|(key, value)| value }
71
+ end
72
+
73
+ def mini_map
74
+ return [] if entries.empty?
75
+
76
+ hash = {}
77
+ Root.minimize(keys) do |path, mini_path|
78
+ hash[path] = mini_path
79
+ end
80
+
81
+ entries.collect {|path, value| [hash[path], value] }
82
+ end
83
+
84
+ # Raised when multiple paths are assigned to the same manifest key.
85
+ class ManifestConflict < StandardError
86
+ end
87
+ end
88
+ end
89
+ end