tap 0.18.0 → 0.19.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.
@@ -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