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.
- data/bin/rap +13 -5
- data/lib/tap.rb +0 -4
- data/lib/tap/app.rb +10 -138
- data/lib/tap/constants.rb +1 -1
- data/lib/tap/declarations.rb +201 -0
- data/lib/tap/env.rb +10 -2
- data/lib/tap/exe.rb +2 -2
- data/lib/tap/generator/generators/root/templates/Rakefile +1 -0
- data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +1 -1
- data/lib/tap/spec.rb +38 -63
- data/lib/tap/spec/adapter.rb +8 -31
- data/lib/tap/spec/inheritable_class_test_root.rb +9 -0
- data/lib/tap/support/audit.rb +0 -2
- data/lib/tap/support/combinator.rb +83 -31
- data/lib/tap/support/configurable_class.rb +7 -7
- data/lib/tap/support/{dependable.rb → dependencies.rb} +9 -7
- data/lib/tap/support/executable.rb +226 -19
- data/lib/tap/support/gems/rake.rb +6 -3
- data/lib/tap/support/join.rb +87 -0
- data/lib/tap/support/joins.rb +13 -0
- data/lib/tap/support/joins/fork.rb +18 -0
- data/lib/tap/support/joins/merge.rb +20 -0
- data/lib/tap/support/joins/sequence.rb +21 -0
- data/lib/tap/support/joins/switch.rb +23 -0
- data/lib/tap/support/joins/sync_merge.rb +57 -0
- data/lib/tap/support/lazydoc/document.rb +10 -0
- data/lib/tap/support/node.rb +58 -0
- data/lib/tap/support/parser.rb +379 -0
- data/lib/tap/support/schema.rb +350 -0
- data/lib/tap/support/tdoc.rb +5 -0
- data/lib/tap/support/validation.rb +2 -0
- data/lib/tap/task.rb +26 -61
- data/lib/tap/test.rb +9 -82
- data/lib/tap/test/assertions.rb +38 -0
- data/lib/tap/test/extensions.rb +78 -0
- data/lib/tap/test/{file_methods.rb → file_test.rb} +64 -37
- data/lib/tap/test/{file_methods_class.rb → file_test_class.rb} +2 -2
- data/lib/tap/test/regexp_escape.rb +87 -0
- data/lib/tap/test/script_test.rb +46 -0
- data/lib/tap/test/script_tester.rb +109 -0
- data/lib/tap/test/{subset_methods.rb → subset_test.rb} +9 -9
- data/lib/tap/test/{subset_methods_class.rb → subset_test_class.rb} +22 -14
- data/lib/tap/test/{tap_methods.rb → tap_test.rb} +43 -6
- data/lib/tap/test/utils.rb +6 -3
- metadata +27 -24
- data/lib/tap/parser.rb +0 -619
- data/lib/tap/spec/file_methods.rb +0 -16
- data/lib/tap/spec/file_methods_class.rb +0 -13
- data/lib/tap/spec/subset_methods.rb +0 -14
- data/lib/tap/support/batchable.rb +0 -47
- data/lib/tap/support/batchable_class.rb +0 -107
- data/lib/tap/support/declarations.rb +0 -131
- data/lib/tap/support/lazydoc/declaration.rb +0 -20
- data/lib/tap/support/parsers/base.rb +0 -81
- data/lib/tap/support/parsers/server.rb +0 -113
- data/lib/tap/test/script_methods.rb +0 -75
- data/lib/tap/test/script_methods/regexp_escape.rb +0 -94
- data/lib/tap/test/script_methods/script_test.rb +0 -109
- 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
|
data/lib/tap/support/tdoc.rb
CHANGED
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 =
|
361
|
-
|
362
|
-
|
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
|
-
@
|
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
|
-
|
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)
|