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
@@ -7,7 +7,7 @@ module Tap
7
7
  # and other gems frequently used by Tap.
8
8
  module Gems
9
9
  module_function
10
-
10
+
11
11
  # Returns the gemspec for the specified gem. A gem version
12
12
  # can be specified in the name, like 'gem >= 1.2'. The gem
13
13
  # will be activated using +gem+ if necessary.
@@ -5,8 +5,6 @@ module Tap
5
5
  # An Intern module:
6
6
  # - adds an accessor for <method_name>_block
7
7
  # - overrides <method_name> to call the block
8
- # - ensures initialize_batch_obj extends the batch object
9
- # with the same Intern module
10
8
  #
11
9
  def self.Intern(method_name)
12
10
  mod = INTERN_MODULES[method_name.to_sym]
@@ -28,10 +26,6 @@ module Tap
28
26
 
29
27
  #{method_name}_block.call(*inputs)
30
28
  end
31
-
32
- def initialize_batch_obj(*args)
33
- super(*args).extend Tap::Support::Intern(:#{method_name})
34
- end
35
29
  }
36
30
  mod
37
31
  end
@@ -1,3 +1,5 @@
1
+ require 'configurable'
2
+
1
3
  module Tap
2
4
  module Support
3
5
 
@@ -6,56 +8,42 @@ module Tap
6
8
  # variety of configurations which affect how one task passes inputs
7
9
  # to subsequent tasks.
8
10
  #
9
- # Joins have a single source and may have multiple targets. See
10
- # ReverseJoin for joins with a single target and multiple sources.
11
11
  class Join
12
- class << self
13
-
14
- # Create a join between the source and targets. Targets should
15
- # be an array; if the last member of targets is a hash, it will
16
- # be used as the configurations for the join.
17
- def join(source, targets, &block)
18
- options = targets[-1].kind_of?(Hash) ? targets.pop : {}
19
- new(options).join(source, targets, &block)
20
- end
21
- end
22
-
23
12
  include Configurable
24
13
 
25
14
  # Causes the join to iterate the results
26
15
  # of the source when enquing the targets.
27
- config :iterate, false, &c.boolean
16
+ config :iterate, false, :short => 'i', &c.boolean
17
+
18
+ # Causes joins to splat ('*') the results
19
+ # of the source when enquing the targets.
20
+ config :splat, false, :short => 's', &c.boolean
28
21
 
29
22
  # Causes the targets to be enqued rather
30
23
  # than executed immediately.
31
- config :stack, false, &c.boolean
32
-
33
- # Causes joins to only occur between the
34
- # explicitly named source and targets,
35
- # and not their batches.
36
- config :unbatched, false, &c.boolean
37
-
38
- # An array of workflow flags. Workflow flags are false unless specified.
39
- FLAGS = configurations.keys
40
-
41
- # An array of the first character in each WORKFLOW_FLAGS.
42
- SHORT_FLAGS = FLAGS.collect {|flag| flag.to_s[0,1]}
24
+ config :stack, false, :short => 'k', &c.boolean
43
25
 
44
26
  # Initializes a new join with the specified configuration.
45
- def initialize(config)
27
+ def initialize(config={})
46
28
  initialize_config(config)
47
29
  end
48
30
 
49
- # The name of the join, as a symbol. By default
50
- # name is the basename of the underscored class.
31
+ # The name of the join, as a symbol. By default name is the basename of
32
+ # the underscored class name.
51
33
  def name
52
34
  File.basename(self.class.to_s.underscore).to_sym
53
35
  end
54
36
 
55
- # Creates a join between the source and targets.
56
- # Must be implemented in subclasses.
57
- def join(source, targets, &block)
58
- raise NotImplementedError
37
+ # Creates a join that passes the results of each input to each output.
38
+ def join(inputs, outputs)
39
+ inputs.each do |input|
40
+ input.on_complete do |_result|
41
+ outputs.each do |output|
42
+ yield(_result) if block_given?
43
+ enq(output, _result)
44
+ end
45
+ end
46
+ end
59
47
  end
60
48
 
61
49
  # A hash of the configurations set to true.
@@ -72,71 +60,49 @@ module Tap
72
60
 
73
61
  protected
74
62
 
75
- # Sets the on_complete block for the specified executable.
76
- # If unbatched == true, the on_complete block will only
77
- # be set for the executable; otherwise the on_complete
78
- # block will be set for executable.batch.
79
- def complete(executable, &block)
80
- executable.send(unbatched ? :unbatched_on_complete : :on_complete, &block)
81
- end
82
-
83
63
  # Enques the executable with the results, respecting the
84
64
  # configuration for self.
85
65
  #
86
66
  # true false
87
67
  # iterate _results are iterated _results are enqued directly
68
+ # splat _results are splat enqued _results are enqued directly
88
69
  # stack the executable is enqued the executable is executed
89
- # unbatched only exectuable is enqued executable.batch is enqued
90
70
  #
91
- def enq(executable, _results)
92
- app = executable.app
93
-
94
- results = iterate ? _results._iterate : [_results]
95
- results.each do |_result|
71
+ def enq(executable, *_results)
72
+ unpack(_results) do |_result|
96
73
  if stack
97
-
98
- if unbatched
99
- executable.unbatched_enq(_result)
100
- else
101
- executable.enq(_result)
102
- end
103
-
74
+ executable.enq(*_result)
104
75
  else
105
-
106
- if unbatched
107
- executable._execute(_result)
108
- else
109
- executable.batch.each do |e|
110
- e._execute(_result)
111
- end
112
- end
113
-
76
+ executable._execute(*_result)
114
77
  end
115
78
  end
116
79
  end
117
- end
118
-
119
- # Like a Join, but with a single target and multiple sources.
120
- class ReverseJoin < Join
121
- class << self
122
- # Create a join between the sources and target. Sources should
123
- # be an array; if the last member of sources is a hash, it will
124
- # be used as the configurations for the join.
125
- def join(target, sources, &block)
126
- options = sources[-1].kind_of?(Hash) ? sources.pop : {}
127
- new(options).join(target, sources, &block)
128
- end
129
- end
130
80
 
131
- # Creates a join between the sources and target.
132
- # Must be implemented in subclasses.
133
- def join(target, sources, &block)
134
- raise NotImplementedError
81
+ # helper method to splat/iterate audited results
82
+ def unpack(_results) # :nodoc:
83
+ case
84
+ when iterate && splat
85
+ raise "splat and iterate"
86
+ when iterate
87
+ _splat(_results).each {|_result| yield(_result) }
88
+ when splat
89
+ yield(_splat(_results))
90
+ else
91
+ yield(_results)
92
+ end
135
93
  end
136
94
 
137
- # Returns a string like: "#<ReverseJoin:object_id>"
138
- def inspect
139
- "#<ReverseJoin:#{object_id}>"
95
+ # helper to splat audits
96
+ def _splat(_results) # :nodoc:
97
+ array = []
98
+ _results.each do |_result|
99
+ unless _result.kind_of?(Audit)
100
+ _result = Audit.new(nil, _result)
101
+ end
102
+
103
+ array.concat(_result.splat)
104
+ end
105
+ array
140
106
  end
141
107
  end
142
108
  end
@@ -5,9 +5,6 @@ module Tap
5
5
 
6
6
  # A module of the standard Join classes supported by Tap.
7
7
  module Joins
8
- autoload(:Sequence, 'tap/support/joins/sequence')
9
- autoload(:Fork, 'tap/support/joins/fork')
10
- autoload(:Merge, 'tap/support/joins/merge')
11
8
  autoload(:SyncMerge, 'tap/support/joins/sync_merge')
12
9
  autoload(:Switch, 'tap/support/joins/switch')
13
10
  end
@@ -2,19 +2,21 @@ module Tap
2
2
  module Support
3
3
  module Joins
4
4
 
5
- # A Switch join allows a block to determine which target from
6
- # set of targets will receive the results of the source.
5
+ # A Switch join allows a block to determine which output from an array
6
+ # of outputs will receive the results of the input.
7
7
  class Switch < Join
8
- def join(source, targets)
9
- complete(source) do |_result|
10
- if index = yield(_result)
11
- unless target = targets[index]
12
- raise "no switch target for index: #{index}"
13
- end
8
+ def join(inputs, outputs)
9
+ inputs.each do |input|
10
+ input.on_complete do |_result|
11
+ if index = yield(_result)
12
+ unless output = outputs[index]
13
+ raise "no switch target for index: #{index}"
14
+ end
14
15
 
15
- enq(target, _result)
16
- else
17
- source.app.aggregator.store(_result)
16
+ enq(output, _result)
17
+ else
18
+ input.app.aggregator.store(_result)
19
+ end
18
20
  end
19
21
  end
20
22
  end
@@ -1,63 +1,38 @@
1
1
  module Tap
2
2
  module Support
3
- autoload(:Combinator, 'tap/support/combinator')
4
-
5
3
  module Joins
6
4
 
7
- # SyncMerge passes the collected results of the sources to the target. The
8
- # results will not be passed until results from all of the sources are
5
+ # SyncMerge passes the collected results of the inputs to the outputs. The
6
+ # results will not be passed until results from all of the inputs are
9
7
  # available; results are passed in one group. Similarly, a collision
10
- # results if a single source completes twice before the group.
11
- class SyncMerge < ReverseJoin
12
- def join(target, sources)
13
-
14
- # a hash of (source, index) pairs where index is the
15
- # index of the source in a combination
16
- indicies = {}
17
-
18
- # a hash of (source, combinations) pairs where combinations
19
- # are combination arrays that the source participates in.
20
- # note that in unbatched mode, some sources may not
21
- # participate in any combinations.
22
- combinations = {}
23
-
24
- sets = sources.collect {|source| unbatched ? [source] : source.batch }
25
- Support::Combinator.new(*sets).each do |combo|
26
- combination = Array.new(combo.length, nil)
27
-
28
- combo.each do |source|
29
- indicies[source] ||= combo.index(source)
30
- (combinations[source] ||= []) << combination
31
- end
32
- end
33
-
34
- sources.each_with_index do |source, index|
35
- complete(source) do |_result|
36
- src = _result._current_source
37
-
38
- source_index = indicies[src]
39
- (combinations[src] ||= []).each do |combination|
40
- if combination[source_index] != nil
41
- raise "sync_merge collision... already got a result for #{src}"
42
- end
43
-
44
- combination[source_index] = _result
45
- unless combination.include?(nil)
46
- # merge the source audits
47
- _merge_result = Support::Audit.merge(*combination)
48
-
49
- yield(_merge_result) if block_given?
50
- enq(target, _merge_result)
51
-
52
- # reset the group array
53
- combination.collect! {|i| nil }
54
- end
8
+ # results if a single input completes twice before the group completes as
9
+ # a whole.
10
+ class SyncMerge < Join
11
+
12
+ def join(inputs, outputs)
13
+ results = Array.new(inputs.length)
14
+
15
+ inputs.each do |input|
16
+ input.on_complete do |_result|
17
+ index = inputs.index(_result.key)
18
+
19
+ unless results[index] == nil
20
+ raise "sync_merge collision... already got a result for #{inputs[index]}"
21
+ end
22
+ results[index] = _result
23
+
24
+ unless results.include?(nil)
25
+ yield(*results) if block_given?
26
+ outputs.each {|output| enq(output, *results) }
27
+
28
+ # reset the results array
29
+ results.collect! {|i| nil }
55
30
  end
56
31
  end
57
32
  end
58
33
  end
34
+
59
35
  end
60
-
61
36
  end
62
37
  end
63
38
  end
@@ -1,4 +1,5 @@
1
1
  require 'tap/support/minimap'
2
+ require 'tap/support/intern'
2
3
 
3
4
  module Tap
4
5
  module Support
@@ -3,41 +3,156 @@ module Tap
3
3
 
4
4
  # Represents a task node in a Schema.
5
5
  class Node
6
+ class << self
7
+
8
+ # Returns the natural round of a set of nodes. The natural round is
9
+ # the lowest round of any of the nodes, or the node ancestors.
10
+ #
11
+ # # (3)-o-[A]-o-[C]-o-[D]
12
+ # # |
13
+ # # (2)-o-[B]-o
14
+ #
15
+ # join1, join2 = Array.new(2) { [:join, [], []] }
16
+ # a = Node.new [], 3, join1
17
+ # b = Node.new [], 2, join1
18
+ # c = Node.new [], join1, join2
19
+ # d = Node.new [], join2
20
+ #
21
+ # Node.natural_round([d]) # => 2
22
+ #
23
+ # Tracking back, the natural round of D is 2. Node order does not
24
+ # matter and globals are ignored.
25
+ #
26
+ # # ( )-o-[E]-o
27
+ # # |
28
+ # # (1)-o-[F]-o
29
+ # # |
30
+ # # (2)-o-[G]-o-[H]
31
+ #
32
+ # join = [:join, [], []]
33
+ # e = Node.new [], nil, join
34
+ # f = Node.new [], 1, join
35
+ # g = Node.new [], 2, join
36
+ # h = Node.new [], join
37
+ #
38
+ # Node.natural_round([d, h]) # => 1
39
+ #
40
+ def natural_round(nodes, visited=[])
41
+ round = nil
42
+ nodes.each do |node|
43
+ next if visited.include?(node)
44
+ visited << node
45
+
46
+ case input = node.input
47
+ when Integer
48
+
49
+ # reassign current round if necesssary
50
+ unless round && round < input
51
+ round = input
52
+ end
53
+
54
+ when Array
55
+ round = natural_round(node.parents, visited)
56
+ end
57
+
58
+ # optimization; no round is less than 0
59
+ return 0 if round == 0
60
+ end
61
+
62
+ round || 0
63
+ end
64
+ end
6
65
 
7
- # An array of arguments used to instantiate
8
- # the node
66
+ # An array of arguments used to instantiate the node
9
67
  attr_accessor :argv
10
68
 
11
- # The input or source for the node. Inputs
12
- # may be a Join, nil, or an Integer. An
13
- # Integer input indicates that the node should
14
- # be enqued to a round using argv as inputs.
15
- attr_accessor :input
69
+ # The input for the node. Input may be:
70
+ #
71
+ # - a join array: [join_instance, input_nodes, output_nodes]
72
+ # - an Integer indicating the round for self
73
+ # - nil signifiying 'global'
74
+ #
75
+ attr_reader :input
16
76
 
17
- # The output for the node. Output may be a
18
- # a Join or nil.
19
- attr_accessor :output
77
+ # The output for the node. Output may be:
78
+ #
79
+ # - a join array: [join_instance, input_nodes, output_nodes]
80
+ # - nil signifying nothing
81
+ #
82
+ attr_reader :output
20
83
 
21
- def initialize(argv=[], input=nil, output=nil)
84
+ def initialize(argv=[], input=0, output=nil)
22
85
  @argv = argv
23
- @input = input
24
- @output = output
86
+ @input = @output = nil
87
+
88
+ self.input = input
89
+ self.output = output
90
+ end
91
+
92
+ def input_join
93
+ input.kind_of?(Array) ? input : nil
94
+ end
95
+
96
+ def output_join
97
+ output.kind_of?(Array) ? output : nil
98
+ end
99
+
100
+ # Returns an array of nodes that pass inputs to self via an input join.
101
+ # If input is not a join, parents is an empty array.
102
+ def parents
103
+ input.kind_of?(Array) ? input[1] : []
25
104
  end
26
105
 
27
- # Resets the source and join to nil.
106
+ # Returns an array of nodes that receive the outputs self via an output
107
+ # join. If output is not a join, children is an empty array.
108
+ def children
109
+ output.kind_of?(Array) ? output[2] : []
110
+ end
111
+
112
+ # Sets the input for self.
113
+ def input=(value)
114
+ if input.kind_of?(Array)
115
+ input[2].delete(self)
116
+ end
117
+
118
+ if value.kind_of?(Array)
119
+ value[2] << self
120
+ end
121
+
122
+ @input = value
123
+ end
124
+
125
+ # Sets the output for self.
126
+ def output=(value)
127
+ if output.kind_of?(Array)
128
+ output[1].delete(self)
129
+
130
+ # cleanup orphan joins
131
+ if output[1].empty?
132
+ orphan_round = natural_round
133
+ output[2].dup.each {|node| node.input = orphan_round }
134
+ end
135
+ end
136
+
137
+ if value.kind_of?(Array)
138
+ value[1] << self
139
+ end
140
+
141
+ @output = value
142
+ end
143
+
144
+ # Sets the input to nil.
28
145
  def globalize
29
146
  self.input = nil
30
- self.output = nil
31
147
  end
32
148
 
33
- # True if the input and output are nil.
149
+ # True if the input is nil.
34
150
  def global?
35
- input == nil && output == nil
151
+ input == nil
36
152
  end
37
153
 
38
- # Returns the round for self; a round is indicated
39
- # by an integer input. If input is anything but
40
- # an integer, round returns nil.
154
+ # Returns the round for self; a round is indicated by an integer input.
155
+ # If input is anything but an integer, round returns nil.
41
156
  def round
42
157
  input.kind_of?(Integer) ? input : nil
43
158
  end
@@ -47,6 +162,11 @@ module Tap
47
162
  self.input = input
48
163
  end
49
164
 
165
+ # Returns the natural round for self.
166
+ def natural_round
167
+ Node.natural_round([self])
168
+ end
169
+
50
170
  def inspect
51
171
  "#<#{self.class}:#{object_id} argv=[#{argv.join(' ')}] input=#{input.inspect} output=#{output.inspect}>"
52
172
  end