tap 0.12.4 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
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