tap 0.18.0 → 0.19.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History +21 -0
- data/MIT-LICENSE +17 -15
- data/README +13 -30
- data/bin/tap +19 -24
- data/cmd/console.rb +1 -12
- data/cmd/manifest.rb +14 -19
- data/cmd/run.rb +96 -86
- data/doc/API +194 -54
- data/doc/Examples/Command Line +27 -1
- data/lib/tap.rb +2 -1
- data/lib/tap/app.rb +613 -166
- data/lib/tap/app/api.rb +115 -0
- data/lib/tap/app/queue.rb +36 -37
- data/lib/tap/app/state.rb +2 -1
- data/lib/tap/env.rb +454 -270
- data/lib/tap/env/constant.rb +83 -33
- data/lib/tap/env/context.rb +61 -0
- data/lib/tap/env/manifest.rb +140 -50
- data/lib/tap/env/minimap.rb +55 -39
- data/lib/tap/join.rb +71 -53
- data/lib/tap/joins/sync.rb +3 -1
- data/lib/tap/middleware.rb +4 -25
- data/lib/tap/middlewares/debugger.rb +75 -0
- data/lib/tap/parser.rb +268 -0
- data/lib/tap/prompt.rb +36 -0
- data/lib/tap/root.rb +3 -3
- data/lib/tap/signals.rb +26 -0
- data/lib/tap/signals/class_methods.rb +222 -0
- data/lib/tap/signals/help.rb +40 -0
- data/lib/tap/signals/module_methods.rb +20 -0
- data/lib/tap/signals/signal.rb +68 -0
- data/lib/tap/task.rb +28 -79
- data/lib/tap/tasks/dump.rb +6 -0
- data/lib/tap/tasks/load.rb +9 -37
- data/lib/tap/templater.rb +12 -1
- data/lib/tap/version.rb +1 -1
- metadata +22 -16
- data/doc/Class Reference +0 -330
- data/lib/tap/exe.rb +0 -130
- data/lib/tap/schema.rb +0 -374
- data/lib/tap/schema/parser.rb +0 -425
- data/lib/tap/schema/utils.rb +0 -56
data/lib/tap/env/minimap.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
module Tap
|
2
2
|
class Env
|
3
3
|
|
4
|
-
# Minimap adds minimization and search methods to an array of paths.
|
4
|
+
# Minimap adds minimization and search methods to an array of paths. The
|
5
|
+
# minimized paths are refered to as 'minipaths' and represent the shortest
|
6
|
+
# basename that uniquely identifies a path within a collection of paths.
|
7
|
+
# File extensions and versions are removed when possible.
|
5
8
|
#
|
6
9
|
# paths = %w{
|
7
10
|
# path/to/file-0.1.0.txt
|
@@ -10,20 +13,33 @@ module Tap
|
|
10
13
|
# }
|
11
14
|
# paths.extend Env::Minimap
|
12
15
|
#
|
16
|
+
# paths.minimap
|
17
|
+
# # => [
|
18
|
+
# # ['file-0.1.0', 'path/to/file-0.1.0.txt'],
|
19
|
+
# # ['file-0.2.0', 'path/to/file-0.2.0.txt'],
|
20
|
+
# # ['another_file','path/to/another_file.txt']]
|
21
|
+
#
|
22
|
+
# Minipaths can be used as keys to uniquely match a path. Longer, more
|
23
|
+
# complete paths may also be used (they add specificity, even though it
|
24
|
+
# is not needed). Likewise shorter paths may be used; the minimatch
|
25
|
+
# method returns the first matching path.
|
26
|
+
#
|
13
27
|
# paths.minimatch('file') # => 'path/to/file-0.1.0.txt'
|
14
28
|
# paths.minimatch('file-0.2.0') # => 'path/to/file-0.2.0.txt'
|
15
|
-
# paths.minimatch('another_file')
|
29
|
+
# paths.minimatch('to/another_file') # => 'path/to/another_file.txt'
|
30
|
+
#
|
31
|
+
# === Usage
|
16
32
|
#
|
17
|
-
#
|
18
|
-
# Non-string entries are
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
33
|
+
# Minimap may extend any object responding to each, or be included in
|
34
|
+
# classes that implement each. Non-string entries are converted to paths
|
35
|
+
# by calling entry.path, if entry responds to path, or entry.to_s if it
|
36
|
+
# does not. Override the entry_to_path method to change this default
|
37
|
+
# behavior.
|
22
38
|
#
|
23
39
|
# class ConstantMap < Array
|
24
40
|
# include Env::Minimap
|
25
41
|
#
|
26
|
-
# def
|
42
|
+
# def entry_to_path(const)
|
27
43
|
# const.underscore
|
28
44
|
# end
|
29
45
|
# end
|
@@ -34,7 +50,8 @@ module Tap
|
|
34
50
|
#
|
35
51
|
module Minimap
|
36
52
|
|
37
|
-
#
|
53
|
+
# Determines the minipaths for each entry in self and returns a mapping
|
54
|
+
# of the minipaths to their corresponding entry.
|
38
55
|
#
|
39
56
|
# paths = %w{
|
40
57
|
# path/to/file-0.1.0.txt
|
@@ -51,7 +68,7 @@ module Tap
|
|
51
68
|
def minimap
|
52
69
|
hash = {}
|
53
70
|
map = []
|
54
|
-
each {|entry| map << (hash[
|
71
|
+
each {|entry| map << (hash[entry_to_path(entry)] = [entry]) }
|
55
72
|
minimize(hash.keys) do |key, mini_key|
|
56
73
|
hash[key].unshift mini_key
|
57
74
|
end
|
@@ -59,8 +76,8 @@ module Tap
|
|
59
76
|
map
|
60
77
|
end
|
61
78
|
|
62
|
-
# Returns the first entry
|
63
|
-
#
|
79
|
+
# Returns the first entry that mini-matches the input, or nil if no such
|
80
|
+
# entry exists.
|
64
81
|
#
|
65
82
|
# paths = %w{
|
66
83
|
# path/to/file-0.1.0.txt
|
@@ -74,12 +91,12 @@ module Tap
|
|
74
91
|
def minimatch(key)
|
75
92
|
key = key.to_s
|
76
93
|
each do |entry|
|
77
|
-
return entry if minimal_match?(
|
94
|
+
return entry if minimal_match?(entry_to_path(entry), key)
|
78
95
|
end
|
79
96
|
nil
|
80
97
|
end
|
81
98
|
|
82
|
-
# Returns minimap as a hash of (
|
99
|
+
# Returns minimap as a hash of (minipath, value) pairs.
|
83
100
|
def minihash(reverse=false)
|
84
101
|
hash = {}
|
85
102
|
minimap.each do |key, value|
|
@@ -94,17 +111,17 @@ module Tap
|
|
94
111
|
|
95
112
|
protected
|
96
113
|
|
97
|
-
# A hook to convert entries to
|
98
|
-
# or entry.
|
99
|
-
def
|
100
|
-
entry.respond_to?(:
|
114
|
+
# A hook to convert entries to paths. Returns entry.to_s by default,
|
115
|
+
# or entry.path if the entry responds to path.
|
116
|
+
def entry_to_path(entry)
|
117
|
+
entry.respond_to?(:path) ? entry.path : entry.to_s
|
101
118
|
end
|
102
119
|
|
103
120
|
module_function
|
104
121
|
|
105
|
-
#
|
106
|
-
#
|
107
|
-
# the basepath if possible. For example:
|
122
|
+
# Determines the shortest basepaths that unqiuely identifies a path
|
123
|
+
# within a collection of paths. The path extension and versions are
|
124
|
+
# removed from the basepath if possible. For example:
|
108
125
|
#
|
109
126
|
# Minimap.minimize ['path/to/a.rb', 'path/to/b.rb']
|
110
127
|
# # => ['a', 'b']
|
@@ -129,13 +146,13 @@ module Tap
|
|
129
146
|
# Minimap.minimize ['path/to/a-0.1.0.rb', 'path/to/a-0.2.0.rb']
|
130
147
|
# # => ['a-0.1.0', 'a-0.2.0']
|
131
148
|
#
|
132
|
-
# If a block is given, each (path,
|
149
|
+
# If a block is given, each (path, minipath) pair will be passed
|
133
150
|
# to it after minimization.
|
134
|
-
def minimize(paths) # :yields: path,
|
151
|
+
def minimize(paths) # :yields: path, minipath
|
135
152
|
unless block_given?
|
136
|
-
|
137
|
-
minimize(paths) {|
|
138
|
-
return
|
153
|
+
minipaths = []
|
154
|
+
minimize(paths) {|path, minipath| minipaths << minipath }
|
155
|
+
return minipaths
|
139
156
|
end
|
140
157
|
|
141
158
|
splits = paths.uniq.collect do |path|
|
@@ -191,13 +208,13 @@ module Tap
|
|
191
208
|
end
|
192
209
|
end
|
193
210
|
|
194
|
-
# Returns true if the
|
211
|
+
# Returns true if the minipath matches path. Matching logic reverses
|
195
212
|
# that of minimize:
|
196
213
|
#
|
197
|
-
# * a match occurs when path ends with
|
198
|
-
# * if
|
214
|
+
# * a match occurs when path ends with minipath
|
215
|
+
# * if minipath doesn't specify an extension, then minipath
|
199
216
|
# must only match path up to the path extension
|
200
|
-
# * if
|
217
|
+
# * if minipath doesn't specify a version, then minipath
|
201
218
|
# must only match path up to the path basename (minus the
|
202
219
|
# version and extname)
|
203
220
|
#
|
@@ -219,11 +236,11 @@ module Tap
|
|
219
236
|
# Minimap.minimal_match?('dir/file-0.1.0.txt', 'ile') # => false
|
220
237
|
# Minimap.minimal_match?('dir/file-0.1.0.txt', 'r/file') # => true
|
221
238
|
#
|
222
|
-
def minimal_match?(path,
|
223
|
-
extname = non_version_extname(
|
224
|
-
version =
|
239
|
+
def minimal_match?(path, minipath)
|
240
|
+
extname = non_version_extname(minipath)
|
241
|
+
version = minipath =~ /(-\d+(\.\d+)*)#{extname}$/ ? $1 : ''
|
225
242
|
|
226
|
-
|
243
|
+
path = case
|
227
244
|
when !extname.empty?
|
228
245
|
# force full match
|
229
246
|
path
|
@@ -231,14 +248,13 @@ module Tap
|
|
231
248
|
# match up to version
|
232
249
|
path.chomp(non_version_extname(path))
|
233
250
|
else
|
234
|
-
# match up base
|
251
|
+
# match up to base
|
235
252
|
path.chomp(non_version_extname(path)).sub(/(-\d+(\.\d+)*)$/, '')
|
236
253
|
end
|
237
254
|
|
238
|
-
#
|
239
|
-
#
|
240
|
-
|
241
|
-
match_path[-mini_path.length, mini_path.length] == mini_path && File.basename(match_path) == File.basename(mini_path)
|
255
|
+
# match if path ends with minipath. note the basename check ensures a full
|
256
|
+
# path segment has been specified. (ex: 'ile' should not match 'file.txt')
|
257
|
+
path[-minipath.length, minipath.length] == minipath && File.basename(path) == File.basename(minipath)
|
242
258
|
end
|
243
259
|
|
244
260
|
# utility method for minimize -- joins the
|
data/lib/tap/join.rb
CHANGED
@@ -15,39 +15,8 @@ module Tap
|
|
15
15
|
# results to m outputs. Flags can augment how the results are passed, in
|
16
16
|
# particular for array results.
|
17
17
|
#
|
18
|
-
class Join
|
18
|
+
class Join < App::Api
|
19
19
|
class << self
|
20
|
-
def inherited(child) # :nodoc:
|
21
|
-
unless child.instance_variable_defined?(:@source_file)
|
22
|
-
caller[0] =~ Lazydoc::CALLER_REGEXP
|
23
|
-
child.instance_variable_set(:@source_file, File.expand_path($1))
|
24
|
-
end
|
25
|
-
super
|
26
|
-
end
|
27
|
-
|
28
|
-
# Parses the argv into an instance of self.
|
29
|
-
def parse(argv=ARGV, app=Tap::App.instance)
|
30
|
-
parse!(argv.dup, app)
|
31
|
-
end
|
32
|
-
|
33
|
-
# Same as parse, but removes arguments destructively.
|
34
|
-
def parse!(argv=ARGV, app=Tap::App.instance)
|
35
|
-
opts = ConfigParser.new
|
36
|
-
opts.separator "configurations:"
|
37
|
-
opts.add(configurations)
|
38
|
-
|
39
|
-
args = opts.parse!(argv, :add_defaults => false)
|
40
|
-
|
41
|
-
instantiate({
|
42
|
-
:config => opts.nested_config,
|
43
|
-
:args => args
|
44
|
-
}, app)
|
45
|
-
end
|
46
|
-
|
47
|
-
# Instantiates an instance of self.
|
48
|
-
def instantiate(argh, app=Tap::App.instance)
|
49
|
-
new(argh[:config] || {}, app)
|
50
|
-
end
|
51
20
|
|
52
21
|
# Instantiates a new join with the input arguments and overrides
|
53
22
|
# call with the block. The block will be called with the join
|
@@ -63,23 +32,58 @@ module Tap
|
|
63
32
|
instance
|
64
33
|
end
|
65
34
|
|
35
|
+
def parse!(argv=ARGV, app=Tap::App.instance)
|
36
|
+
parser = self.parser
|
37
|
+
argv = parser.parse!(argv, :add_defaults => false)
|
38
|
+
instance = build({
|
39
|
+
'config' => parser.nested_config,
|
40
|
+
'inputs' => argv.shift,
|
41
|
+
'outputs' => argv.shift
|
42
|
+
}, app)
|
43
|
+
|
44
|
+
[instance, argv]
|
45
|
+
end
|
46
|
+
|
47
|
+
def build(spec={}, app=Tap::App.instance)
|
48
|
+
inputs = resolve(spec['inputs']) do |var|
|
49
|
+
app.get(var) or raise "missing join input: #{var}"
|
50
|
+
end
|
51
|
+
|
52
|
+
outputs = resolve(spec['outputs']) do |var|
|
53
|
+
app.get(var) or raise "missing join output: #{var}"
|
54
|
+
end
|
55
|
+
|
56
|
+
new(spec['config'] || {}, app).join(inputs, outputs)
|
57
|
+
end
|
58
|
+
|
66
59
|
protected
|
67
60
|
|
68
|
-
def
|
69
|
-
case
|
61
|
+
def resolve(refs) # :nodoc:
|
62
|
+
refs = case refs
|
63
|
+
when String then parse_indicies(refs)
|
70
64
|
when nil then []
|
71
|
-
|
72
|
-
|
73
|
-
|
65
|
+
else refs
|
66
|
+
end
|
67
|
+
|
68
|
+
refs.collect! do |var|
|
69
|
+
if var.kind_of?(String)
|
70
|
+
yield(var)
|
71
|
+
else
|
72
|
+
var
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# parses an str along commas, and collects the indicies as integers
|
78
|
+
def parse_indicies(str) # :nodoc:
|
79
|
+
str.split(",").delete_if do |n|
|
80
|
+
n.empty?
|
74
81
|
end
|
75
82
|
end
|
76
83
|
end
|
77
|
-
include Configurable
|
78
84
|
|
79
|
-
|
80
|
-
|
81
|
-
# Causes the targets to be enqued rather than executed immediately.
|
82
|
-
config :enq, false, :short => 'q', &c.flag
|
85
|
+
# Causes the outputs to be enqued rather than executed immediately.
|
86
|
+
config :enq, false, :short => 'q', &c.flag # Enque output nodes
|
83
87
|
|
84
88
|
# Splats the result to the outputs, allowing a many-to-one join
|
85
89
|
# from the perspective of the results.
|
@@ -88,7 +92,7 @@ module Tap
|
|
88
92
|
# # outputs: call(*inputs)
|
89
93
|
# app.execute(output, *result)
|
90
94
|
#
|
91
|
-
config :splat, false, :short => 's', &c.flag
|
95
|
+
config :splat, false, :short => 's', &c.flag # Splat results to outputs
|
92
96
|
|
93
97
|
# Iterates the results to the outputs, allowing a many-to-one join
|
94
98
|
# from the perspective of the results. Non-array results are converted
|
@@ -104,10 +108,21 @@ module Tap
|
|
104
108
|
# # outputs: call(*inputs)
|
105
109
|
# result.to_ary.each {|r| app.execute(output, *r) }
|
106
110
|
#
|
107
|
-
config :iterate, false, :short => 'i', &c.flag
|
111
|
+
config :iterate, false, :short => 'i', &c.flag # Iterate results to outputs
|
108
112
|
|
109
|
-
|
110
|
-
|
113
|
+
signal :join do |sig, (inputs, outputs)|
|
114
|
+
app = sig.obj.app
|
115
|
+
|
116
|
+
inputs = resolve(inputs) do |var|
|
117
|
+
app.get(var) or raise "missing join input: #{var}"
|
118
|
+
end
|
119
|
+
|
120
|
+
outputs = resolve(outputs) do |var|
|
121
|
+
app.get(var) or raise "missing join output: #{var}"
|
122
|
+
end
|
123
|
+
|
124
|
+
[inputs, outputs]
|
125
|
+
end
|
111
126
|
|
112
127
|
# An array of input nodes, or nil if the join has not been set.
|
113
128
|
attr_reader :inputs
|
@@ -117,10 +132,9 @@ module Tap
|
|
117
132
|
|
118
133
|
# Initializes a new join with the specified configuration.
|
119
134
|
def initialize(config={}, app=Tap::App.instance)
|
120
|
-
@app = app
|
121
135
|
@inputs = nil
|
122
136
|
@outputs = nil
|
123
|
-
|
137
|
+
super
|
124
138
|
end
|
125
139
|
|
126
140
|
# Sets self as a join between the inputs and outputs.
|
@@ -147,11 +161,15 @@ module Tap
|
|
147
161
|
end
|
148
162
|
end
|
149
163
|
|
150
|
-
def
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
164
|
+
def associations
|
165
|
+
[inputs + outputs]
|
166
|
+
end
|
167
|
+
|
168
|
+
def to_spec
|
169
|
+
spec = super
|
170
|
+
spec['inputs'] = inputs.collect {|node| app.var(node) }
|
171
|
+
spec['outputs'] = outputs.collect {|node| app.var(node) }
|
172
|
+
spec
|
155
173
|
end
|
156
174
|
|
157
175
|
protected
|
data/lib/tap/joins/sync.rb
CHANGED
data/lib/tap/middleware.rb
CHANGED
@@ -1,34 +1,13 @@
|
|
1
|
-
require '
|
1
|
+
require 'tap/app'
|
2
2
|
|
3
3
|
module Tap
|
4
|
-
class Middleware
|
4
|
+
class Middleware < App::Api
|
5
5
|
class << self
|
6
|
-
|
7
|
-
|
8
|
-
# as middleware.
|
9
|
-
def parse(argv=ARGV, app=Tap::App.instance)
|
10
|
-
parse!(argv.dup, app)
|
11
|
-
end
|
12
|
-
|
13
|
-
# Same as parse, but removes arguments destructively.
|
14
|
-
def parse!(argv=ARGV, app=Tap::App.instance)
|
15
|
-
opts = ConfigParser.new
|
16
|
-
opts.separator "configurations:"
|
17
|
-
opts.add(configurations)
|
18
|
-
|
19
|
-
args = opts.parse!(argv, :add_defaults => false)
|
20
|
-
instantiate({:config => opts.nested_config}, app)
|
21
|
-
end
|
22
|
-
|
23
|
-
# Instantiates an instance of self and causes app to use the instance
|
24
|
-
# as middleware.
|
25
|
-
def instantiate(argh, app=Tap::App.instance)
|
26
|
-
app.use(self, argh[:config] || {})
|
6
|
+
def build(spec={}, app=Tap::App.instance)
|
7
|
+
new(app.stack, spec['config'] || {})
|
27
8
|
end
|
28
9
|
end
|
29
10
|
|
30
|
-
include Configurable
|
31
|
-
|
32
11
|
# The call stack.
|
33
12
|
attr_reader :stack
|
34
13
|
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'tap/middleware'
|
2
|
+
|
3
|
+
module Tap
|
4
|
+
module Middlewares
|
5
|
+
|
6
|
+
# :startdoc::middleware the default debugger
|
7
|
+
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
|
20
|
+
|
21
|
+
def call(node, inputs=[])
|
22
|
+
open_io(output) do |io|
|
23
|
+
io.puts "- - #{node.class}"
|
24
|
+
io.puts " - #{summarize(inputs)}"
|
25
|
+
end
|
26
|
+
|
27
|
+
check_signature(node, inputs)
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
def summarize(inputs)
|
32
|
+
unless verbose
|
33
|
+
inputs = inputs.collect do |input|
|
34
|
+
input.class
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
inputs.inspect
|
39
|
+
end
|
40
|
+
|
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
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|