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.
Files changed (66) hide show
  1. data/History +100 -45
  2. data/MIT-LICENSE +1 -1
  3. data/README +95 -51
  4. data/bin/tap +11 -57
  5. data/bin/tapexe +84 -0
  6. data/doc/API +91 -139
  7. data/doc/Configuration +93 -0
  8. data/doc/Examples/Command Line +10 -42
  9. data/doc/Examples/Tapfile +124 -0
  10. data/doc/Ruby to Ruby +87 -0
  11. data/doc/Workflow Syntax +185 -0
  12. data/lib/tap.rb +74 -5
  13. data/lib/tap/app.rb +217 -310
  14. data/lib/tap/app/api.rb +44 -23
  15. data/lib/tap/app/queue.rb +11 -12
  16. data/lib/tap/app/stack.rb +4 -4
  17. data/lib/tap/declarations.rb +200 -0
  18. data/lib/tap/declarations/context.rb +31 -0
  19. data/lib/tap/declarations/description.rb +33 -0
  20. data/lib/tap/env.rb +133 -779
  21. data/lib/tap/env/cache.rb +87 -0
  22. data/lib/tap/env/constant.rb +94 -39
  23. data/lib/tap/env/path.rb +71 -0
  24. data/lib/tap/join.rb +42 -78
  25. data/lib/tap/joins/gate.rb +85 -0
  26. data/lib/tap/joins/switch.rb +4 -2
  27. data/lib/tap/joins/sync.rb +3 -3
  28. data/lib/tap/middleware.rb +5 -5
  29. data/lib/tap/middlewares/debugger.rb +18 -58
  30. data/lib/tap/parser.rb +115 -183
  31. data/lib/tap/root.rb +162 -239
  32. data/lib/tap/signal.rb +72 -0
  33. data/lib/tap/signals.rb +20 -2
  34. data/lib/tap/signals/class_methods.rb +38 -43
  35. data/lib/tap/signals/configure.rb +19 -0
  36. data/lib/tap/signals/help.rb +5 -7
  37. data/lib/tap/signals/load.rb +49 -0
  38. data/lib/tap/signals/module_methods.rb +1 -0
  39. data/lib/tap/task.rb +46 -275
  40. data/lib/tap/tasks/dump.rb +21 -16
  41. data/lib/tap/tasks/list.rb +184 -0
  42. data/lib/tap/tasks/load.rb +4 -4
  43. data/lib/tap/tasks/prompt.rb +128 -0
  44. data/lib/tap/tasks/signal.rb +42 -0
  45. data/lib/tap/tasks/singleton.rb +35 -0
  46. data/lib/tap/tasks/stream.rb +64 -0
  47. data/lib/tap/utils.rb +83 -0
  48. data/lib/tap/version.rb +2 -2
  49. data/lib/tap/workflow.rb +124 -0
  50. data/tap.yml +0 -0
  51. metadata +59 -24
  52. data/cmd/console.rb +0 -43
  53. data/cmd/manifest.rb +0 -118
  54. data/cmd/run.rb +0 -145
  55. data/doc/Examples/Workflow +0 -40
  56. data/lib/tap/app/node.rb +0 -29
  57. data/lib/tap/env/context.rb +0 -61
  58. data/lib/tap/env/gems.rb +0 -63
  59. data/lib/tap/env/manifest.rb +0 -179
  60. data/lib/tap/env/minimap.rb +0 -308
  61. data/lib/tap/intern.rb +0 -50
  62. data/lib/tap/joins.rb +0 -9
  63. data/lib/tap/prompt.rb +0 -36
  64. data/lib/tap/root/utils.rb +0 -220
  65. data/lib/tap/root/versions.rb +0 -138
  66. data/lib/tap/signals/signal.rb +0 -68
@@ -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 = self.class.signals[sig]
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
- self.class.signals.has_key?(sig.to_s)
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/signals/signal'
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
- signal = define_signal(sig, opts) do |args|
78
+ define_signal(sig, opts) do |args|
77
79
  argv = convert_to_array(args, signature, remainder)
78
- block_given? ? yield(self, argv) : argv
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
- signal = define_signal(sig, opts) do |args|
96
+ define_signal(sig, opts) do |args|
96
97
  argh = convert_to_hash(args, signature, remainder)
97
- [block_given? ? yield(self, argh) : argh]
98
+ [block ? block.call(self, argh) : argh]
98
99
  end
99
-
100
- register_signal(sig, signal, opts)
101
100
  end
102
101
 
103
- def signal_class(sig, signal_class=Signal, opts={}, &block) # :yields: sig, argv
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 = Class.new(signal_class)
106
- signal.class_eval(&block)
107
- else
108
- signal = signal_class
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/ && !const_defined?(const_name)
199
- const_set(const_name, signal)
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
@@ -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(args)
8
- argv = convert_to_array(args, ['sig'])
9
- argv.empty? ? list : desc(*argv)
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: (#{obj.class})\n#{lines.join("\n")}"
23
+ "signals (#{obj.class})\n#{lines.join("\n")}"
26
24
  end
27
25
 
28
- def desc(sig)
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
@@ -7,6 +7,7 @@ module Tap
7
7
 
8
8
  # Extends including classes with Configurable::ClassMethods
9
9
  def included(base)
10
+ super
10
11
  base.extend ClassMethods
11
12
  base.extend ModuleMethods unless base.kind_of?(Class)
12
13
 
@@ -1,58 +1,10 @@
1
- require 'tap/joins'
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
- opts.config.merge!(load_config(config_file))
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
- signal :enq
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
- super
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
- def call(*inputs)
302
- process(*inputs)
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. Override this method in
306
- # subclasses to provide class-specific process logic. The number of
307
- # arguments specified by process corresponds to the number of arguments
308
- # the task should have when enqued or executed.
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 {|result| results << result }
123
+ # app = Tap::App.new
318
124
  #
319
- # t = TaskWithTwoInputs.new({}, app)
320
- # t.enq(1,2).enq(3,4)
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
- # Enqueues self to app with the inputs. The number of inputs provided
331
- # should match the number of inputs for the method_name method.
332
- def enq(*inputs)
333
- app.queue.enq(self, inputs)
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
- # Sets a sequence workflow pattern for the tasks; each task
338
- # enques the next task with it's results, starting with self.
339
- def sequence(*tasks)
340
- options = tasks[-1].kind_of?(Hash) ? tasks.pop : {}
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
- # Provides an abbreviated version of the default inspect, with only
389
- # the task class, object_id, and configurations listed.
390
- def inspect
391
- "#<#{self.class.to_s}:#{object_id} #{config.to_hash.inspect} >"
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