tap 0.12.4 → 0.17.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 (102) hide show
  1. data/History +34 -0
  2. data/README +62 -41
  3. data/bin/tap +36 -40
  4. data/cmd/console.rb +14 -6
  5. data/cmd/manifest.rb +62 -58
  6. data/cmd/run.rb +49 -31
  7. data/doc/API +84 -0
  8. data/doc/Class Reference +83 -115
  9. data/doc/Examples/Command Line +36 -0
  10. data/doc/Examples/Workflow +40 -0
  11. data/lib/tap/app.rb +293 -214
  12. data/lib/tap/app/node.rb +43 -0
  13. data/lib/tap/app/queue.rb +77 -0
  14. data/lib/tap/app/stack.rb +16 -0
  15. data/lib/tap/app/state.rb +22 -0
  16. data/lib/tap/constants.rb +2 -2
  17. data/lib/tap/env.rb +400 -314
  18. data/lib/tap/env/constant.rb +227 -0
  19. data/lib/tap/env/gems.rb +63 -0
  20. data/lib/tap/env/manifest.rb +89 -0
  21. data/lib/tap/env/minimap.rb +292 -0
  22. data/lib/tap/{support → env}/string_ext.rb +2 -2
  23. data/lib/tap/exe.rb +113 -125
  24. data/lib/tap/join.rb +175 -0
  25. data/lib/tap/joins.rb +9 -0
  26. data/lib/tap/joins/switch.rb +44 -0
  27. data/lib/tap/joins/sync.rb +99 -0
  28. data/lib/tap/root.rb +100 -491
  29. data/lib/tap/root/utils.rb +220 -0
  30. data/lib/tap/{support → root}/versions.rb +31 -29
  31. data/lib/tap/schema.rb +248 -0
  32. data/lib/tap/schema/parser.rb +413 -0
  33. data/lib/tap/schema/utils.rb +82 -0
  34. data/lib/tap/support/intern.rb +19 -6
  35. data/lib/tap/support/templater.rb +8 -3
  36. data/lib/tap/task.rb +175 -171
  37. data/lib/tap/tasks/dump.rb +58 -0
  38. data/lib/tap/tasks/load.rb +62 -0
  39. metadata +30 -73
  40. data/cmd/destroy.rb +0 -27
  41. data/cmd/generate.rb +0 -27
  42. data/doc/Command Reference +0 -105
  43. data/doc/Syntax Reference +0 -234
  44. data/doc/Tutorial +0 -348
  45. data/lib/tap/dump.rb +0 -142
  46. data/lib/tap/file_task.rb +0 -384
  47. data/lib/tap/generator/arguments.rb +0 -13
  48. data/lib/tap/generator/base.rb +0 -176
  49. data/lib/tap/generator/destroy.rb +0 -60
  50. data/lib/tap/generator/generate.rb +0 -93
  51. data/lib/tap/generator/generators/command/command_generator.rb +0 -21
  52. data/lib/tap/generator/generators/command/templates/command.erb +0 -32
  53. data/lib/tap/generator/generators/config/config_generator.rb +0 -98
  54. data/lib/tap/generator/generators/generator/generator_generator.rb +0 -37
  55. data/lib/tap/generator/generators/generator/templates/task.erb +0 -27
  56. data/lib/tap/generator/generators/generator/templates/test.erb +0 -26
  57. data/lib/tap/generator/generators/root/root_generator.rb +0 -84
  58. data/lib/tap/generator/generators/root/templates/MIT-LICENSE +0 -22
  59. data/lib/tap/generator/generators/root/templates/README +0 -14
  60. data/lib/tap/generator/generators/root/templates/Rakefile +0 -84
  61. data/lib/tap/generator/generators/root/templates/Rapfile +0 -11
  62. data/lib/tap/generator/generators/root/templates/gemspec +0 -27
  63. data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +0 -3
  64. data/lib/tap/generator/generators/task/task_generator.rb +0 -25
  65. data/lib/tap/generator/generators/task/templates/task.erb +0 -14
  66. data/lib/tap/generator/generators/task/templates/test.erb +0 -19
  67. data/lib/tap/generator/manifest.rb +0 -20
  68. data/lib/tap/generator/preview.rb +0 -69
  69. data/lib/tap/load.rb +0 -64
  70. data/lib/tap/spec.rb +0 -41
  71. data/lib/tap/support/aggregator.rb +0 -65
  72. data/lib/tap/support/audit.rb +0 -333
  73. data/lib/tap/support/constant.rb +0 -143
  74. data/lib/tap/support/constant_manifest.rb +0 -126
  75. data/lib/tap/support/dependencies.rb +0 -54
  76. data/lib/tap/support/dependency.rb +0 -44
  77. data/lib/tap/support/executable.rb +0 -198
  78. data/lib/tap/support/executable_queue.rb +0 -125
  79. data/lib/tap/support/gems.rb +0 -43
  80. data/lib/tap/support/join.rb +0 -144
  81. data/lib/tap/support/joins.rb +0 -12
  82. data/lib/tap/support/joins/switch.rb +0 -27
  83. data/lib/tap/support/joins/sync_merge.rb +0 -38
  84. data/lib/tap/support/manifest.rb +0 -171
  85. data/lib/tap/support/minimap.rb +0 -90
  86. data/lib/tap/support/node.rb +0 -176
  87. data/lib/tap/support/parser.rb +0 -450
  88. data/lib/tap/support/schema.rb +0 -385
  89. data/lib/tap/support/shell_utils.rb +0 -67
  90. data/lib/tap/test.rb +0 -77
  91. data/lib/tap/test/assertions.rb +0 -38
  92. data/lib/tap/test/env_vars.rb +0 -29
  93. data/lib/tap/test/extensions.rb +0 -73
  94. data/lib/tap/test/file_test.rb +0 -362
  95. data/lib/tap/test/file_test_class.rb +0 -15
  96. data/lib/tap/test/regexp_escape.rb +0 -87
  97. data/lib/tap/test/script_test.rb +0 -46
  98. data/lib/tap/test/script_tester.rb +0 -115
  99. data/lib/tap/test/subset_test.rb +0 -260
  100. data/lib/tap/test/subset_test_class.rb +0 -99
  101. data/lib/tap/test/tap_test.rb +0 -109
  102. data/lib/tap/test/utils.rb +0 -231
@@ -1,29 +1,42 @@
1
1
  module Tap
2
2
  module Support
3
3
 
4
- # Generates an Intern module for the specified method_name.
4
+ # Generates an Intern module to override the specified method_name. Intern
5
+ # modules are useful to override a tiny bit of functionality without having
6
+ # to generate a full subclass.
7
+ #
5
8
  # An Intern module:
9
+ #
6
10
  # - adds an accessor for <method_name>_block
7
- # - overrides <method_name> to call the block
11
+ # - overrides <method_name> to call the block, prepending self to
12
+ # the input arguments
13
+ #
14
+ # For example:
15
+ #
16
+ # array = [1,2,3].extend Intern(:last)
17
+ #
18
+ # array.last # => 3
19
+ # array.last_block = lambda {|arr| arr.first }
20
+ # array.last # => 3
8
21
  #
9
22
  def self.Intern(method_name)
10
23
  mod = INTERN_MODULES[method_name.to_sym]
11
24
  return mod unless mod == nil
12
-
25
+
13
26
  mod = INTERN_MODULES[method_name.to_sym] = Module.new
14
27
  mod.module_eval %Q{
15
28
  attr_accessor :#{method_name}_block
16
29
 
17
30
  def #{method_name}(*inputs)
18
- raise "no #{method_name} block set" unless #{method_name}_block
31
+ return super unless #{method_name}_block
19
32
  inputs.unshift(self)
20
-
33
+
21
34
  arity = #{method_name}_block.arity
22
35
  n = inputs.length
23
36
  unless n == arity || (arity < 0 && (-1-n) <= arity)
24
37
  raise ArgumentError.new("wrong number of arguments (\#{n} for \#{arity})")
25
38
  end
26
-
39
+
27
40
  #{method_name}_block.call(*inputs)
28
41
  end
29
42
  }
@@ -121,8 +121,10 @@ module Tap
121
121
  end
122
122
 
123
123
  class << self
124
- def build(template, attributes={})
125
- new(template, attributes).build
124
+
125
+ # Builds the erb template with the specified attributes.
126
+ def build(template, attributes={}, filename=nil)
127
+ new(template, attributes, filename).build
126
128
  end
127
129
  end
128
130
 
@@ -163,6 +165,8 @@ module Tap
163
165
  end
164
166
 
165
167
  unless RUBY_VERSION < "1.9"
168
+ #-- TODO
169
+ # check if this is still needed...
166
170
  def force_encoding(encoding)
167
171
  @_erbout.force_encoding(encoding)
168
172
  @_erbout
@@ -188,11 +192,12 @@ module Tap
188
192
 
189
193
  # Build the template. All methods of self will be
190
194
  # accessible in the template.
191
- def build(attrs={})
195
+ def build(attrs={}, filename=nil)
192
196
  attrs.each_pair do |key, value|
193
197
  send("#{key}=", value)
194
198
  end
195
199
 
200
+ @template.filename = filename
196
201
  @template.result(binding)
197
202
  @_erbout
198
203
  end
data/lib/tap/task.rb CHANGED
@@ -1,13 +1,22 @@
1
- autoload(:ConfigParser, 'config_parser')
1
+ require 'tap/joins'
2
+ require 'tap/root'
3
+ require 'tap/env/string_ext'
2
4
 
3
5
  module Tap
4
- autoload(:FileTask, 'tap/file_task')
5
-
6
6
  module Support
7
7
  autoload(:Templater, 'tap/support/templater')
8
- autoload(:Intern, 'tap/support/intern')
9
8
  end
10
9
 
10
+ class App
11
+ # Generates a task initialized to self.
12
+ def task(config={}, klass=Task, &block)
13
+ klass.intern(config, self, &block)
14
+ end
15
+ end
16
+
17
+ # Tasks are nodes that map to the command line. Tasks provide support for
18
+ # configuration, documentation, and provide helpers to build workflows.
19
+ #
11
20
  # === Task Definition
12
21
  #
13
22
  # Tasks specify executable code by overridding the process method in
@@ -31,17 +40,17 @@ module Tap
31
40
  # MixedInputs.new.execute(:a, :b) # => [:a, :b, []]
32
41
  # MixedInputs.new.execute(:a, :b, 1, 2, 3) # => [:a, :b, [1,2,3]]
33
42
  #
34
- # Tasks may be create with new, or with intern. Intern overrides process
35
- # using a block that receives the task instance and the inputs.
43
+ # Tasks may be created with new, or with intern. Intern overrides process
44
+ # using a block that receives the task and the inputs.
36
45
  #
37
46
  # no_inputs = Task.intern {|task| [] }
38
47
  # one_input = Task.intern {|task, input| [input] }
39
48
  # mixed_inputs = Task.intern {|task, a, b, *args| [a, b, args] }
40
49
  #
41
- # no_inputs.execute # => []
42
- # one_input.execute(:a) # => [:a]
43
- # mixed_inputs.execute(:a, :b) # => [:a, :b, []]
44
- # mixed_inputs.execute(:a, :b, 1, 2, 3) # => [:a, :b, [1,2,3]]
50
+ # no_inputs.execute # => []
51
+ # one_input.execute(:a) # => [:a]
52
+ # mixed_inputs.execute(:a, :b) # => [:a, :b, []]
53
+ # mixed_inputs.execute(:a, :b, 1, 2, 3) # => [:a, :b, [1,2,3]]
45
54
  #
46
55
  # === Configuration
47
56
  #
@@ -80,16 +89,17 @@ module Tap
80
89
  # end
81
90
  #
82
91
  # t = ValidatingTask.new
83
- # t.string = 1 # !> ValidationError
84
- # t.integer = 1.1 # !> ValidationError
92
+ # t.string = 1 # !> ValidationError
93
+ # t.integer = 1.1 # !> ValidationError
85
94
  #
86
95
  # t.integer = "1"
87
- # t.integer == 1 # => true
96
+ # t.integer == 1 # => true
88
97
  #
89
98
  # See the {Configurable}[http://tap.rubyforge.org/configurable/]
90
99
  # documentation for more information.
91
100
  #
92
101
  # === Subclassing
102
+ #
93
103
  # Tasks may be subclassed normally, but be sure to call super as necessary,
94
104
  # in particular when overriding the following methods:
95
105
  #
@@ -110,29 +120,16 @@ module Tap
110
120
  # end
111
121
  #
112
122
  class Task
123
+ include App::Node
113
124
  include Configurable
114
- include Support::Executable
115
125
 
116
126
  class << self
117
127
  # Returns class dependencies
118
128
  attr_reader :dependencies
119
129
 
120
- # Sets the class default_name
121
- attr_writer :default_name
122
-
123
- # Returns the default name for the class: to_s.underscore
124
- def default_name
125
- # lazy-setting default_name like this (rather than
126
- # within inherited, for example) is an optimization
127
- # since many subclass operations end up setting
128
- # default_name themselves.
129
- @default_name ||= to_s.underscore
130
- end
131
-
132
- # Returns an instance of self; the instance is a kind of 'global'
133
- # instance used in class-level dependencies. See depends_on.
134
- def instance
135
- @instance ||= new.extend(Support::Dependency)
130
+ # Returns or initializes the instance of self cached with app.
131
+ def instance(app=Tap::App.instance, auto_initialize=true)
132
+ app.cache[self] ||= (auto_initialize ? new({}, app) : nil)
136
133
  end
137
134
 
138
135
  def inherited(child) # :nodoc:
@@ -146,26 +143,28 @@ module Tap
146
143
  end
147
144
 
148
145
  # Instantiates a new task with the input arguments and overrides
149
- # process with the block. The block will be called with the
146
+ # process with the block. The block will be called with the task
150
147
  # instance, plus any inputs.
151
148
  #
152
149
  # Simply instantiates a new task if no block is given.
153
- def intern(*args, &block) # :yields: task, inputs...
154
- instance = new(*args)
150
+ def intern(config={}, app=Tap::App.instance, &block) # :yields: task, inputs...
151
+ instance = new(config, app)
155
152
  if block_given?
156
- instance.extend Support::Intern
153
+ instance.extend Support::Intern(:process)
157
154
  instance.process_block = block
158
155
  end
159
156
  instance
160
157
  end
161
158
 
162
- # Parses the argv into an instance of self and an array of arguments
163
- # (implicitly to be enqued to the instance).
159
+ # Parses the argv into an instance of self and an array of arguments
160
+ # (implicitly to be enqued to the instance). By default parse
161
+ # parses an argh then calls instantiate, but there is no requirement
162
+ # that this occurs in subclasses.
164
163
  def parse(argv=ARGV, app=Tap::App.instance)
165
164
  parse!(argv.dup, app)
166
165
  end
167
166
 
168
- # Same as parse, but removes switches destructively.
167
+ # Same as parse, but removes arguments destructively.
169
168
  def parse!(argv=ARGV, app=Tap::App.instance)
170
169
  opts = ConfigParser.new
171
170
 
@@ -189,51 +188,44 @@ module Tap
189
188
  puts opts
190
189
  exit
191
190
  end
192
-
193
- # add option to specify the task name
194
- name = default_name
195
- opts.on('--name NAME', 'Specifies the task name') do |value|
196
- name = value
197
- end
198
191
 
199
192
  # add option to specify a config file
200
- config_path = nil
201
- opts.on('--config FILE', 'Specifies a config file') do |value|
202
- config_path = value
203
- end
204
-
205
- # add option to load args to ARGV
206
- use_args = []
207
- opts.on('--use FILE', 'Loads inputs to ARGV') do |path|
208
- use(path, use_args)
193
+ opts.on('--config FILE', 'Specifies a config file') do |config_file|
194
+ opts.config.merge!(load_config(config_file))
209
195
  end
210
196
 
211
- # parse!
212
- argv = opts.parse!(argv, {}, false)
213
-
214
- # load configurations
215
- config = load_config(config_path)
216
-
217
- # build and reconfigure the instance
218
- instance = new({}, name, app).reconfigure(config).reconfigure(opts.nested_config)
197
+ # (note defaults are not added so they will not
198
+ # conflict with string keys from a config file)
199
+ argv = opts.parse!(argv, :add_defaults => false)
200
+ argh = {
201
+ :config => opts.nested_config,
202
+ :args => argv
203
+ }
219
204
 
220
- [instance, (argv + use_args)]
205
+ instantiate(argh, app)
221
206
  end
222
207
 
223
- # A convenience method to parse the argv and execute the instance
224
- # with the remaining arguments. If 'help' is specified in the argv,
225
- # execute prints the help and exits.
226
- #
227
- # Returns the non-audited result.
228
- def execute(argv=ARGV)
229
- instance, args = parse(ARGV)
230
- instance.execute(*args)
208
+ # Instantiates an instance of self and returns an instance of self and
209
+ # an array of arguments (implicitly to be enqued to the instance).
210
+ def instantiate(argh={}, app=Tap::App.instance)
211
+ config = argh[:config] || {}
212
+ instance = new(config, app)
213
+
214
+ if argh[:cache]
215
+ if app.cache.has_key?(self) && app.cache[self] != instance
216
+ raise "cache already has an instance for: #{self}"
217
+ end
218
+
219
+ app.cache[self] = instance
220
+ end
221
+
222
+ [instance, argh[:args]]
231
223
  end
232
224
 
233
- DEFAULT_HELP_TEMPLATE = %Q{<% manifest = task_class.manifest %>
234
- <%= task_class %><%= manifest.empty? ? '' : ' -- ' %><%= manifest.to_s %>
225
+ DEFAULT_HELP_TEMPLATE = %Q{<% desc = task_class::desc %>
226
+ <%= task_class %><%= desc.empty? ? '' : ' -- ' %><%= desc.to_s %>
235
227
 
236
- <% desc = manifest.kind_of?(Lazydoc::Comment) ? manifest.wrap(77, 2, nil) : [] %>
228
+ <% desc = desc.kind_of?(Lazydoc::Comment) ? desc.wrap(77, 2, nil) : [] %>
237
229
  <% unless desc.empty? %>
238
230
  <%= '-' * 80 %>
239
231
 
@@ -253,51 +245,34 @@ module Tap
253
245
  # Recursively loads path into a nested configuration file.
254
246
  def load_config(path)
255
247
  # optimization to check for trivial paths
256
- return {} if Root.trivial?(path)
248
+ return {} if Root::Utils.trivial?(path)
257
249
 
258
250
  Configurable::Utils.load_file(path, true) do |base, key, value|
259
251
  base[key] ||= value if base.kind_of?(Hash)
260
252
  end
261
253
  end
262
254
 
263
- # Loads the contents of path onto argv.
264
- def use(path, argv=ARGV)
265
- obj = Root.trivial?(path) ? [] : (YAML.load_file(path) || [])
266
-
267
- case obj
268
- when Array then argv.concat(obj)
269
- else argv << obj
270
- end
271
-
272
- argv
273
- end
274
-
275
255
  protected
276
256
 
277
257
  # Sets a class-level dependency; when task class B depends_on another
278
- # task class A, instances of B are initialized to depend on A.instance.
258
+ # task class A, instances of B are initialized to depend on a shared
259
+ # instance of A. The shared instance is specific to an app and can
260
+ # be accessed through instance(app).
261
+ #
279
262
  # If a non-nil name is specified, depends_on will create a reader of
280
- # the resolved dependency value.
263
+ # the dependency instance.
281
264
  #
282
265
  # class A < Tap::Task
283
- # def process
284
- # "result"
285
- # end
286
266
  # end
287
267
  #
288
268
  # class B < Tap::Task
289
269
  # depends_on :a, A
290
270
  # end
291
271
  #
292
- # b = B.new
293
- # b.dependencies # => [A.instance]
294
- # b.a # => "result"
295
- #
296
- # A.instance.resolved? # => true
297
- #
298
- # Normally class-level dependencies are not added to existing instances
299
- # but, as a special case, depends_on updates instance to depend on
300
- # dependency_class.instance.
272
+ # app = Tap::App.new
273
+ # b = B.new({}, app)
274
+ # b.dependencies # => [A.instance(app)]
275
+ # b.a # => A.instance(app)
301
276
  #
302
277
  # Returns self.
303
278
  def depends_on(name, dependency_class)
@@ -305,15 +280,10 @@ module Tap
305
280
  dependencies << dependency_class
306
281
  end
307
282
 
308
- # update instance with the dependency if necessary
309
- if instance_variable_defined?(:@instance)
310
- instance.depends_on(dependency_class.instance)
311
- end
312
-
313
283
  if name
314
284
  # returns the resolved result of the dependency
315
285
  define_method(name) do
316
- dependency_class.instance.resolve.value
286
+ dependency_class.instance(app)
317
287
  end
318
288
 
319
289
  public(name)
@@ -322,14 +292,13 @@ module Tap
322
292
  self
323
293
  end
324
294
 
325
- # Defines a task subclass with the specified configurations and process block.
326
- # During initialization the subclass is instantiated and made accessible
327
- # through a reader by the specified name.
295
+ # Defines a task subclass with the specified configurations and process
296
+ # block. During initialization the subclass is instantiated and made
297
+ # accessible through the name method.
328
298
  #
329
- # Defined tasks may be configured during initialization, through config, or
330
- # directly through the instance; in effect you get tasks with nested configs
331
- # which greatly facilitates workflows. Indeed, defined tasks are often
332
- # joined in the workflow method.
299
+ # Defined tasks may be configured during through config, or directly
300
+ # through the instance; in effect you get tasks with nested configs which
301
+ # can greatly facilitate workflows.
333
302
  #
334
303
  # class AddALetter < Tap::Task
335
304
  # config :letter, 'a'
@@ -341,7 +310,8 @@ module Tap
341
310
  # define :b, AddALetter, {:letter => 'b'}
342
311
  # define :c, AddALetter, {:letter => 'c'}
343
312
  #
344
- # def workflow
313
+ # def initialize(*args)
314
+ # super
345
315
  # a.sequence(b, c)
346
316
  # end
347
317
  #
@@ -394,8 +364,6 @@ module Tap
394
364
  def define(name, baseclass=Tap::Task, configs={}, options={}, &block)
395
365
  # define the subclass
396
366
  subclass = Class.new(baseclass)
397
- subclass.default_name = name.to_s
398
-
399
367
  configs.each_pair do |key, value|
400
368
  subclass.send(:config, key, value)
401
369
  end
@@ -416,25 +384,34 @@ module Tap
416
384
  end
417
385
 
418
386
  instance_variable_set(:@source_file, __FILE__)
419
- instance_variable_set(:@default_name, 'tap/task')
420
387
  instance_variable_set(:@dependencies, [])
421
388
 
422
- lazy_attr :manifest
389
+ lazy_attr :desc, 'task'
423
390
  lazy_attr :args, :process
424
391
  lazy_register :process, Lazydoc::Arguments
425
392
 
426
- # The name of self
427
- #--
428
- # Currently names may be any object. Audit makes use of name
429
- # via to_s, as does app when figuring configuration filepaths.
430
- attr_accessor :name
393
+ ###############################################################
394
+ # [depreciated] manifest will be removed at 1.0
395
+ lazy_attr :manifest
396
+ def self.desc(resolve=true)
397
+ comment = const_attrs['task'] ||= self.manifest
398
+ resolve && comment.kind_of?(Lazydoc::Comment) ? comment.resolve : comment
399
+ end
400
+ def self.manifest
401
+ # :::-
402
+ #"warn manifest is depreciated, use ::task instead"
403
+ # :::+
404
+ const_attrs['manifest'] ||= Lazydoc::Subject.new(nil, lazydoc)
405
+ end
406
+ ###############################################################
407
+
408
+ # The App receiving self during enq
409
+ attr_reader :app
431
410
 
432
411
  # Initializes a new Task.
433
- def initialize(config={}, name=nil, app=App.instance)
434
- @name = name || self.class.default_name
412
+ def initialize(config={}, app=Tap::App.instance)
435
413
  @app = app
436
- @method_name = :execute_with_callbacks
437
- @on_complete_block = nil
414
+ @joins = []
438
415
  @dependencies = []
439
416
 
440
417
  # initialize configs
@@ -442,10 +419,20 @@ module Tap
442
419
 
443
420
  # setup class dependencies
444
421
  self.class.dependencies.each do |dependency_class|
445
- depends_on(dependency_class.instance)
422
+ depends_on dependency_class.instance(app)
446
423
  end
447
-
448
- workflow
424
+ end
425
+
426
+ # Auditing method call. Resolves dependencies, executes method_name,
427
+ # and sends the audited result to the on_complete_block (if set).
428
+ #
429
+ # Returns the audited result.
430
+ def execute(*inputs)
431
+ app.dispatch(self, inputs)
432
+ end
433
+
434
+ def call(*inputs)
435
+ process(*inputs)
449
436
  end
450
437
 
451
438
  # The method for processing inputs into outputs. Override this method in
@@ -459,65 +446,82 @@ module Tap
459
446
  # end
460
447
  # end
461
448
  #
462
- # t = TaskWithTwoInputs.new
449
+ # results = []
450
+ # app = Tap::App.new {|result| results << result }
451
+ #
452
+ # t = TaskWithTwoInputs.new({}, app)
463
453
  # t.enq(1,2).enq(3,4)
464
- # t.app.run
465
- # t.app.results(t) # => [[2,1], [4,3]]
454
+ #
455
+ # app.run
456
+ # results # => [[2,1], [4,3]]
466
457
  #
467
458
  # By default, process simply returns the inputs.
468
459
  def process(*inputs)
469
460
  inputs
470
461
  end
471
462
 
472
- # Logs the inputs to the application logger (via app.log)
473
- def log(action, msg="", level=Logger::INFO)
474
- # TODO - add a task identifier?
475
- app.log(action, msg, level)
463
+ # Enqueues self to app with the inputs. The number of inputs provided
464
+ # should match the number of inputs for the method_name method.
465
+ def enq(*inputs)
466
+ app.queue.enq(self, inputs)
467
+ self
476
468
  end
477
469
 
478
- # Returns self.name
479
- def to_s
480
- name.to_s
470
+ # Sets a sequence workflow pattern for the tasks; each task
471
+ # enques the next task with it's results, starting with self.
472
+ def sequence(*tasks)
473
+ options = tasks[-1].kind_of?(Hash) ? tasks.pop : {}
474
+
475
+ current_task = self
476
+ tasks.each do |next_task|
477
+ Join.new(options, app).join([current_task], [next_task])
478
+ current_task = next_task
479
+ end
481
480
  end
482
-
483
- # Provides an abbreviated version of the default inspect, with only
484
- # the task class, object_id, name, and configurations listed.
485
- def inspect
486
- "#<#{self.class.to_s}:#{object_id} #{name} #{config.to_hash.inspect} >"
481
+
482
+ # Sets a fork workflow pattern for self; each target will enque the
483
+ # results of self.
484
+ def fork(*targets)
485
+ options = targets[-1].kind_of?(Hash) ? targets.pop : {}
486
+ Join.new(options, app).join([self], targets)
487
487
  end
488
-
489
- protected
490
488
 
491
- # Hook to define a workflow for defined tasks.
492
- def workflow
489
+ # Sets a simple merge workflow pattern for the source tasks. Each
490
+ # source enques self with it's result; no synchronization occurs,
491
+ # nor are results grouped before being enqued.
492
+ def merge(*sources)
493
+ options = sources[-1].kind_of?(Hash) ? sources.pop : {}
494
+ Join.new(options, app).join(sources, [self])
493
495
  end
494
-
495
- # Hook to execute code before inputs are processed.
496
- def before_execute() end
497
-
498
- # Hook to execute code after inputs are processed.
499
- def after_execute() end
500
496
 
501
- # Hook to handle unhandled errors from processing inputs on a task level.
502
- # By default on_execute_error simply re-raises the unhandled error.
503
- def on_execute_error(err)
504
- raise err
497
+ # Sets a synchronized merge workflow for the source tasks. Results
498
+ # from each source are collected and enqued as a single group to
499
+ # self. The collective results are not enqued until all sources
500
+ # have completed. See Joins::Sync.
501
+ def sync_merge(*sources)
502
+ options = sources[-1].kind_of?(Hash) ? sources.pop : {}
503
+ Joins::Sync.new(options, app).join(sources, [self])
504
+ end
505
+
506
+ # Sets a switch workflow pattern for self. On complete, switch yields
507
+ # the result to the block and the block should return the index of the
508
+ # target to enque with the results. No target will be enqued if the
509
+ # index is false or nil. An error is raised if no target can be found
510
+ # for the specified index. See Joins::Switch.
511
+ def switch(*targets, &block) # :yields: result
512
+ options = targets[-1].kind_of?(Hash) ? targets.pop : {}
513
+ Joins::Switch.new(options, app).join([self], targets, &block)
505
514
  end
506
515
 
507
- private
508
-
509
- # execute_with_callbacks is the method called by _execute
510
- def execute_with_callbacks(*inputs) # :nodoc:
511
- before_execute
512
- begin
513
- result = process(*inputs)
514
- rescue
515
- on_execute_error($!)
516
- end
517
- after_execute
518
-
519
- result
516
+ # Logs the inputs to the application logger (via app.log)
517
+ def log(action, msg="", level=Logger::INFO)
518
+ app.log(action, msg, level)
520
519
  end
521
520
 
521
+ # Provides an abbreviated version of the default inspect, with only
522
+ # the task class, object_id, and configurations listed.
523
+ def inspect
524
+ "#<#{self.class.to_s}:#{object_id} #{config.to_hash.inspect} >"
525
+ end
522
526
  end
523
527
  end