tap 0.18.0 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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') # => 'path/to/another_file.txt'
29
+ # paths.minimatch('to/another_file') # => 'path/to/another_file.txt'
30
+ #
31
+ # === Usage
16
32
  #
17
- # More generally, Minimap may extend any object responding to each.
18
- # Non-string entries are allowed; if the entry responds to minikey, then
19
- # minimap will call that method to determine the 'path' to the entry.
20
- # Otherwise, to_s is used. Override the entry_to_minikey method to
21
- # change this default behavior.
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 entry_to_minikey(const)
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
- # Provides a minimized map of the entries using keys provided minikey.
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[entry_to_minikey(entry)] = [entry]) }
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 whose minikey mini-matches the input, or nil if
63
- # no such entry exists.
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?(entry_to_minikey(entry), key)
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 (minikey, value) pairs.
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 minikeys. Returns the entry by default,
98
- # or entry.minikey if the entry responds to minikey.
99
- def entry_to_minikey(entry)
100
- entry.respond_to?(:minikey) ? entry.minikey : entry.to_s
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
- # Minimizes a set of paths to the set of shortest basepaths that unqiuely
106
- # identify the paths. The path extension and versions are removed from
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, mini-path) pair will be passed
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, mini_path
151
+ def minimize(paths) # :yields: path, minipath
135
152
  unless block_given?
136
- mini_paths = []
137
- minimize(paths) {|p, mp| mini_paths << mp }
138
- return mini_paths
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 mini_path matches path. Matching logic reverses
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 mini_path
198
- # * if mini_path doesn't specify an extension, then mini_path
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 mini_path doesn't specify a version, then mini_path
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, mini_path)
223
- extname = non_version_extname(mini_path)
224
- version = mini_path =~ /(-\d+(\.\d+)*)#{extname}$/ ? $1 : ''
239
+ def minimal_match?(path, minipath)
240
+ extname = non_version_extname(minipath)
241
+ version = minipath =~ /(-\d+(\.\d+)*)#{extname}$/ ? $1 : ''
225
242
 
226
- match_path = case
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
- # key ends with pattern AND basenames of each are equal...
239
- # the last check ensures that a full path segment has
240
- # been specified
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
@@ -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 parse_array(obj) # :nodoc:
69
- case obj
61
+ def resolve(refs) # :nodoc:
62
+ refs = case refs
63
+ when String then parse_indicies(refs)
70
64
  when nil then []
71
- when Array then obj
72
- else
73
- obj.split(",").collect {|str| str.to_i }
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
- lazy_attr :desc, 'join'
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
- # The App receiving self during enq
110
- attr_accessor :app
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
- initialize_config(config)
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 to_hash
151
- {
152
- :class => self.class,
153
- :config => config.to_hash
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
@@ -33,7 +33,9 @@ module Tap
33
33
  # correct 'results' slot.
34
34
  def join(inputs, outputs)
35
35
  @inputs.each do |input|
36
- input.joins.delete(self)
36
+ input.joins.delete_if do |join|
37
+ join.kind_of?(Callback) && join.join == self
38
+ end
37
39
  end if @inputs
38
40
 
39
41
  @inputs = inputs
@@ -1,34 +1,13 @@
1
- require 'configurable'
1
+ require 'tap/app'
2
2
 
3
3
  module Tap
4
- class Middleware
4
+ class Middleware < App::Api
5
5
  class << self
6
-
7
- # Instantiates an instance of self and causes app to use the instance
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