bahuvrihi-tap 0.10.6 → 0.10.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/bin/rap +13 -5
  2. data/lib/tap.rb +0 -4
  3. data/lib/tap/app.rb +10 -138
  4. data/lib/tap/constants.rb +1 -1
  5. data/lib/tap/declarations.rb +201 -0
  6. data/lib/tap/env.rb +10 -2
  7. data/lib/tap/exe.rb +2 -2
  8. data/lib/tap/generator/generators/root/templates/Rakefile +1 -0
  9. data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +1 -1
  10. data/lib/tap/spec.rb +38 -63
  11. data/lib/tap/spec/adapter.rb +8 -31
  12. data/lib/tap/spec/inheritable_class_test_root.rb +9 -0
  13. data/lib/tap/support/audit.rb +0 -2
  14. data/lib/tap/support/combinator.rb +83 -31
  15. data/lib/tap/support/configurable_class.rb +7 -7
  16. data/lib/tap/support/{dependable.rb → dependencies.rb} +9 -7
  17. data/lib/tap/support/executable.rb +226 -19
  18. data/lib/tap/support/gems/rake.rb +6 -3
  19. data/lib/tap/support/join.rb +87 -0
  20. data/lib/tap/support/joins.rb +13 -0
  21. data/lib/tap/support/joins/fork.rb +18 -0
  22. data/lib/tap/support/joins/merge.rb +20 -0
  23. data/lib/tap/support/joins/sequence.rb +21 -0
  24. data/lib/tap/support/joins/switch.rb +23 -0
  25. data/lib/tap/support/joins/sync_merge.rb +57 -0
  26. data/lib/tap/support/lazydoc/document.rb +10 -0
  27. data/lib/tap/support/node.rb +58 -0
  28. data/lib/tap/support/parser.rb +379 -0
  29. data/lib/tap/support/schema.rb +350 -0
  30. data/lib/tap/support/tdoc.rb +5 -0
  31. data/lib/tap/support/validation.rb +2 -0
  32. data/lib/tap/task.rb +26 -61
  33. data/lib/tap/test.rb +9 -82
  34. data/lib/tap/test/assertions.rb +38 -0
  35. data/lib/tap/test/extensions.rb +78 -0
  36. data/lib/tap/test/{file_methods.rb → file_test.rb} +64 -37
  37. data/lib/tap/test/{file_methods_class.rb → file_test_class.rb} +2 -2
  38. data/lib/tap/test/regexp_escape.rb +87 -0
  39. data/lib/tap/test/script_test.rb +46 -0
  40. data/lib/tap/test/script_tester.rb +109 -0
  41. data/lib/tap/test/{subset_methods.rb → subset_test.rb} +9 -9
  42. data/lib/tap/test/{subset_methods_class.rb → subset_test_class.rb} +22 -14
  43. data/lib/tap/test/{tap_methods.rb → tap_test.rb} +43 -6
  44. data/lib/tap/test/utils.rb +6 -3
  45. metadata +27 -24
  46. data/lib/tap/parser.rb +0 -619
  47. data/lib/tap/spec/file_methods.rb +0 -16
  48. data/lib/tap/spec/file_methods_class.rb +0 -13
  49. data/lib/tap/spec/subset_methods.rb +0 -14
  50. data/lib/tap/support/batchable.rb +0 -47
  51. data/lib/tap/support/batchable_class.rb +0 -107
  52. data/lib/tap/support/declarations.rb +0 -131
  53. data/lib/tap/support/lazydoc/declaration.rb +0 -20
  54. data/lib/tap/support/parsers/base.rb +0 -81
  55. data/lib/tap/support/parsers/server.rb +0 -113
  56. data/lib/tap/test/script_methods.rb +0 -75
  57. data/lib/tap/test/script_methods/regexp_escape.rb +0 -94
  58. data/lib/tap/test/script_methods/script_test.rb +0 -109
  59. data/lib/tap/workflow.rb +0 -161
@@ -0,0 +1,350 @@
1
+ require 'tap/support/node'
2
+ require 'tap/support/joins'
3
+ autoload(:Shellwords, 'shellwords')
4
+
5
+ module Tap
6
+ module Support
7
+ autoload(:Parser, 'tap/support/parser')
8
+
9
+ class Schema
10
+ module Utils
11
+ module_function
12
+
13
+ # Shell quotes the input string by enclosing in quotes if
14
+ # str has no quotes, or double quotes if str has no double
15
+ # quotes. Returns the str if it has not whitespace, quotes
16
+ # or double quotes.
17
+ #
18
+ # Raises an ArgumentError if str has both quotes and double
19
+ # quotes.
20
+ def shell_quote(str)
21
+ return str unless str =~ /[\s'"]/
22
+
23
+ quote = str.include?("'")
24
+ double_quote = str.include?('"')
25
+
26
+ case
27
+ when !quote then "'#{str}'"
28
+ when !double_quote then "\"#{str}\""
29
+ else raise ArgumentError, "cannot shell quote: #{str}"
30
+ end
31
+ end
32
+
33
+ # Formats a round string.
34
+ #
35
+ # format_round(1, [1,2,3]) # => "+1[1,2,3]"
36
+ #
37
+ def format_round(round, indicies)
38
+ "+#{round}[#{indicies.join(',')}]"
39
+ end
40
+
41
+ # Formats a sequence string.
42
+ #
43
+ # format_sequence(1, [2,3], {}) # => "1:2:3"
44
+ #
45
+ def format_sequence(source_index, target_indicies, options)
46
+ ([source_index] + target_indicies).join(":") + format_options(options)
47
+ end
48
+
49
+ # Formats a global instance string.
50
+ #
51
+ # format_instance(1) # => "*1"
52
+ #
53
+ def format_instance(index)
54
+ "*#{index}"
55
+ end
56
+
57
+ # Formats a fork string.
58
+ #
59
+ # format_fork(1, [2,3],{}) # => "1[2,3]"
60
+ #
61
+ def format_fork(source_index, target_indicies, options)
62
+ "#{source_index}[#{target_indicies.join(',')}]#{format_options(options)}"
63
+ end
64
+
65
+ # Formats a merge string (note the target index is
66
+ # provided first).
67
+ #
68
+ # format_merge(1, [2,3],{}) # => "1{2,3}"
69
+ #
70
+ def format_merge(target_index, source_indicies, options)
71
+ "#{target_index}{#{source_indicies.join(',')}}#{format_options(options)}"
72
+ end
73
+
74
+ # Formats a sync_merge string (note the target index
75
+ # is provided first).
76
+ #
77
+ # format_sync_merge(1, [2,3],{}) # => "1(2,3)"
78
+ #
79
+ def format_sync_merge(target_index, source_indicies, options)
80
+ "#{target_index}(#{source_indicies.join(',')})#{format_options(options)}"
81
+ end
82
+
83
+ # Formats an options hash into a string. Raises an error
84
+ # for unknown options.
85
+ #
86
+ # format_options({:iterate => true}) # => "i"
87
+ #
88
+ def format_options(options)
89
+ options_str = []
90
+ options.each_pair do |key, value|
91
+ unless index = Join::FLAGS.index(key)
92
+ raise "unknown key in: #{options} (#{key})"
93
+ end
94
+
95
+ if value
96
+ options_str << Join::SHORT_FLAGS[index]
97
+ end
98
+ end
99
+ options_str.sort.join
100
+ end
101
+ end
102
+
103
+ include Utils
104
+
105
+ class << self
106
+ def parse(argv=ARGV)
107
+ Support::Parser.new(argv).schema
108
+ end
109
+
110
+ def load(argv)
111
+ parser = Parser.new
112
+ parser.load(argv)
113
+ parser.schema
114
+ end
115
+
116
+ def load_file(path)
117
+ argv = YAML.load_file(path)
118
+ load(argv)
119
+ end
120
+ end
121
+
122
+ # An array of the nodes registered in self.
123
+ attr_reader :nodes
124
+
125
+ def initialize(nodes=[])
126
+ @nodes = nodes
127
+ @current_index = 1
128
+ end
129
+
130
+ # Retrieves the node at index, or instantiates
131
+ # a new Node if one does not already exists.
132
+ def [](index)
133
+ nodes[index] ||= Node.new
134
+ end
135
+
136
+ # Sets a join between the source and targets.
137
+ # Returns the new join.
138
+ def set(join_class, source_indicies, target_indicies, options={})
139
+ join = join_class.new(options)
140
+
141
+ [*source_indicies].each {|source_index| self[source_index].output = join }
142
+ [*target_indicies].each {|target_index| self[target_index].input = join }
143
+
144
+ join
145
+ end
146
+
147
+ # Removes all nil nodes, and nodes with empty argvs.
148
+ # Additionally reassigns rounds by shifting later
149
+ # rounds up to fill any nils in the rounds array.
150
+ #
151
+ # Returns self.
152
+ def compact
153
+ # remove nil and empty nodes
154
+ nodes.delete_if do |node|
155
+ node == nil || node.argv.empty?
156
+ end
157
+
158
+ # reassign rounds
159
+ index = 0
160
+ rounds.compact.each do |round|
161
+ round.each {|node| node.round = index }
162
+ index += 1
163
+ end
164
+
165
+ self
166
+ end
167
+
168
+ # Returns an array of the argvs for each nodes.
169
+ def argvs
170
+ nodes.collect do |node|
171
+ node == nil ? nil : node.argv
172
+ end
173
+ end
174
+
175
+ # Returns a collection of nodes sorted
176
+ # into arrays by node.round.
177
+ def rounds(as_indicies=false)
178
+ rounds = []
179
+ nodes.each do |node|
180
+ (rounds[node.round] ||= []) << node if node && node.round
181
+ end
182
+
183
+ rounds.each do |round|
184
+ next unless round
185
+ round.collect! {|node| nodes.index(node) }
186
+ end if as_indicies
187
+
188
+ rounds
189
+ end
190
+
191
+ # Returns a collection of global nodes
192
+ # (nodes with no input or output set).
193
+ def globals(as_indicies=false)
194
+ globals = []
195
+ nodes.each do |node|
196
+ globals << node if node && node.global?
197
+ end
198
+
199
+ globals.collect! do |node|
200
+ nodes.index(node)
201
+ end if as_indicies
202
+
203
+ globals
204
+ end
205
+
206
+ # Returns a hash of [join, [source_node, target_nodes]] pairs
207
+ # across all nodes.
208
+ def joins(as_indicies=false)
209
+ joins = {}
210
+ nodes.each do |node|
211
+ next unless node
212
+
213
+ case node.input
214
+ when Join, ReverseJoin
215
+ (joins[node.input] ||= [nil,[]])[1] << node
216
+ end
217
+
218
+ case node.output
219
+ when Join, ReverseJoin
220
+ (joins[node.output] ||= [nil,[]])[0] = node
221
+ end
222
+ end
223
+
224
+ if as_indicies
225
+ summary = []
226
+ joins.each_pair do |join, (source_node, target_nodes)|
227
+ target_indicies = target_nodes.collect {|node| nodes.index(node) }
228
+ summary << [join.name, nodes.index(source_node), target_indicies, join.options]
229
+ end
230
+
231
+ summary.sort_by {|entry| entry[1] || -1 }
232
+ else
233
+ joins
234
+ end
235
+ end
236
+
237
+ def build(app)
238
+ tasks = {}
239
+
240
+ # instantiate the nodes
241
+ nodes.each do |node|
242
+ tasks[node] = yield(node.argv) if node
243
+ end
244
+
245
+ # instantiate and reconfigure globals
246
+ globals.each do |node|
247
+ task, args = tasks[node]
248
+ task.class.instance.reconfigure(task.config.to_hash)
249
+ end
250
+
251
+ # build the workflow
252
+ joins.each_pair do |join, (source_node, target_nodes)|
253
+ raise "unassigned join: #{join}" if source_node == nil
254
+
255
+ targets = target_nodes.collect do |target_node|
256
+ tasks[target_node][0]
257
+ end
258
+ source = tasks[source_node][0]
259
+
260
+ join.join(source, targets)
261
+ end
262
+
263
+ # build queues
264
+ queues = rounds.compact.collect do |round|
265
+ round.each do |node|
266
+ task, args = tasks.delete(node)
267
+ task.enq(*args)
268
+ end
269
+
270
+ app.queue.clear
271
+ end
272
+
273
+ # notify any args that will be overlooked
274
+ tasks.each_pair do |node, (task, args)|
275
+ next if args.empty?
276
+ warn "warning: ignoring args for node (#{nodes.index(node)}) #{task} [#{args.join(' ')}]"
277
+ end
278
+
279
+ queues
280
+ end
281
+
282
+ # Creates an array dump of the contents of self.
283
+ def dump
284
+ segments = argvs
285
+ each_schema_str {|str| segments << str }
286
+ segments
287
+ end
288
+
289
+ # Constructs a command-line string for the schema, ex:
290
+ # '-- a -- b --0:1'.
291
+ def to_s
292
+ segments = []
293
+ nodes.each do |node|
294
+ segments << "--"
295
+
296
+ node.argv.each do |arg|
297
+ segments << shell_quote(arg)
298
+ end unless node == nil
299
+ end
300
+
301
+ each_schema_str {|str| segments << "--#{str}" }
302
+ segments.join(' ')
303
+ end
304
+
305
+ protected
306
+
307
+ # Yields each formatted schema string (global, round, and join).
308
+ def each_schema_str # :nodoc:
309
+ each_globals_str {|str| yield str }
310
+ each_round_str {|str| yield str }
311
+ each_join_str {|str| yield str }
312
+ end
313
+
314
+ # Yields globals formatted as a string.
315
+ def each_globals_str # :nodoc:
316
+ globals.each do |node|
317
+ yield format_instance(nodes.index(node))
318
+ end
319
+ end
320
+
321
+ # Yields each round formatted as a string.
322
+ def each_round_str # :nodoc
323
+ index = 0
324
+ rounds.each do |indicies|
325
+ unless indicies == nil || index == 0
326
+ indicies = indicies.collect {|node| nodes.index(node) }
327
+ yield format_round(index, indicies)
328
+ end
329
+ index += 1
330
+ end
331
+ end
332
+
333
+ # Yields each join formatted as a string.
334
+ def each_join_str # :nodoc
335
+ joins.each_pair do |join, (source_node, target_nodes)|
336
+ source_index = nodes.index(source_node)
337
+ target_indicies = target_nodes.collect {|node| nodes.index(node) }
338
+
339
+ yield case join
340
+ when Joins::Sequence then format_sequence(source_index, target_indicies, join.options)
341
+ when Joins::Fork then format_fork(source_index, target_indicies, join.options)
342
+ when Joins::Merge then format_merge(source_index, target_indicies, join.options)
343
+ when Joins::SyncMerge then format_sync_merge(source_index, target_indicies, join.options)
344
+ else raise "unknown join type: #{join.class} (#{source_index}, [#{target_indicies.join(',')}])"
345
+ end
346
+ end
347
+ end
348
+ end
349
+ end
350
+ end
@@ -47,6 +47,11 @@ else
47
47
  end
48
48
  end
49
49
 
50
+ unless Object.const_defined?(:TokenStream)
51
+ TokenStream = RDoc::TokenStream
52
+ Options = RDoc::Options
53
+ end
54
+
50
55
  module Tap
51
56
  module Support
52
57
 
@@ -1,3 +1,5 @@
1
+ autoload(:PP, 'pp')
2
+
1
3
  module Tap
2
4
  module Support
3
5
 
data/lib/tap/task.rb CHANGED
@@ -1,4 +1,3 @@
1
- require 'tap/support/batchable'
2
1
  require 'tap/support/executable'
3
2
  require 'tap/support/lazydoc/method'
4
3
  autoload(:OptionParser, 'optparse')
@@ -158,7 +157,6 @@ module Tap
158
157
  # t1.array.object_id == t2.array.object_id # => false
159
158
  #
160
159
  class Task
161
- include Support::Batchable
162
160
  include Support::Configurable
163
161
  include Support::Executable
164
162
 
@@ -357,9 +355,9 @@ module Tap
357
355
  depends_on(dependency_class, *args)
358
356
 
359
357
  define_method(name) do
360
- index = Support::Executable.index(dependency_class.instance, args)
361
- Support::Executable.resolve([index])
362
- Support::Executable.results[index]._current
358
+ index = app.dependencies.index(dependency_class.instance, args)
359
+ app.dependencies.resolve([index])
360
+ app.dependencies.results[index]._current
363
361
  end
364
362
 
365
363
  public(name)
@@ -420,10 +418,6 @@ module Tap
420
418
  lazy_attr :manifest
421
419
  lazy_attr :args
422
420
 
423
- # The application used to load config_file templates
424
- # (and hence, to initialize batched objects).
425
- attr_reader :app
426
-
427
421
  # The name of self.
428
422
  #--
429
423
  # Currently names may be any object. Audit makes use of name
@@ -439,14 +433,15 @@ module Tap
439
433
  # config_file = app.config_filepath(name).
440
434
  def initialize(config={}, name=nil, app=App.instance, &task_block)
441
435
  super()
442
-
443
- @app = app
436
+
444
437
  @name = name || self.class.default_name
445
438
  @task_block = (task_block == nil ? default_task_block : task_block)
446
439
 
447
- @_method_name = :execute
440
+ @app = app
441
+ @_method_name = :execute_with_callbacks
448
442
  @on_complete_block = nil
449
443
  @dependencies = []
444
+ @batch = [self]
450
445
 
451
446
  case config
452
447
  when Support::InstanceConfiguration
@@ -459,6 +454,8 @@ module Tap
459
454
  self.class.dependencies.each do |task_class, args|
460
455
  depends_on(task_class.instance, *args)
461
456
  end
457
+
458
+ workflow
462
459
  end
463
460
 
464
461
  # Creates a new batched object and adds the object to batch. The batched object
@@ -469,46 +466,6 @@ module Tap
469
466
  obj.name = name if name
470
467
  obj
471
468
  end
472
-
473
- # Enqueues self and self.batch to app with the inputs.
474
- # The number of inputs provided should match the number
475
- # of inputs specified by the arity of the _method_name method.
476
- def enq(*inputs)
477
- app.queue.enq(self, inputs)
478
- end
479
-
480
- batch_function :enq
481
- batch_function(:on_complete) {}
482
-
483
- # Convenience method, equivalent to:
484
- # self.app.sequence([self] + tasks)
485
- def sequence(*tasks)
486
- app.sequence([self] + tasks)
487
- end
488
-
489
- # Convenience method, equivalent to:
490
- # self.app.fork(self, targets)
491
- def fork(*targets)
492
- app.fork(self, targets)
493
- end
494
-
495
- # Convenience method, equivalent to:
496
- # self.app.merge(self, sources)
497
- def merge(*sources)
498
- app.merge(self, sources)
499
- end
500
-
501
- # Convenience method, equivalent to:
502
- # self.app.sync_merge(self, sources)
503
- def sync_merge(*sources)
504
- app.sync_merge(self, sources)
505
- end
506
-
507
- # Convenience method, equivalent to:
508
- # self.app.switch(self, targets, &block)
509
- def switch(*targets, &block)
510
- app.switch(self, targets, &block)
511
- end
512
469
 
513
470
  # Executes self with the given inputs. Execute provides hooks for subclasses
514
471
  # to insert standard execution code: before_execute, on_execute_error,
@@ -516,15 +473,7 @@ module Tap
516
473
  #
517
474
  # Execute passes the inputs to process and returns the result.
518
475
  def execute(*inputs)
519
- before_execute
520
- begin
521
- result = process(*inputs)
522
- rescue
523
- on_execute_error($!)
524
- end
525
- after_execute
526
-
527
- result
476
+ _execute(*inputs)._current
528
477
  end
529
478
 
530
479
  # The method for processing inputs into outputs. Override this method in
@@ -601,6 +550,10 @@ module Tap
601
550
  nil
602
551
  end
603
552
 
553
+ # Hook to define a workflow for defined tasks.
554
+ def workflow
555
+ end
556
+
604
557
  # Hook to execute code before inputs are processed.
605
558
  def before_execute() end
606
559
 
@@ -615,6 +568,18 @@ module Tap
615
568
 
616
569
  private
617
570
 
571
+ def execute_with_callbacks(*inputs)
572
+ before_execute
573
+ begin
574
+ result = process(*inputs)
575
+ rescue
576
+ on_execute_error($!)
577
+ end
578
+ after_execute
579
+
580
+ result
581
+ end
582
+
618
583
  def config_task(name, klass=Tap::Task, &block)
619
584
  configs = config[name] || {}
620
585
  raise ArgumentError, "config '#{name}' is not a hash" unless configs.kind_of?(Hash)