tap 0.19.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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