tap 0.19.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. data/History +100 -45
  2. data/MIT-LICENSE +1 -1
  3. data/README +95 -51
  4. data/bin/tap +11 -57
  5. data/bin/tapexe +84 -0
  6. data/doc/API +91 -139
  7. data/doc/Configuration +93 -0
  8. data/doc/Examples/Command Line +10 -42
  9. data/doc/Examples/Tapfile +124 -0
  10. data/doc/Ruby to Ruby +87 -0
  11. data/doc/Workflow Syntax +185 -0
  12. data/lib/tap.rb +74 -5
  13. data/lib/tap/app.rb +217 -310
  14. data/lib/tap/app/api.rb +44 -23
  15. data/lib/tap/app/queue.rb +11 -12
  16. data/lib/tap/app/stack.rb +4 -4
  17. data/lib/tap/declarations.rb +200 -0
  18. data/lib/tap/declarations/context.rb +31 -0
  19. data/lib/tap/declarations/description.rb +33 -0
  20. data/lib/tap/env.rb +133 -779
  21. data/lib/tap/env/cache.rb +87 -0
  22. data/lib/tap/env/constant.rb +94 -39
  23. data/lib/tap/env/path.rb +71 -0
  24. data/lib/tap/join.rb +42 -78
  25. data/lib/tap/joins/gate.rb +85 -0
  26. data/lib/tap/joins/switch.rb +4 -2
  27. data/lib/tap/joins/sync.rb +3 -3
  28. data/lib/tap/middleware.rb +5 -5
  29. data/lib/tap/middlewares/debugger.rb +18 -58
  30. data/lib/tap/parser.rb +115 -183
  31. data/lib/tap/root.rb +162 -239
  32. data/lib/tap/signal.rb +72 -0
  33. data/lib/tap/signals.rb +20 -2
  34. data/lib/tap/signals/class_methods.rb +38 -43
  35. data/lib/tap/signals/configure.rb +19 -0
  36. data/lib/tap/signals/help.rb +5 -7
  37. data/lib/tap/signals/load.rb +49 -0
  38. data/lib/tap/signals/module_methods.rb +1 -0
  39. data/lib/tap/task.rb +46 -275
  40. data/lib/tap/tasks/dump.rb +21 -16
  41. data/lib/tap/tasks/list.rb +184 -0
  42. data/lib/tap/tasks/load.rb +4 -4
  43. data/lib/tap/tasks/prompt.rb +128 -0
  44. data/lib/tap/tasks/signal.rb +42 -0
  45. data/lib/tap/tasks/singleton.rb +35 -0
  46. data/lib/tap/tasks/stream.rb +64 -0
  47. data/lib/tap/utils.rb +83 -0
  48. data/lib/tap/version.rb +2 -2
  49. data/lib/tap/workflow.rb +124 -0
  50. data/tap.yml +0 -0
  51. metadata +59 -24
  52. data/cmd/console.rb +0 -43
  53. data/cmd/manifest.rb +0 -118
  54. data/cmd/run.rb +0 -145
  55. data/doc/Examples/Workflow +0 -40
  56. data/lib/tap/app/node.rb +0 -29
  57. data/lib/tap/env/context.rb +0 -61
  58. data/lib/tap/env/gems.rb +0 -63
  59. data/lib/tap/env/manifest.rb +0 -179
  60. data/lib/tap/env/minimap.rb +0 -308
  61. data/lib/tap/intern.rb +0 -50
  62. data/lib/tap/joins.rb +0 -9
  63. data/lib/tap/prompt.rb +0 -36
  64. data/lib/tap/root/utils.rb +0 -220
  65. data/lib/tap/root/versions.rb +0 -138
  66. data/lib/tap/signals/signal.rb +0 -68
@@ -0,0 +1,85 @@
1
+ module Tap
2
+ module Joins
3
+
4
+ # :startdoc::join collects results
5
+ #
6
+ # Similar to a synchronized merge, but collects all results regardless of
7
+ # where they come from. Gates enque themselves when called as a join, and
8
+ # won't let results pass until they get run as a task.
9
+ #
10
+ # % tap load a -- load b - inspect - gate 0,1 2
11
+ # ["a", "b"]
12
+ #
13
+ # Gates are useful in conjunction with iteration where a single task may
14
+ # feed multiple results to a single join; in this case a sync merge doesn't
15
+ # produce the desired behavior of collecting the results.
16
+ #
17
+ # % tap load/yaml "[1, 2, 3]" -:i inspect -:.gate inspect
18
+ # 1
19
+ # 2
20
+ # 3
21
+ # [1, 2, 3]
22
+ #
23
+ # % tap load/yaml "[1, 2, 3]" -:i inspect -:.sync inspect
24
+ # 1
25
+ # [1]
26
+ # 2
27
+ # [2]
28
+ # 3
29
+ # [3]
30
+ #
31
+ # When a limit is specified, the gate will collect results up to the limit
32
+ # and then pass the results. Any leftover results are still passed at the
33
+ # end.
34
+ #
35
+ # % tap load/yaml "[1, 2, 3]" -:i inspect - inspect - gate 1 2 --limit 2
36
+ # 1
37
+ # 2
38
+ # [1, 2]
39
+ # 3
40
+ # [3]
41
+ #
42
+ class Gate < Join
43
+
44
+ # An array of results collected thusfar.
45
+ attr_reader :results
46
+
47
+ config :limit, nil, :short => :l, &c.integer_or_nil # Pass results after limit
48
+
49
+ def initialize(config={}, app=Tap::App.current)
50
+ super
51
+ @results = nil
52
+ end
53
+
54
+ def call(result)
55
+ if @results
56
+ # Results are set, so self is already enqued and collecting
57
+ # results. If the input is the collection, then it's time
58
+ # to execute the results and reset. Otherwise, just
59
+ # collect the input and wait.
60
+
61
+ if result == @results
62
+ @results = nil
63
+ super(result)
64
+ else
65
+ @results << result
66
+
67
+ if limit && @results.length >= limit
68
+ super(@results.dup)
69
+ @results.clear
70
+ end
71
+ end
72
+
73
+ else
74
+ # No results are set, so this is a first call and self is
75
+ # not enqued. Setup the collection.
76
+
77
+ @results = [result]
78
+ app.enq(self, @results)
79
+ end
80
+
81
+ self
82
+ end
83
+ end
84
+ end
85
+ end
@@ -1,3 +1,5 @@
1
+ require 'tap/join'
2
+
1
3
  module Tap
2
4
  module Joins
3
5
 
@@ -16,7 +18,7 @@ module Tap
16
18
  # to that receives the result.
17
19
  attr_accessor :selector
18
20
 
19
- def initialize(config={}, app=Tap::App.instance, &block)
21
+ def initialize(config={}, app=Tap::App.current, &block)
20
22
  super(config, app)
21
23
  @selector = block
22
24
  end
@@ -33,7 +35,7 @@ module Tap
33
35
  raise SwitchError, "no switch target at index: #{index}"
34
36
  end
35
37
 
36
- dispatch(output, result)
38
+ exe(output, result)
37
39
  end
38
40
 
39
41
  # Raised by a Switch join to indicate when a switch index is out of bounds.
@@ -1,7 +1,7 @@
1
1
  module Tap
2
2
  module Joins
3
3
 
4
- # :startdoc::join a synchronized multi-way join
4
+ # :startdoc::join synchronized multi-way join
5
5
  #
6
6
  # Sync works the same as Join, but passes the collected results of the
7
7
  # inputs (ie an array) to the outputs. The results will not be passed
@@ -17,7 +17,7 @@ module Tap
17
17
  # An array holding results until the batch is ready to execute.
18
18
  attr_reader :results
19
19
 
20
- def initialize(config={}, app=Tap::App.instance)
20
+ def initialize(config={}, app=Tap::App.current)
21
21
  super
22
22
  @results = nil
23
23
  end
@@ -65,7 +65,7 @@ module Tap
65
65
  results[index] = result
66
66
 
67
67
  unless results.include?(NIL_VALUE)
68
- outputs.each {|output| dispatch(output, results) }
68
+ outputs.each {|output| exe(output, results) }
69
69
  reset
70
70
  end
71
71
  end
@@ -1,9 +1,9 @@
1
- require 'tap/app'
1
+ require 'tap/app/api'
2
2
 
3
3
  module Tap
4
4
  class Middleware < App::Api
5
5
  class << self
6
- def build(spec={}, app=Tap::App.instance)
6
+ def build(spec={}, app=Tap::App.current)
7
7
  new(app.stack, spec['config'] || {})
8
8
  end
9
9
  end
@@ -27,9 +27,9 @@ module Tap
27
27
  end
28
28
  end
29
29
 
30
- # By default call simply calls stack with the node and inputs.
31
- def call(node, inputs=[])
32
- stack.call(node, inputs)
30
+ # By default call simply calls stack with the task and inputs.
31
+ def call(task, input)
32
+ stack.call(task, input)
33
33
  end
34
34
  end
35
35
  end
@@ -3,72 +3,32 @@ require 'tap/middleware'
3
3
  module Tap
4
4
  module Middlewares
5
5
 
6
- # :startdoc::middleware the default debugger
6
+ # :startdoc::middleware default debugger
7
+ #
8
+ # Logs the execution of tasks with their inputs. Debugger outputs the
9
+ # same information as App.exe will output when the app is set to debug. To
10
+ # avoid duplication, debugger ONLY logs execution when the app is not in
11
+ # debug mode, or when force is set.
12
+ #
7
13
  class Debugger < Middleware
8
- module Utils
9
- module_function
10
-
11
- def arity_ok?(arity, n)
12
- n == arity || (arity < 0 && (-1-n) <= arity)
13
- end
14
- end
15
-
16
- include Utils
17
-
18
- config :verbose, false, &c.flag
19
- config :output, $stderr, &c.io
14
+ config :force, false, &c.flag # Force logging
20
15
 
21
- def call(node, inputs=[])
22
- open_io(output) do |io|
23
- io.puts "- - #{node.class}"
24
- io.puts " - #{summarize(inputs)}"
25
- end
16
+ def call(task, input)
17
+ log("#{app.var(task)} <<", "#{summarize input} (#{task.class})")
18
+ output = super
26
19
 
27
- check_signature(node, inputs)
28
- super
20
+ log("#{app.var(task)} >>", "#{summarize output} (#{task.class})")
21
+ output
29
22
  end
30
23
 
31
- def summarize(inputs)
32
- unless verbose
33
- inputs = inputs.collect do |input|
34
- input.class
35
- end
24
+ def log(action, msg)
25
+ if force || !app.debug
26
+ app.log(action, msg)
36
27
  end
37
-
38
- inputs.inspect
39
28
  end
40
29
 
41
- def check_signature(node, inputs)
42
- n = inputs.length
43
-
44
- call_arity = node.method(:call).arity
45
- unless arity_ok?(call_arity, n)
46
- raise InvalidSignatureError.new(node, inputs, :call, call_arity)
47
- end
48
-
49
- if node.kind_of?(Task)
50
- process_arity = node.method(:process).arity
51
- unless arity_ok?(process_arity, n)
52
- raise InvalidSignatureError.new(node, inputs, :process, process_arity)
53
- end
54
- end
55
-
56
- if node.kind_of?(Intern)
57
- process_block_arity = node.process_block
58
- unless arity_ok?(process_block_arity, n)
59
- raise InvalidSignatureError.new(node, inputs, :process_block, process_block_arity)
60
- end
61
- end
62
-
63
- end
64
-
65
- class InvalidSignatureError < StandardError
66
- def initialize(node, inputs, method, arity)
67
- lines = []
68
- lines << "Invalid input signature to: #{node.class} (#{method})"
69
- lines << "Expected #{arity} input but was given #{inputs.length}"
70
- super(lines.join("\n"))
71
- end
30
+ def summarize(obj)
31
+ obj.inspect
72
32
  end
73
33
  end
74
34
  end
@@ -1,74 +1,35 @@
1
- require 'shellwords'
1
+ require 'tap/join'
2
+ require 'tap/tasks/signal'
2
3
 
3
4
  module Tap
4
5
 
5
6
  # A parser for workflows defined on the command line.
6
7
  class Parser
7
- class << self
8
- def parse(argv=ARGV)
9
- parse!(argv.dup)
10
- end
11
-
12
- def parse!(argv=ARGV)
13
- argv = Shellwords.shellwords(argv) if argv.kind_of?(String)
14
- sig, obj = argv.shift, nil
15
-
16
- if sig =~ OBJECT
17
- obj, sig = $1, $2
18
- end
19
-
20
- [obj, sig, argv]
21
- end
22
- end
23
8
 
24
- # The escape begin argument
25
- ESCAPE_BEGIN = "-."
26
-
27
- # The escape end argument
28
- ESCAPE_END = ".-"
29
-
30
- # The parser end flag
31
- END_FLAG = "---"
32
-
33
- # Matches any breaking arg. Examples:
34
- #
35
- # --
36
- # --:
37
- # --[1,2][3]
38
- # --@
39
- # --/var
40
- # --.
41
- #
42
- # After the match:
43
- #
44
- # $1:: The string after the break, or nil
45
- # (ex: '--' => nil, '--:' => ':', '--[1,2][3,4]' => '[1,2][3,4]')
46
- #
47
- BREAK = /\A--(?:\z|([\:\[\/\.@].*?)\z)/
48
-
49
- # The node modifier.
50
- NODE_BREAK = nil
51
-
52
- # The join modifier.
53
- JOIN_BREAK = "."
54
-
9
+ # Regexp to match any dash-nonword break
10
+ BREAK = /\A-(?!-?\w)/
11
+
12
+ # Matches a traditional dash-letter or double-dash-word option
13
+ OPTION = /\A--?\w/
14
+
15
+ # Delmits and sets the next object
16
+ SET = '-'
17
+
18
+ # Sets and enques the next object
19
+ ENQUE = '--'
20
+
21
+ # Sets and executes the next object
22
+ EXECUTE = '-!'
23
+
24
+ # Break to enque an existing object
25
+ SIGENQ = '-@'
26
+
55
27
  # Matches a sequence break. After the match:
56
28
  #
57
29
  # $1:: The modifier string, or nil
58
30
  # (ex: ':' => nil, ':i' => 'i')
59
31
  #
60
- SEQUENCE = /\A:(.+)?\z/
61
-
62
- # Matches a generic join break. After the match:
63
- #
64
- # $1:: The inputs string.
65
- # (ex: '[1,2,3][4,5,6]' => '1,2,3')
66
- # $2:: The outputs string.
67
- # (ex: '[1,2,3][4,5,6]' => '4,5,6')
68
- # $3:: The modifier string, or nil
69
- # (ex: '[][]is' => 'is')
70
- #
71
- JOIN = /\A\[(.*?)\]\[(.*?)\](.+)?\z/
32
+ JOIN = /\A-:(.+)?\z/
72
33
 
73
34
  # Matches a join modifier. After the match:
74
35
  #
@@ -77,14 +38,7 @@ module Tap
77
38
  # $2:: The class string.
78
39
  # (ex: 'is.sync' => 'sync')
79
40
  #
80
- JOIN_MODIFIER = /\A([A-z]*)(?:\.(.*))?\z/
81
-
82
- # Matches an enque modifier. After the match:
83
- #
84
- # $1:: The modifier string, or nil
85
- # (ex: '@var' => 'var')
86
- #
87
- ENQUE = /\A@(.+)?\z/
41
+ MODIFIER = /\A([A-z]*)(?:\.(.*))?\z/
88
42
 
89
43
  # Matches a signal break. After the match:
90
44
  #
@@ -93,49 +47,39 @@ module Tap
93
47
  # $2:: The signal string
94
48
  # (ex: 'obj/sig' => 'sig')
95
49
  #
96
- SIGNAL = /\A\/(?:(.*)\/)?(.*)\z/
50
+ SIGNAL = /\A-(-)?\/(.*)\z/
97
51
 
98
- # Splits a signal into an object string and a signal string. If OBJECT
99
- # doesn't match, then the string can be considered a signal, and the
100
- # object is nil. After a match:
101
- #
102
- # $1:: The object string
103
- # (ex: 'obj/sig' => 'obj')
104
- # $2:: The signal string
105
- # (ex: 'obj/sig' => 'sig')
106
- #
107
- OBJECT = /\A(.*)\/(.*)\z/
52
+ # The escape begin argument
53
+ ESCAPE_BEGIN = "-."
54
+
55
+ # The escape end argument
56
+ ESCAPE_END = ".-"
57
+
58
+ # The parser end flag
59
+ END_FLAG = "---"
108
60
 
109
61
  attr_reader :specs
110
62
 
111
- def initialize(specs=[])
112
- @specs = specs
63
+ def initialize
64
+ @specs = []
113
65
  end
114
66
 
115
67
  def parse(argv)
116
- argv = argv.dup unless argv.kind_of?(String)
117
- parse!(argv)
68
+ parse!(argv.dup)
118
69
  end
119
70
 
120
71
  # Same as parse, but removes parsed args from argv.
121
72
  def parse!(argv)
122
- argv = Shellwords.shellwords(argv) if argv.kind_of?(String)
123
73
  return argv if argv.empty?
124
74
 
125
- unless argv[0] =~ BREAK
126
- argv.unshift("--")
127
- end
128
-
129
- @current_type = nil
130
75
  @current_index = -1
131
- @current = nil
76
+ current = argv[0] =~ BREAK ? nil : spec(:enq)
132
77
  escape = false
133
78
 
134
79
  while !argv.empty?
135
80
  arg = argv.shift
136
81
 
137
- # if escaping, add escaped arguments
138
- # until an escape-end argument
82
+ # collect escaped arguments until an escape-end
139
83
  if escape
140
84
  if arg == ESCAPE_END
141
85
  escape = false
@@ -144,125 +88,113 @@ module Tap
144
88
  end
145
89
  next
146
90
  end
147
-
148
- # handle breaks and parser flags
149
- case arg
150
- when BREAK
151
- begin
152
- @current_type = nil
153
- @current_index += 1
154
- @current = parse_break($1)
155
- rescue
156
- raise "invalid break: #{arg} (#{$!.message})"
157
- end
158
- next
159
-
160
- when ESCAPE_BEGIN
161
- escape = true
91
+
92
+ # collect non-option/break arguments
93
+ unless arg[0] == ?-
94
+ current << arg
162
95
  next
163
-
164
- when END_FLAG
165
- break
96
+ end
166
97
 
167
- end if arg[0] == ?-
98
+ begin
99
+
100
+ # parse option/break arguments
101
+ case arg
102
+ when SET
103
+ current = spec(:set)
104
+ when ENQUE
105
+ current = spec(:enq)
106
+ when EXECUTE
107
+ current = spec(:exe)
108
+ when OPTION
109
+ current << arg
110
+ when JOIN
111
+ current = parse_join($1)
112
+ when SIGENQ
113
+ current = parse_signal(nil, 'enq')
114
+ when SIGNAL
115
+ current = parse_signal($1, $2)
116
+ when ESCAPE_BEGIN
117
+ escape = true
118
+ when END_FLAG
119
+ break
120
+ else
121
+ raise "unknown"
122
+ end
123
+
124
+ rescue
125
+ raise "invalid break: #{arg} (#{$!.message})"
126
+ end
127
+ end
168
128
 
169
- # add all remaining args to the current argv
170
- current << arg
129
+ argv
130
+ end
131
+
132
+ def build_to(app)
133
+ blocks = Hash.new do |hash, type|
134
+ hash[type] = block(type, app)
171
135
  end
172
136
 
173
- @current_type = nil
174
- @current_index = nil
175
- @current = nil
137
+ app.scope do
138
+ specs.each do |(spec, type)|
139
+ app.call(spec, &blocks[type])
140
+ end
141
+ end
176
142
 
177
- argv
143
+ self
178
144
  end
179
145
 
180
146
  private
181
147
 
182
- def spec(*argv) # :nodoc:
183
- specs << argv
184
- argv
148
+ def next_args # :nodoc:
149
+ @current_index += 1
150
+ [@current_index.to_s]
185
151
  end
186
152
 
187
- # returns the current argv or a new spec argv for the current type/index
188
- def current # :nodoc:
189
- @current ||= spec(@current_type, nil, 'set', @current_index.to_s)
153
+ def spec(type, args=next_args) # :nodoc:
154
+ specs << [{'sig' => 'set', 'args' => args}, type]
155
+ args
190
156
  end
191
-
192
- # determines the type of break and modifies self appropriately
193
- def parse_break(one) # :nodoc:
194
- case one
195
- when NODE_BREAK
196
- set_type(:node)
197
- when JOIN_BREAK
198
- set_type(:join)
199
- when SEQUENCE
200
- parse_sequence($1)
201
- when JOIN
202
- parse_join($1, $2, $3)
203
- when ENQUE
204
- parse_enque($1)
205
- when SIGNAL
206
- parse_signal($1, $2)
157
+
158
+ def block(type, app) # :nodoc:
159
+ case type
160
+ when :enq
161
+ lambda {|obj, args| app.queue.enq(obj, args); obj }
162
+ when :exe
163
+ lambda {|obj, args| app.exe(obj, args); obj }
207
164
  else
208
- raise "invalid modifier"
165
+ nil
209
166
  end
210
167
  end
211
-
212
- # sets the type of the next spec
213
- def set_type(type) # :nodoc:
214
- @current_type = type
215
- nil
216
- end
217
-
218
- # parses the match of a SEQUENCE regexp
219
- def parse_sequence(one) # :nodoc:
220
- unless @current_index > 0
168
+
169
+ # parses the match of a JOIN regexp
170
+ def parse_join(one) # :nodoc:
171
+ if @current_index < 0
221
172
  raise "no prior entry"
222
173
  end
223
174
 
224
- @current_type = :node
225
- @current = nil
226
- argv = current
227
- parse_join_spec(one, "#{@current_index - 1}", @current_index.to_s)
228
- argv
229
- end
230
-
231
- # parses the match of a JOIN regexp
232
- def parse_join(one, two, three) # :nodoc:
233
- parse_join_spec(three, one, two)
234
- end
235
-
236
- # parses a join modifier string into an argv.
237
- def parse_join_spec(modifier, inputs, outputs) # :nodoc:
238
- argv = [:join, nil, 'set', nil]
175
+ current = spec(:set)
176
+ join = spec(:set, [nil])
239
177
 
240
- case
241
- when modifier.nil?
242
- argv << 'tap:join'
243
- argv << inputs
244
- argv << outputs
245
- when modifier =~ JOIN_MODIFIER
246
- argv << ($2 || 'join')
247
- argv << inputs
248
- argv << outputs
249
- $1.split("").each {|char| argv << "-#{char}"}
178
+ case one
179
+ when nil
180
+ join << Tap::Join
181
+ when MODIFIER
182
+ join << ($2 || Tap::Join)
183
+ $1.split('').each {|flag| join << "-#{flag}"}
250
184
  else
251
185
  raise "invalid join modifier"
252
186
  end
253
187
 
254
- specs << argv
255
- argv
256
- end
257
-
258
- # parses the match of an ENQUE regexp
259
- def parse_enque(one) # :nodoc:
260
- spec(:signal, nil, 'enque', one)
188
+ join << (@current_index - 1).to_s
189
+ join << @current_index.to_s
190
+
191
+ current
261
192
  end
262
193
 
263
194
  # parses the match of a SIGNAL regexp
264
195
  def parse_signal(one, two) # :nodoc:
265
- spec(:signal, one, two)
196
+ next_args
197
+ spec(one.nil? ? :enq : :exe, [nil, Tap::Tasks::Signal, two, '--'])
266
198
  end
267
199
  end
268
200
  end