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
data/lib/tap/signals.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
require 'tap/signals/module_methods'
|
2
|
+
require 'tap/signals/configure'
|
3
|
+
require 'tap/signals/help'
|
4
|
+
require 'tap/signals/load'
|
2
5
|
|
3
6
|
module Tap
|
4
7
|
|
@@ -8,9 +11,16 @@ module Tap
|
|
8
11
|
# commonly produce a parameters hash.
|
9
12
|
#
|
10
13
|
module Signals
|
14
|
+
def signals
|
15
|
+
# memoization here is tempting, but a bad idea because the signals must
|
16
|
+
# be recalculated in case of added modules. see cache_signals for a
|
17
|
+
# better way.
|
18
|
+
self.class.signals
|
19
|
+
end
|
20
|
+
|
11
21
|
def signal(sig, &block)
|
12
22
|
sig = sig.to_s
|
13
|
-
unless signal =
|
23
|
+
unless signal = signals[sig]
|
14
24
|
raise "unknown signal: #{sig} (#{self.class})"
|
15
25
|
end
|
16
26
|
|
@@ -19,7 +29,15 @@ module Tap
|
|
19
29
|
|
20
30
|
def signal?(sig)
|
21
31
|
sig = sig.to_s
|
22
|
-
|
32
|
+
signals.has_key?(sig.to_s)
|
33
|
+
end
|
34
|
+
|
35
|
+
def sig(signal)
|
36
|
+
signal = signal.class
|
37
|
+
signals.each_pair do |sig, value|
|
38
|
+
return sig if value == signal
|
39
|
+
end
|
40
|
+
nil
|
23
41
|
end
|
24
42
|
end
|
25
43
|
end
|
@@ -1,4 +1,5 @@
|
|
1
|
-
require 'tap/
|
1
|
+
require 'tap/signal'
|
2
|
+
require 'configurable'
|
2
3
|
|
3
4
|
module Tap
|
4
5
|
module Signals
|
@@ -69,16 +70,15 @@ module Tap
|
|
69
70
|
# A block may also be provided to pre-process the argv before it is sent
|
70
71
|
# to the method; the block return is sent to the method (and so should
|
71
72
|
# be an argv).
|
72
|
-
def signal(sig, opts={}) # :yields: sig, argv
|
73
|
+
def signal(sig, opts={}, &block) # :yields: sig, argv
|
73
74
|
signature = opts[:signature] || []
|
74
75
|
remainder = opts[:remainder] || false
|
76
|
+
opts[:caller_index] ||= 2
|
75
77
|
|
76
|
-
|
78
|
+
define_signal(sig, opts) do |args|
|
77
79
|
argv = convert_to_array(args, signature, remainder)
|
78
|
-
|
80
|
+
block ? block.call(self, argv) : argv
|
79
81
|
end
|
80
|
-
|
81
|
-
register_signal(sig, signal, opts)
|
82
82
|
end
|
83
83
|
|
84
84
|
# Defines a signal to call a method that receives a single hash as an
|
@@ -88,24 +88,42 @@ module Tap
|
|
88
88
|
# A block may also be provided to pre-process the hash before it is sent
|
89
89
|
# to the method; the block return is sent to the method (and so should
|
90
90
|
# be a hash).
|
91
|
-
def signal_hash(sig, opts={}) # :yields: sig, argh
|
91
|
+
def signal_hash(sig, opts={}, &block) # :yields: sig, argh
|
92
92
|
signature = opts[:signature] || []
|
93
93
|
remainder = opts[:remainder]
|
94
|
+
opts[:caller_index] ||= 2
|
94
95
|
|
95
|
-
|
96
|
+
define_signal(sig, opts) do |args|
|
96
97
|
argh = convert_to_hash(args, signature, remainder)
|
97
|
-
[
|
98
|
+
[block ? block.call(self, argh) : argh]
|
98
99
|
end
|
99
|
-
|
100
|
-
register_signal(sig, signal, opts)
|
101
100
|
end
|
102
101
|
|
103
|
-
def
|
102
|
+
def define_signal(sig, opts=nil, &block) # :yields: args
|
103
|
+
unless opts.kind_of?(Hash)
|
104
|
+
opts = {:class => opts, :bind => false}
|
105
|
+
end
|
106
|
+
|
107
|
+
# generate a subclass of signal
|
108
|
+
klass = opts[:class] || Signal
|
109
|
+
signal = Class.new(klass)
|
110
|
+
|
111
|
+
# bind the new signal
|
112
|
+
method_name = opts.has_key?(:bind) ? opts[:bind] : sig
|
113
|
+
if method_name
|
114
|
+
signal.send(:define_method, :call) do |args|
|
115
|
+
args = process(args)
|
116
|
+
obj.send(method_name, *args, &self.block)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
104
120
|
if block_given?
|
105
|
-
signal
|
106
|
-
|
107
|
-
|
108
|
-
|
121
|
+
signal.send(:define_method, :process, &block)
|
122
|
+
end
|
123
|
+
|
124
|
+
if signal.respond_to?(:desc=)
|
125
|
+
caller_index = opts[:caller_index] || 1
|
126
|
+
signal.desc ||= Lazydoc.register_caller(Lazydoc::Trailer, caller_index)
|
109
127
|
end
|
110
128
|
|
111
129
|
register_signal(sig, signal, opts)
|
@@ -161,32 +179,7 @@ module Tap
|
|
161
179
|
super
|
162
180
|
end
|
163
181
|
|
164
|
-
def define_signal(sig, opts={}, &block) # :nodoc:
|
165
|
-
# generate a subclass of signal
|
166
|
-
klass = opts[:class] || Signal
|
167
|
-
signal = Class.new(klass)
|
168
|
-
|
169
|
-
# bind the new signal
|
170
|
-
method_name = opts.has_key?(:bind) ? opts[:bind] : sig
|
171
|
-
if method_name
|
172
|
-
signal.send(:define_method, :call) do |args|
|
173
|
-
args = process(args)
|
174
|
-
obj.send(method_name, *args, &self.block)
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
if block_given?
|
179
|
-
signal.send(:define_method, :process, &block)
|
180
|
-
end
|
181
|
-
|
182
|
-
signal
|
183
|
-
end
|
184
|
-
|
185
182
|
def register_signal(sig, signal, opts={}) # :nodoc:
|
186
|
-
if signal.respond_to?(:desc=)
|
187
|
-
signal.desc ||= Lazydoc.register_caller(Lazydoc::Trailer, 2)
|
188
|
-
end
|
189
|
-
|
190
183
|
signal_registry[sig.to_s] = signal
|
191
184
|
cache_signals(@signals != nil)
|
192
185
|
|
@@ -195,8 +188,10 @@ module Tap
|
|
195
188
|
const_name = opts.has_key?(:const_name) ? opts[:const_name] : sig.to_s.capitalize
|
196
189
|
const_name = const_name.to_s
|
197
190
|
|
198
|
-
if const_name =~ /\A[A-Z]\w*\z/
|
199
|
-
|
191
|
+
if const_name =~ /\A[A-Z]\w*\z/
|
192
|
+
unless const_defined?(const_name) && const_get(const_name) == signal
|
193
|
+
const_set(const_name, signal)
|
194
|
+
end
|
200
195
|
end
|
201
196
|
end
|
202
197
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Tap
|
2
|
+
module Signals
|
3
|
+
class Configure < Signal
|
4
|
+
def call(config)
|
5
|
+
if config.kind_of?(Array)
|
6
|
+
psr = ConfigParser.new(:add_defaults => false)
|
7
|
+
psr.add(obj.class.configurations)
|
8
|
+
args = psr.parse!(config)
|
9
|
+
psr.warn_ignored_args(args)
|
10
|
+
|
11
|
+
config = psr.config
|
12
|
+
end
|
13
|
+
|
14
|
+
obj.reconfigure(config)
|
15
|
+
obj.config
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/tap/signals/help.rb
CHANGED
@@ -1,12 +1,10 @@
|
|
1
|
-
require 'tap/signals'
|
2
|
-
|
3
1
|
module Tap
|
4
2
|
module Signals
|
5
3
|
class Help < Signal
|
6
4
|
|
7
|
-
def call(
|
8
|
-
|
9
|
-
|
5
|
+
def call(input)
|
6
|
+
args = convert_to_array(input, ['sig'])
|
7
|
+
args.empty? ? list : process(*args)
|
10
8
|
end
|
11
9
|
|
12
10
|
def list
|
@@ -22,10 +20,10 @@ module Tap
|
|
22
20
|
lines << " /#{key.ljust(width)}#{desc}"
|
23
21
|
end
|
24
22
|
|
25
|
-
"signals
|
23
|
+
"signals (#{obj.class})\n#{lines.join("\n")}"
|
26
24
|
end
|
27
25
|
|
28
|
-
def
|
26
|
+
def process(sig)
|
29
27
|
clas = obj.signal(sig).class
|
30
28
|
|
31
29
|
if clas.respond_to?(:desc)
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'tap/utils'
|
2
|
+
|
3
|
+
module Tap
|
4
|
+
module Signals
|
5
|
+
class Load < Signal
|
6
|
+
include Utils
|
7
|
+
|
8
|
+
def call(args)
|
9
|
+
args.each {|path| process(path) }
|
10
|
+
obj
|
11
|
+
end
|
12
|
+
|
13
|
+
def process(path)
|
14
|
+
if File.exists?(path)
|
15
|
+
File.open(path) do |io|
|
16
|
+
each_signal(io) do |sig, args|
|
17
|
+
obj.signal(sig).call(args)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
obj
|
23
|
+
end
|
24
|
+
|
25
|
+
def each_signal(io)
|
26
|
+
offset = -1 * ($/.length + 1)
|
27
|
+
|
28
|
+
carryover = nil
|
29
|
+
io.each_line do |line|
|
30
|
+
if line[offset] == ?\\
|
31
|
+
carryover ||= []
|
32
|
+
carryover << line[0, line.length + offset]
|
33
|
+
carryover << $/
|
34
|
+
next
|
35
|
+
end
|
36
|
+
|
37
|
+
if carryover
|
38
|
+
carryover << line
|
39
|
+
line = carryover.join
|
40
|
+
carryover = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
sig, *args = shellsplit(line)
|
44
|
+
yield(sig, args) if sig
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/tap/task.rb
CHANGED
@@ -1,58 +1,10 @@
|
|
1
|
-
require 'tap/
|
2
|
-
require 'tap/root'
|
1
|
+
require 'tap/app/api'
|
3
2
|
|
4
3
|
module Tap
|
5
|
-
class App
|
6
|
-
# Generates a task with the specified config, initialized to self.
|
7
|
-
#
|
8
|
-
# A block may be provided to overrride the process method; it will be
|
9
|
-
# called with the task instance, plus any inputs.
|
10
|
-
#
|
11
|
-
# no_inputs = app.task {|task| [] }
|
12
|
-
# one_input = app.task {|task, input| [input] }
|
13
|
-
# mixed_inputs = app.task {|task, a, b, *args| [a, b, args] }
|
14
|
-
#
|
15
|
-
# no_inputs.execute # => []
|
16
|
-
# one_input.execute(:a) # => [:a]
|
17
|
-
# mixed_inputs.execute(:a, :b) # => [:a, :b, []]
|
18
|
-
# mixed_inputs.execute(:a, :b, 1, 2, 3) # => [:a, :b, [1,2,3]]
|
19
|
-
#
|
20
|
-
def task(config={}, klass=Task, &block)
|
21
|
-
instance = klass.new(config, self)
|
22
|
-
if block_given?
|
23
|
-
instance.extend Intern
|
24
|
-
instance.process_block = block
|
25
|
-
end
|
26
|
-
instance
|
27
|
-
end
|
28
|
-
end
|
29
4
|
|
30
5
|
# Tasks are nodes that map to the command line. Tasks provide support for
|
31
6
|
# configuration, documentation, and provide helpers to build workflows.
|
32
7
|
#
|
33
|
-
# === Task Definition
|
34
|
-
#
|
35
|
-
# Tasks specify executable code by overridding the process method in
|
36
|
-
# subclasses. The number of inputs to process corresponds to the inputs
|
37
|
-
# given to execute or enq.
|
38
|
-
#
|
39
|
-
# class NoInput < Tap::Task
|
40
|
-
# def process(); []; end
|
41
|
-
# end
|
42
|
-
#
|
43
|
-
# class OneInput < Tap::Task
|
44
|
-
# def process(input); [input]; end
|
45
|
-
# end
|
46
|
-
#
|
47
|
-
# class MixedInputs < Tap::Task
|
48
|
-
# def process(a, b, *args); [a,b,args]; end
|
49
|
-
# end
|
50
|
-
#
|
51
|
-
# NoInput.new.execute # => []
|
52
|
-
# OneInput.new.execute(:a) # => [:a]
|
53
|
-
# MixedInputs.new.execute(:a, :b) # => [:a, :b, []]
|
54
|
-
# MixedInputs.new.execute(:a, :b, 1, 2, 3) # => [:a, :b, [1,2,3]]
|
55
|
-
#
|
56
8
|
# === Configuration
|
57
9
|
#
|
58
10
|
# Tasks are configurable. By default each task will be configured as
|
@@ -121,263 +73,77 @@ module Tap
|
|
121
73
|
# end
|
122
74
|
#
|
123
75
|
class Task < App::Api
|
124
|
-
include App::Node
|
125
|
-
|
126
76
|
class << self
|
127
|
-
|
128
|
-
def parser
|
77
|
+
def parser(app)
|
129
78
|
opts = super
|
130
79
|
|
131
|
-
# add option to print help
|
132
|
-
opts.on!("--help", "Print this help") do
|
133
|
-
lines = desc.kind_of?(Lazydoc::Comment) ? desc.wrap(77, 2, nil) : []
|
134
|
-
lines.collect! {|line| " #{line}"}
|
135
|
-
unless lines.empty?
|
136
|
-
line = '-' * 80
|
137
|
-
lines.unshift(line)
|
138
|
-
lines.push(line)
|
139
|
-
end
|
140
|
-
|
141
|
-
puts "#{self}#{desc.empty? ? '' : ' -- '}#{desc.to_s}"
|
142
|
-
puts help
|
143
|
-
puts "usage: tap run -- #{to_s.underscore} #{args}"
|
144
|
-
puts
|
145
|
-
puts opts
|
146
|
-
exit
|
147
|
-
end
|
148
|
-
|
149
|
-
opts.on('--enque', 'Manually enques self') do
|
150
|
-
opts['enque'] = true
|
151
|
-
end
|
152
|
-
|
153
80
|
# add option to specify a config file
|
154
81
|
opts.on('--config FILE', 'Specifies a config file') do |config_file|
|
155
|
-
|
82
|
+
configs = Configurable::Utils.load_file(config_file, true)
|
83
|
+
opts.config.merge!(configs)
|
156
84
|
end
|
157
85
|
|
158
86
|
opts
|
159
87
|
end
|
160
|
-
|
161
|
-
# Same as parse, but removes arguments destructively.
|
162
|
-
def parse!(argv=ARGV, app=Tap::App.instance)
|
163
|
-
parser = self.parser
|
164
|
-
|
165
|
-
# (note defaults are not added so they will not
|
166
|
-
# conflict with string keys from a config file)
|
167
|
-
argv = parser.parse!(argv, :add_defaults => false)
|
168
|
-
enque = parser.config.delete('enque')
|
169
|
-
instance = build({'config' => parser.nested_config}, app)
|
170
|
-
|
171
|
-
instance.enq(*argv) if enque
|
172
|
-
[instance, argv]
|
173
|
-
end
|
174
|
-
|
175
|
-
# Recursively loads path into a nested configuration file.
|
176
|
-
def load_config(path) # :nodoc:
|
177
|
-
# optimization to check for trivial paths
|
178
|
-
return {} if Root::Utils.trivial?(path)
|
179
|
-
|
180
|
-
Configurable::Utils.load_file(path, true) do |base, key, value|
|
181
|
-
base[key] ||= value if base.kind_of?(Hash)
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
protected
|
186
|
-
|
187
|
-
# Defines a task subclass with the specified configurations and process
|
188
|
-
# block. During initialization the subclass is instantiated and made
|
189
|
-
# accessible through the name method.
|
190
|
-
#
|
191
|
-
# Defined tasks may be configured during through config, or directly
|
192
|
-
# through the instance; in effect you get tasks with nested configs which
|
193
|
-
# can greatly facilitate workflows.
|
194
|
-
#
|
195
|
-
# class AddALetter < Tap::Task
|
196
|
-
# config :letter, 'a'
|
197
|
-
# def process(input); input << letter; end
|
198
|
-
# end
|
199
|
-
#
|
200
|
-
# class AlphabetSoup < Tap::Task
|
201
|
-
# define :a, AddALetter, {:letter => 'a'}
|
202
|
-
# define :b, AddALetter, {:letter => 'b'}
|
203
|
-
# define :c, AddALetter, {:letter => 'c'}
|
204
|
-
#
|
205
|
-
# def initialize(*args)
|
206
|
-
# super
|
207
|
-
# a.sequence(b, c)
|
208
|
-
# end
|
209
|
-
#
|
210
|
-
# def process
|
211
|
-
# a.execute("")
|
212
|
-
# end
|
213
|
-
# end
|
214
|
-
#
|
215
|
-
# AlphabetSoup.new.process # => 'abc'
|
216
|
-
#
|
217
|
-
# i = AlphabetSoup.new(:a => {:letter => 'x'}, :b => {:letter => 'y'}, :c => {:letter => 'z'})
|
218
|
-
# i.process # => 'xyz'
|
219
|
-
#
|
220
|
-
# i.config[:a] = {:letter => 'p'}
|
221
|
-
# i.config[:b][:letter] = 'q'
|
222
|
-
# i.c.letter = 'r'
|
223
|
-
# i.process # => 'pqr'
|
224
|
-
#
|
225
|
-
# ==== Usage
|
226
|
-
#
|
227
|
-
# Define is basically the equivalent of:
|
228
|
-
#
|
229
|
-
# class Sample < Tap::Task
|
230
|
-
# Name = baseclass.subclass(config, &block)
|
231
|
-
#
|
232
|
-
# # accesses an instance of Name
|
233
|
-
# attr_reader :name
|
234
|
-
#
|
235
|
-
# # register name as a config, but with a
|
236
|
-
# # non-standard reader and writer
|
237
|
-
# config :name, {}, {:reader => :name_config, :writer => :name_config=}.merge(options)
|
238
|
-
#
|
239
|
-
# # reader for name.config
|
240
|
-
# def name_config; ...; end
|
241
|
-
#
|
242
|
-
# # reconfigures name with input
|
243
|
-
# def name_config=(input); ...; end
|
244
|
-
#
|
245
|
-
# def initialize(*args)
|
246
|
-
# super
|
247
|
-
# @name = Name.new(config[:name])
|
248
|
-
# end
|
249
|
-
# end
|
250
|
-
#
|
251
|
-
# Note the following:
|
252
|
-
# * define will set a constant like name.camelize
|
253
|
-
# * the block defines the process method in the subclass
|
254
|
-
# * three methods are created by define: name, name_config, name_config=
|
255
|
-
#
|
256
|
-
def define(name, baseclass=Tap::Task, configs={}, options={}, &block)
|
257
|
-
# define the subclass
|
258
|
-
subclass = Class.new(baseclass)
|
259
|
-
configs.each_pair do |key, value|
|
260
|
-
subclass.send(:config, key, value)
|
261
|
-
end
|
262
|
-
|
263
|
-
if block_given?
|
264
|
-
# prevent lazydoc registration of the process method
|
265
|
-
subclass.registered_methods.delete(:process)
|
266
|
-
subclass.send(:define_method, :process, &block)
|
267
|
-
end
|
268
|
-
|
269
|
-
# register documentation
|
270
|
-
# TODO: register subclass in documentation
|
271
|
-
options[:desc] ||= Lazydoc.register_caller(Lazydoc::Trailer, 1)
|
272
|
-
|
273
|
-
# add the configuration
|
274
|
-
nest(name, subclass, {:const_name => name.to_s.camelize}.merge!(options))
|
275
|
-
end
|
276
88
|
end
|
277
89
|
|
90
|
+
# An array of joins for self
|
91
|
+
attr_reader :joins
|
92
|
+
|
93
|
+
signal :enq # enque self
|
94
|
+
signal :exe # execute self
|
95
|
+
|
278
96
|
lazy_attr :args, :process
|
279
97
|
lazy_register :process, Lazydoc::Arguments
|
280
98
|
|
281
|
-
|
282
|
-
|
283
|
-
# Initializes a new Task.
|
284
|
-
def initialize(config={}, app=Tap::App.instance)
|
99
|
+
def initialize(config={}, app=Tap::App.current)
|
100
|
+
@app = app
|
285
101
|
@joins = []
|
286
|
-
|
287
|
-
end
|
288
|
-
|
289
|
-
def associations
|
290
|
-
[nil, joins]
|
291
|
-
end
|
292
|
-
|
293
|
-
# Auditing method call. Resolves dependencies, executes method_name,
|
294
|
-
# and sends the audited result to the on_complete_block (if set).
|
295
|
-
#
|
296
|
-
# Returns the audited result.
|
297
|
-
def execute(*inputs)
|
298
|
-
app.dispatch(self, inputs)
|
102
|
+
initialize_config(config)
|
299
103
|
end
|
300
104
|
|
301
|
-
|
302
|
-
|
105
|
+
# Call splats the input to process and exists to provide subclasses
|
106
|
+
# a way to wrap process behavior.
|
107
|
+
def call(input)
|
108
|
+
process(*input)
|
303
109
|
end
|
304
110
|
|
305
|
-
# The method for processing inputs into outputs.
|
306
|
-
# subclasses to provide class-specific process logic.
|
307
|
-
#
|
308
|
-
#
|
309
|
-
#
|
111
|
+
# The method for processing inputs into outputs. Override this method in
|
112
|
+
# subclasses to provide class-specific process logic. The arguments given
|
113
|
+
# to enq/exe should correspond to the arguments required by process. The
|
114
|
+
# process return is the result passed to joins.
|
115
|
+
#
|
310
116
|
# class TaskWithTwoInputs < Tap::Task
|
311
117
|
# def process(a, b)
|
312
118
|
# [b,a]
|
313
119
|
# end
|
314
120
|
# end
|
315
|
-
#
|
121
|
+
#
|
316
122
|
# results = []
|
317
|
-
# app = Tap::App.new
|
123
|
+
# app = Tap::App.new
|
318
124
|
#
|
319
|
-
#
|
320
|
-
#
|
321
|
-
#
|
125
|
+
# task = TaskWithTwoInputs.new({}, app)
|
126
|
+
# task.enq(1,2).enq(3,4)
|
127
|
+
# task.on_complete {|result| results << result }
|
128
|
+
#
|
322
129
|
# app.run
|
323
130
|
# results # => [[2,1], [4,3]]
|
324
|
-
#
|
131
|
+
#
|
325
132
|
# By default, process simply returns the inputs.
|
326
133
|
def process(*inputs)
|
327
134
|
inputs
|
328
135
|
end
|
329
136
|
|
330
|
-
#
|
331
|
-
#
|
332
|
-
def enq(*
|
333
|
-
app.
|
334
|
-
self
|
137
|
+
# Enques self with an array of inputs (directly use app.enq to enque with
|
138
|
+
# a non-array input, or override in a subclass).
|
139
|
+
def enq(*args)
|
140
|
+
app.enq(self, args)
|
335
141
|
end
|
336
142
|
|
337
|
-
#
|
338
|
-
#
|
339
|
-
def
|
340
|
-
|
341
|
-
|
342
|
-
current_task = self
|
343
|
-
tasks.each do |next_task|
|
344
|
-
Join.new(options, app).join([current_task], [next_task])
|
345
|
-
current_task = next_task
|
346
|
-
end
|
347
|
-
end
|
348
|
-
|
349
|
-
# Sets a fork workflow pattern for self; each target will enque the
|
350
|
-
# results of self.
|
351
|
-
def fork(*targets)
|
352
|
-
options = targets[-1].kind_of?(Hash) ? targets.pop : {}
|
353
|
-
Join.new(options, app).join([self], targets)
|
354
|
-
end
|
355
|
-
|
356
|
-
# Sets a simple merge workflow pattern for the source tasks. Each
|
357
|
-
# source enques self with it's result; no synchronization occurs,
|
358
|
-
# nor are results grouped before being enqued.
|
359
|
-
def merge(*sources)
|
360
|
-
options = sources[-1].kind_of?(Hash) ? sources.pop : {}
|
361
|
-
Join.new(options, app).join(sources, [self])
|
362
|
-
end
|
363
|
-
|
364
|
-
# Sets a synchronized merge workflow for the source tasks. Results
|
365
|
-
# from each source are collected and enqued as a single group to
|
366
|
-
# self. The collective results are not enqued until all sources
|
367
|
-
# have completed. See Joins::Sync.
|
368
|
-
def sync_merge(*sources)
|
369
|
-
options = sources[-1].kind_of?(Hash) ? sources.pop : {}
|
370
|
-
Joins::Sync.new(options, app).join(sources, [self])
|
371
|
-
end
|
372
|
-
|
373
|
-
# Sets a switch workflow pattern for self. On complete, switch yields
|
374
|
-
# the result to the block and the block should return the index of the
|
375
|
-
# target to enque with the results. No target will be enqued if the
|
376
|
-
# index is false or nil. An error is raised if no target can be found
|
377
|
-
# for the specified index. See Joins::Switch.
|
378
|
-
def switch(*targets, &block) # :yields: result
|
379
|
-
options = targets[-1].kind_of?(Hash) ? targets.pop : {}
|
380
|
-
Joins::Switch.new(options, app).join([self], targets, &block)
|
143
|
+
# Executes self with an array of inputs (directly use app.exe to execute
|
144
|
+
# with a non-array input, or override in a subclass).
|
145
|
+
def exe(*args)
|
146
|
+
app.exe(self, args)
|
381
147
|
end
|
382
148
|
|
383
149
|
# Logs the inputs to the application logger (via app.log)
|
@@ -385,10 +151,15 @@ module Tap
|
|
385
151
|
app.log(action, msg, level) { yield }
|
386
152
|
end
|
387
153
|
|
388
|
-
#
|
389
|
-
|
390
|
-
|
391
|
-
|
154
|
+
# Sets the block as a join for self.
|
155
|
+
def on_complete(&block) # :yields: result
|
156
|
+
joins << block if block
|
157
|
+
self
|
158
|
+
end
|
159
|
+
|
160
|
+
# Returns the associations array: [nil, joins]
|
161
|
+
def associations
|
162
|
+
[nil, joins]
|
392
163
|
end
|
393
164
|
end
|
394
165
|
end
|