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.
- data/History +100 -45
- data/MIT-LICENSE +1 -1
- data/README +95 -51
- data/bin/tap +11 -57
- data/bin/tapexe +84 -0
- data/doc/API +91 -139
- data/doc/Configuration +93 -0
- data/doc/Examples/Command Line +10 -42
- data/doc/Examples/Tapfile +124 -0
- data/doc/Ruby to Ruby +87 -0
- data/doc/Workflow Syntax +185 -0
- data/lib/tap.rb +74 -5
- data/lib/tap/app.rb +217 -310
- data/lib/tap/app/api.rb +44 -23
- data/lib/tap/app/queue.rb +11 -12
- data/lib/tap/app/stack.rb +4 -4
- data/lib/tap/declarations.rb +200 -0
- data/lib/tap/declarations/context.rb +31 -0
- data/lib/tap/declarations/description.rb +33 -0
- data/lib/tap/env.rb +133 -779
- data/lib/tap/env/cache.rb +87 -0
- data/lib/tap/env/constant.rb +94 -39
- data/lib/tap/env/path.rb +71 -0
- data/lib/tap/join.rb +42 -78
- data/lib/tap/joins/gate.rb +85 -0
- data/lib/tap/joins/switch.rb +4 -2
- data/lib/tap/joins/sync.rb +3 -3
- data/lib/tap/middleware.rb +5 -5
- data/lib/tap/middlewares/debugger.rb +18 -58
- data/lib/tap/parser.rb +115 -183
- data/lib/tap/root.rb +162 -239
- data/lib/tap/signal.rb +72 -0
- data/lib/tap/signals.rb +20 -2
- data/lib/tap/signals/class_methods.rb +38 -43
- data/lib/tap/signals/configure.rb +19 -0
- data/lib/tap/signals/help.rb +5 -7
- data/lib/tap/signals/load.rb +49 -0
- data/lib/tap/signals/module_methods.rb +1 -0
- data/lib/tap/task.rb +46 -275
- data/lib/tap/tasks/dump.rb +21 -16
- data/lib/tap/tasks/list.rb +184 -0
- data/lib/tap/tasks/load.rb +4 -4
- data/lib/tap/tasks/prompt.rb +128 -0
- data/lib/tap/tasks/signal.rb +42 -0
- data/lib/tap/tasks/singleton.rb +35 -0
- data/lib/tap/tasks/stream.rb +64 -0
- data/lib/tap/utils.rb +83 -0
- data/lib/tap/version.rb +2 -2
- data/lib/tap/workflow.rb +124 -0
- data/tap.yml +0 -0
- metadata +59 -24
- data/cmd/console.rb +0 -43
- data/cmd/manifest.rb +0 -118
- data/cmd/run.rb +0 -145
- data/doc/Examples/Workflow +0 -40
- data/lib/tap/app/node.rb +0 -29
- data/lib/tap/env/context.rb +0 -61
- data/lib/tap/env/gems.rb +0 -63
- data/lib/tap/env/manifest.rb +0 -179
- data/lib/tap/env/minimap.rb +0 -308
- data/lib/tap/intern.rb +0 -50
- data/lib/tap/joins.rb +0 -9
- data/lib/tap/prompt.rb +0 -36
- data/lib/tap/root/utils.rb +0 -220
- data/lib/tap/root/versions.rb +0 -138
- data/lib/tap/signals/signal.rb +0 -68
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'tap/env/path'
|
2
|
+
|
3
|
+
autoload(:Gem, 'rubygems')
|
4
|
+
autoload(:RbConfig, 'rbconfig')
|
5
|
+
module Tap
|
6
|
+
class Env
|
7
|
+
class Cache
|
8
|
+
attr_reader :cache_home
|
9
|
+
|
10
|
+
def initialize(dir=Dir.pwd, debug=false)
|
11
|
+
@cache_home = File.expand_path("#{RbConfig::CONFIG['RUBY_INSTALL_NAME']}/#{RUBY_VERSION}", dir)
|
12
|
+
@debug = debug
|
13
|
+
end
|
14
|
+
|
15
|
+
def select(dependencies)
|
16
|
+
if dependencies.kind_of?(String)
|
17
|
+
dependencies = dependencies.split(':')
|
18
|
+
end
|
19
|
+
|
20
|
+
paths = []
|
21
|
+
dependencies.collect! do |dep|
|
22
|
+
dep.kind_of?(String) ? dep.split(',', 2) : dep
|
23
|
+
end.each do |(pattern, version_requirements)|
|
24
|
+
pattern = Regexp.new(pattern) if pattern.kind_of?(String)
|
25
|
+
paths.concat search(pattern, version_requirements)
|
26
|
+
end
|
27
|
+
|
28
|
+
paths.uniq!
|
29
|
+
paths
|
30
|
+
end
|
31
|
+
|
32
|
+
def search(pattern, version_requirements)
|
33
|
+
dependency = Gem::Dependency.new(pattern, version_requirements)
|
34
|
+
|
35
|
+
sources = {}
|
36
|
+
Gem.source_index.search(dependency).sort_by do |spec|
|
37
|
+
spec.version
|
38
|
+
end.reverse_each do |spec|
|
39
|
+
sources[spec.name] ||= spec
|
40
|
+
end
|
41
|
+
|
42
|
+
paths = []
|
43
|
+
sources.values.sort_by do |spec|
|
44
|
+
spec.name
|
45
|
+
end.each do |spec|
|
46
|
+
unless File.exists? File.expand_path(Path::FILE, spec.full_gem_path)
|
47
|
+
next
|
48
|
+
end
|
49
|
+
|
50
|
+
path = File.join(cache_home, spec.full_name)
|
51
|
+
gem_path = spec.full_gem_path
|
52
|
+
|
53
|
+
unless FileUtils.uptodate?(path, [gem_path, __FILE__])
|
54
|
+
unless File.exists?(cache_home)
|
55
|
+
FileUtils.mkdir_p(cache_home)
|
56
|
+
end
|
57
|
+
|
58
|
+
if @debug
|
59
|
+
$stderr.puts(App::LOG_FORMAT % [' ', nil, :generate, spec.full_name])
|
60
|
+
end
|
61
|
+
|
62
|
+
File.open(path, 'w') do |io|
|
63
|
+
io << generate(spec)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
paths << path
|
68
|
+
end
|
69
|
+
|
70
|
+
paths
|
71
|
+
end
|
72
|
+
|
73
|
+
def generate(spec)
|
74
|
+
lines = Env.generate(
|
75
|
+
:dir => spec.full_gem_path,
|
76
|
+
:pathfile => File.expand_path(Path::FILE, spec.full_gem_path),
|
77
|
+
:load_paths => false)
|
78
|
+
|
79
|
+
lines.unshift "# Generated for #{spec.full_name} on #{Time.now}. Do not edit."
|
80
|
+
lines << "activate #{spec.name} #{spec.version}"
|
81
|
+
lines.uniq!
|
82
|
+
lines.sort!
|
83
|
+
lines.join("\n")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/tap/env/constant.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'tap/env/string_ext'
|
2
|
+
require 'tap/root'
|
2
3
|
|
3
4
|
module Tap
|
4
5
|
class Env
|
@@ -8,11 +9,11 @@ module Tap
|
|
8
9
|
# it doesn't exist, constantize requires require_path and tries again.
|
9
10
|
#
|
10
11
|
# Object.const_defined?(:Net) # => false
|
11
|
-
# $".
|
12
|
+
# $".grep(/net\/http.rb$/).empty? # => true
|
12
13
|
#
|
13
14
|
# http = Constant.new('Net::HTTP', 'net/http.rb')
|
14
15
|
# http.constantize # => Net::HTTP
|
15
|
-
# $".
|
16
|
+
# $".grep(/net\/http.rb$/).empty? # => false
|
16
17
|
#
|
17
18
|
# === Unloading
|
18
19
|
#
|
@@ -84,33 +85,39 @@ module Tap
|
|
84
85
|
const
|
85
86
|
end
|
86
87
|
|
87
|
-
# Scans the directory and pattern for constants
|
88
|
-
|
89
|
-
|
90
|
-
if pattern.include?("..")
|
91
|
-
raise "patterns cannot include relative paths: #{pattern.inspect}"
|
92
|
-
end
|
88
|
+
# Scans the directory and pattern for constants.
|
89
|
+
def scan(dir, pattern="**/*.rb")
|
90
|
+
constants = {}
|
93
91
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
Lazydoc::Document.scan(File.read(path)) do |const_name, type, summary|
|
103
|
-
const_name = default_const_name if const_name.empty?
|
104
|
-
|
105
|
-
constant = (constants[const_name] ||= Constant.new(const_name))
|
106
|
-
constant.register_as(type, summary)
|
107
|
-
constant.require_paths << path
|
92
|
+
root = Root.new(dir)
|
93
|
+
root.glob(pattern).each do |path|
|
94
|
+
Lazydoc::Document.scan(File.read(path)) do |const_name, type, summary|
|
95
|
+
require_path = root.relative_path(path)
|
96
|
+
|
97
|
+
if const_name.empty?
|
98
|
+
extname = File.extname(path)
|
99
|
+
const_name = require_path.chomp(extname).camelize
|
108
100
|
end
|
101
|
+
|
102
|
+
constant = (constants[const_name] ||= new(const_name))
|
103
|
+
constant.register_as(type, summary)
|
104
|
+
constant.require_paths << require_path
|
109
105
|
end
|
110
106
|
end
|
111
|
-
|
107
|
+
|
108
|
+
constants = constants.values
|
109
|
+
constants.each {|constant| constant.require_paths.uniq! }
|
112
110
|
constants
|
113
111
|
end
|
112
|
+
|
113
|
+
def cast(obj)
|
114
|
+
case obj
|
115
|
+
when String then new(obj)
|
116
|
+
when Module then new(obj.to_s)
|
117
|
+
when Constant then obj
|
118
|
+
else raise ArgumentError, "not a constant or constant name: #{obj.inspect}"
|
119
|
+
end
|
120
|
+
end
|
114
121
|
|
115
122
|
private
|
116
123
|
|
@@ -129,9 +136,12 @@ module Tap
|
|
129
136
|
end
|
130
137
|
end
|
131
138
|
|
132
|
-
# Matches a valid constant
|
139
|
+
# Matches a valid constant. After the match:
|
140
|
+
#
|
141
|
+
# $1:: The unqualified constant (ex 'Const' for '::Const')
|
142
|
+
#
|
133
143
|
CONST_REGEXP = /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/
|
134
|
-
|
144
|
+
|
135
145
|
# The full constant name
|
136
146
|
attr_reader :const_name
|
137
147
|
|
@@ -142,21 +152,24 @@ module Tap
|
|
142
152
|
# A hash of (type, summary) pairs used to classify self.
|
143
153
|
attr_reader :types
|
144
154
|
|
145
|
-
# Initializes a new Constant with the specified constant name,
|
146
|
-
#
|
147
|
-
# constant name.
|
155
|
+
# Initializes a new Constant with the specified constant name, and
|
156
|
+
# require_paths. Raises an error if const_name is not valid.
|
148
157
|
def initialize(const_name, *require_paths)
|
149
|
-
@const_name = const_name
|
150
|
-
@require_paths = require_paths
|
151
158
|
@types = {}
|
159
|
+
@const_name = normalize(const_name)
|
160
|
+
@require_paths = require_paths
|
161
|
+
end
|
162
|
+
|
163
|
+
def relative_path
|
164
|
+
@relative_path ||= const_name.underscore
|
152
165
|
end
|
153
166
|
|
154
167
|
# Returns the underscored const_name.
|
155
168
|
#
|
156
|
-
# Constant.new("Const::Name").path # => 'const/name'
|
169
|
+
# Constant.new("Const::Name").path # => '/const/name'
|
157
170
|
#
|
158
171
|
def path
|
159
|
-
@path ||=
|
172
|
+
@path ||= "/#{relative_path}"
|
160
173
|
end
|
161
174
|
|
162
175
|
# Returns the basename of path.
|
@@ -169,10 +182,10 @@ module Tap
|
|
169
182
|
|
170
183
|
# Returns the path, minus the basename of path.
|
171
184
|
#
|
172
|
-
# Constant.new("Const::Name").dirname # => 'const'
|
185
|
+
# Constant.new("Const::Name").dirname # => '/const'
|
173
186
|
#
|
174
187
|
def dirname
|
175
|
-
@dirname ||=
|
188
|
+
@dirname ||= File.dirname(path)
|
176
189
|
end
|
177
190
|
|
178
191
|
# Returns the name of the constant, minus nesting.
|
@@ -206,12 +219,17 @@ module Tap
|
|
206
219
|
another.const_name == self.const_name &&
|
207
220
|
another.require_paths == self.require_paths
|
208
221
|
end
|
222
|
+
|
223
|
+
# Peforms comparison of the const_name of self vs another.
|
224
|
+
def <=>(another)
|
225
|
+
const_name <=> another.const_name
|
226
|
+
end
|
209
227
|
|
210
228
|
# Registers the type and summary with self. Raises an error if self is
|
211
229
|
# already registerd as the type and override is false.
|
212
230
|
def register_as(type, summary=nil, override=false)
|
213
|
-
if types.include?(type) && !override
|
214
|
-
raise "already registered as a #{type.inspect}"
|
231
|
+
if types.include?(type) && types[type] != summary && !override
|
232
|
+
raise "already registered as a #{type.inspect} (#{const_name})"
|
215
233
|
end
|
216
234
|
|
217
235
|
types[type] = summary
|
@@ -250,8 +268,10 @@ module Tap
|
|
250
268
|
|
251
269
|
if const.const_defined?(name)
|
252
270
|
require_paths.each do |require_path|
|
253
|
-
|
254
|
-
|
271
|
+
require_path = File.extname(require_path).empty? ? "#{require_path}.rb" : require_path
|
272
|
+
regexp = /#{require_path}$/
|
273
|
+
|
274
|
+
$".delete_if {|path| path =~ regexp }
|
255
275
|
end if unrequire
|
256
276
|
|
257
277
|
return const.send(:remove_const, name)
|
@@ -259,7 +279,19 @@ module Tap
|
|
259
279
|
|
260
280
|
nil
|
261
281
|
end
|
262
|
-
|
282
|
+
|
283
|
+
def path_match?(head, tail=nil)
|
284
|
+
(head.nil? || head.empty? || head_match(head)) && (tail.nil? || tail.empty? || tail_match(tail))
|
285
|
+
end
|
286
|
+
|
287
|
+
def type_match?(type)
|
288
|
+
case type
|
289
|
+
when nil then true
|
290
|
+
when Array then type.any? {|t| types.has_key?(t) }
|
291
|
+
else types.has_key?(type)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
263
295
|
# Returns a string like:
|
264
296
|
#
|
265
297
|
# "#<Tap::Env::Constant:object_id Const::Name (require_path)>"
|
@@ -272,6 +304,29 @@ module Tap
|
|
272
304
|
def to_s
|
273
305
|
const_name
|
274
306
|
end
|
307
|
+
|
308
|
+
private
|
309
|
+
|
310
|
+
def normalize(const_name) # :nodoc:
|
311
|
+
case const_name
|
312
|
+
when Module then const_name.to_s
|
313
|
+
when CONST_REGEXP then $1
|
314
|
+
else raise NameError, "#{const_name.inspect} is not a valid constant name!"
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
def head_match(head) # :nodoc:
|
319
|
+
index = path.index(head)
|
320
|
+
index == (head[0] == ?/ ? 0 : 1) && begin
|
321
|
+
match_end = index + head.length
|
322
|
+
(match_end == path.length || path[match_end] == ?/)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def tail_match(tail) # :nodoc:
|
327
|
+
index = path.rindex(tail)
|
328
|
+
index && (index + tail.length) == path.length && (index == 0 || path[index-1] == ?/)
|
329
|
+
end
|
275
330
|
end
|
276
331
|
end
|
277
332
|
end
|
data/lib/tap/env/path.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
module Tap
|
2
|
+
class Env
|
3
|
+
class Path
|
4
|
+
class << self
|
5
|
+
|
6
|
+
# Splits the path string along ':' boundaries and expands each
|
7
|
+
# resulting fragments relative to dir. Duplicate paths are removed.
|
8
|
+
# Returns the resulting paths.
|
9
|
+
#
|
10
|
+
# An array of pre-split paths may also be provided as an input.
|
11
|
+
def split(str, dir=Dir.pwd)
|
12
|
+
paths = str.kind_of?(String) ? str.split(':') : str
|
13
|
+
paths.collect! {|path| File.expand_path(path, dir) } if dir
|
14
|
+
paths.uniq!
|
15
|
+
paths
|
16
|
+
end
|
17
|
+
|
18
|
+
def join(paths)
|
19
|
+
paths.join(':')
|
20
|
+
end
|
21
|
+
|
22
|
+
def load(path_file)
|
23
|
+
Root.trivial?(path_file) ? {} : (YAML.load_file(path_file) || {})
|
24
|
+
end
|
25
|
+
|
26
|
+
def escape(str)
|
27
|
+
"'#{str.gsub("'", "\\\\'")}'"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
FILE = 'tap.yml'
|
32
|
+
|
33
|
+
# The path base.
|
34
|
+
attr_reader :base
|
35
|
+
|
36
|
+
# A mapping of types to paths.
|
37
|
+
attr_reader :map
|
38
|
+
|
39
|
+
# Creates a new Path relative to the base.
|
40
|
+
def initialize(base, map={})
|
41
|
+
@base = File.expand_path(base)
|
42
|
+
@map = {}
|
43
|
+
|
44
|
+
map.each_pair {|type, paths| self[type] = paths }
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns an array of expanded paths associated with the type; by
|
48
|
+
# default the type expanded under base.
|
49
|
+
def [](type)
|
50
|
+
map[type] ||= [File.expand_path(type.to_s, base)]
|
51
|
+
end
|
52
|
+
|
53
|
+
# Sets the path for the type. Paths are split and expanded relative to
|
54
|
+
# base (see Path.split).
|
55
|
+
def []=(type, paths)
|
56
|
+
map[type] = Path.split(paths, base)
|
57
|
+
end
|
58
|
+
|
59
|
+
def ==(another)
|
60
|
+
another.kind_of?(Path) &&
|
61
|
+
base == another.base &&
|
62
|
+
map == another.map
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns the base path.
|
66
|
+
def to_s
|
67
|
+
base
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/tap/join.rb
CHANGED
@@ -1,15 +1,7 @@
|
|
1
|
-
require 'tap/app'
|
2
|
-
require 'tap/intern'
|
1
|
+
require 'tap/app/api'
|
3
2
|
|
4
3
|
module Tap
|
5
|
-
|
6
|
-
# Generates a join between the inputs and outputs.
|
7
|
-
def join(inputs, outputs, config={}, klass=Join, &block)
|
8
|
-
klass.new(config, self).join(inputs, outputs, &block)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
# :startdoc::join an unsyncrhonized, multi-way join
|
4
|
+
# :startdoc::join unsyncrhonized multi-way join
|
13
5
|
#
|
14
6
|
# Join defines an unsynchronized, multi-way join where n inputs send their
|
15
7
|
# results to m outputs. Flags can augment how the results are passed, in
|
@@ -17,34 +9,7 @@ module Tap
|
|
17
9
|
#
|
18
10
|
class Join < App::Api
|
19
11
|
class << self
|
20
|
-
|
21
|
-
# Instantiates a new join with the input arguments and overrides
|
22
|
-
# call with the block. The block will be called with the join
|
23
|
-
# instance and result.
|
24
|
-
#
|
25
|
-
# Simply instantiates a new join if no block is given.
|
26
|
-
def intern(config={}, app=Tap::App.instance, &block) # :yields: join, result
|
27
|
-
instance = new(config, app)
|
28
|
-
if block_given?
|
29
|
-
instance.extend Intern(:call)
|
30
|
-
instance.call_block = block
|
31
|
-
end
|
32
|
-
instance
|
33
|
-
end
|
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)
|
12
|
+
def build(spec={}, app=Tap::App.current)
|
48
13
|
inputs = resolve(spec['inputs']) do |var|
|
49
14
|
app.get(var) or raise "missing join input: #{var}"
|
50
15
|
end
|
@@ -58,6 +23,14 @@ module Tap
|
|
58
23
|
|
59
24
|
protected
|
60
25
|
|
26
|
+
def convert_to_spec(parser, args)
|
27
|
+
{
|
28
|
+
'config' => parser.nested_config,
|
29
|
+
'inputs' => args.shift,
|
30
|
+
'outputs' => args.shift
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
61
34
|
def resolve(refs) # :nodoc:
|
62
35
|
refs = case refs
|
63
36
|
when String then parse_indicies(refs)
|
@@ -83,34 +56,23 @@ module Tap
|
|
83
56
|
end
|
84
57
|
|
85
58
|
# Causes the outputs to be enqued rather than executed immediately.
|
86
|
-
config :enq, false, :short => 'q', &c.flag # Enque output
|
59
|
+
config :enq, false, :short => 'q', &c.flag # Enque output tasks
|
87
60
|
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
|
92
|
-
# # outputs: call(*inputs)
|
93
|
-
# app.execute(output, *result)
|
94
|
-
#
|
95
|
-
config :splat, false, :short => 's', &c.flag # Splat results to outputs
|
61
|
+
# Converts each result into a one-member array before being passed onto
|
62
|
+
# outputs. Arrayify occurs before iterate and combined the two flags
|
63
|
+
# cancel.
|
64
|
+
config :arrayify, false, :short => 'a', &c.flag # Arrayify results
|
96
65
|
|
97
|
-
# Iterates the results to the outputs
|
98
|
-
#
|
99
|
-
# to arrays using to_ary:
|
66
|
+
# Iterates the results to the outputs. Non-array results are converted to
|
67
|
+
# arrays using to_ary:
|
100
68
|
#
|
101
69
|
# # results: [1,2,3]
|
102
70
|
# # outputs: call(input)
|
103
|
-
# result.to_ary.each {|r| app.
|
104
|
-
#
|
105
|
-
# Iterate may be combined with splat:
|
106
|
-
#
|
107
|
-
# # results: [[1,2],3]
|
108
|
-
# # outputs: call(*inputs)
|
109
|
-
# result.to_ary.each {|r| app.execute(output, *r) }
|
71
|
+
# result.to_ary.each {|r| app.exe(output, r) }
|
110
72
|
#
|
111
73
|
config :iterate, false, :short => 'i', &c.flag # Iterate results to outputs
|
112
74
|
|
113
|
-
signal :join do |sig, (inputs, outputs)|
|
75
|
+
signal :join do |sig, (inputs, outputs)| # join app objects
|
114
76
|
app = sig.obj.app
|
115
77
|
|
116
78
|
inputs = resolve(inputs) do |var|
|
@@ -124,14 +86,14 @@ module Tap
|
|
124
86
|
[inputs, outputs]
|
125
87
|
end
|
126
88
|
|
127
|
-
# An array of input
|
89
|
+
# An array of input tasks, or nil if the join has not been set.
|
128
90
|
attr_reader :inputs
|
129
91
|
|
130
|
-
# An array of output
|
92
|
+
# An array of output tasks, or nil if the join has not been set.
|
131
93
|
attr_reader :outputs
|
132
94
|
|
133
95
|
# Initializes a new join with the specified configuration.
|
134
|
-
def initialize(config={}, app=Tap::App.
|
96
|
+
def initialize(config={}, app=Tap::App.current)
|
135
97
|
@inputs = nil
|
136
98
|
@outputs = nil
|
137
99
|
super
|
@@ -143,6 +105,12 @@ module Tap
|
|
143
105
|
input.joins.delete(self)
|
144
106
|
end if @inputs
|
145
107
|
|
108
|
+
inputs.each do |input|
|
109
|
+
unless input.respond_to?(:joins)
|
110
|
+
raise "input does not support joins: #{input.inspect}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
146
114
|
@inputs = inputs
|
147
115
|
|
148
116
|
inputs.each do |input|
|
@@ -157,7 +125,7 @@ module Tap
|
|
157
125
|
# each output.
|
158
126
|
def call(result)
|
159
127
|
outputs.each do |output|
|
160
|
-
|
128
|
+
exe(output, result)
|
161
129
|
end
|
162
130
|
end
|
163
131
|
|
@@ -167,29 +135,25 @@ module Tap
|
|
167
135
|
|
168
136
|
def to_spec
|
169
137
|
spec = super
|
170
|
-
spec['inputs'] = inputs.collect {|
|
171
|
-
spec['outputs'] = outputs.collect {|
|
138
|
+
spec['inputs'] = inputs.collect {|task| app.var(task) }
|
139
|
+
spec['outputs'] = outputs.collect {|task| app.var(task) }
|
172
140
|
spec
|
173
141
|
end
|
174
142
|
|
175
143
|
protected
|
176
144
|
|
177
|
-
#
|
178
|
-
def
|
179
|
-
mode = enq ? :enq : :
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
execute(mode, node, result)
|
145
|
+
# Executes the task with the input results.
|
146
|
+
def exe(task, result) # :nodoc:
|
147
|
+
mode = enq ? :enq : :exe
|
148
|
+
|
149
|
+
if arrayify
|
150
|
+
result = [result]
|
184
151
|
end
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
def execute(mode, node, result) # :nodoc:
|
189
|
-
if splat
|
190
|
-
app.send(mode, node, *result)
|
152
|
+
|
153
|
+
if iterate
|
154
|
+
result.to_ary.each {|item| app.send(mode, task, item) }
|
191
155
|
else
|
192
|
-
app.send(mode,
|
156
|
+
app.send(mode, task, result)
|
193
157
|
end
|
194
158
|
end
|
195
159
|
end
|