Class: Tap::Task
- Includes:
- App::Node
- Defined in:
- lib/tap/task.rb
Overview
Tasks are nodes that map to the command line. Tasks provide support for configuration, documentation, and provide helpers to build workflows.
Task Definition
Tasks specify executable code by overridding the process method in subclasses. The number of inputs to process corresponds to the inputs given to execute or enq.
class NoInput < Tap::Task
def process(); []; end
end
class OneInput < Tap::Task
def process(input); [input]; end
end
class MixedInputs < Tap::Task
def process(a, b, *args); [a,b,args]; end
end
NoInput.new.execute # => []
OneInput.new.execute(:a) # => [:a]
MixedInputs.new.execute(:a, :b) # => [:a, :b, []]
MixedInputs.new.execute(:a, :b, 1, 2, 3) # => [:a, :b, [1,2,3]]
Configuration
Tasks are configurable. By default each task will be configured as specified in the class definition. Configurations may be accessed through config, or through accessors.
class ConfiguredTask < Tap::Task
config :one, 'one'
config :two, 'two'
end
t = ConfiguredTask.new
t.config # => {:one => 'one', :two => 'two'}
t.one # => 'one'
t.one = 'ONE'
t.config # => {:one => 'ONE', :two => 'two'}
Overrides and even unspecified configurations may be provided during initialization. Unspecified configurations do not have accessors.
t = ConfiguredTask.new(:one => 'ONE', :three => 'three')
t.config # => {:one => 'ONE', :two => 'two', :three => 'three'}
t.respond_to?(:three) # => false
Configurations can be validated/transformed using an optional block.
Many common blocks are pre-packaged and may be accessed through the class method ‘c’:
class ValidatingTask < Tap::Task
# string config validated to be a string
config :string, 'str', &c.check(String)
# integer config; string inputs are converted using YAML
config :integer, 1, &c.yaml(Integer)
end
t = ValidatingTask.new
t.string = 1 # !> ValidationError
t.integer = 1.1 # !> ValidationError
t.integer = "1"
t.integer == 1 # => true
See the Configurable documentation for more information.
Subclassing
Tasks may be subclassed normally, but be sure to call super as necessary, in particular when overriding the following methods:
class Subclass < Tap::Task
class << self
def inherited(child)
super
end
end
def initialize(*args)
super
end
def initialize_copy(orig)
super
end
end
Direct Known Subclasses
Instance Attribute Summary
Attributes included from App::Node
Attributes inherited from App::Api
Class Method Summary collapse
-
.load_config(path) ⇒ Object
Recursively loads path into a nested configuration file.
-
.parse!(argv = ARGV, app = Tap::App.instance) ⇒ Object
Same as parse, but removes arguments destructively.
- .parser ⇒ Object
Instance Method Summary collapse
- #associations ⇒ Object
- #call(*inputs) ⇒ Object
-
#enq(*inputs) ⇒ Object
Enqueues self to app with the inputs.
-
#execute(*inputs) ⇒ Object
Auditing method call.
-
#fork(*targets) ⇒ Object
Sets a fork workflow pattern for self; each target will enque the results of self.
-
#initialize(config = {}, app = Tap::App.instance) ⇒ Task
constructor
Initializes a new Task.
-
#inspect ⇒ Object
Provides an abbreviated version of the default inspect, with only the task class, object_id, and configurations listed.
-
#log(action, msg = nil, level = Logger::INFO) ⇒ Object
Logs the inputs to the application logger (via app.log).
-
#merge(*sources) ⇒ Object
Sets a simple merge workflow pattern for the source tasks.
-
#process(*inputs) ⇒ Object
The method for processing inputs into outputs.
-
#sequence(*tasks) ⇒ Object
Sets a sequence workflow pattern for the tasks; each task enques the next task with it’s results, starting with self.
-
#switch(*targets, &block) ⇒ Object
Sets a switch workflow pattern for self.
-
#sync_merge(*sources) ⇒ Object
Sets a synchronized merge workflow for the source tasks.
Methods included from App::Node
extended, intern, #on_complete
Methods inherited from App::Api
build, help, inherited, parse, #to_spec
Methods included from Signals
Methods included from Signals::ModuleMethods
Constructor Details
#initialize(config = {}, app = Tap::App.instance) ⇒ Task
Initializes a new Task.
284 285 286 287 |
# File 'lib/tap/task.rb', line 284 def initialize(config={}, app=Tap::App.instance) @joins = [] super end |
Class Method Details
.load_config(path) ⇒ Object
Recursively loads path into a nested configuration file.
176 177 178 179 180 181 182 183 |
# File 'lib/tap/task.rb', line 176 def load_config(path) # :nodoc: # optimization to check for trivial paths return {} if Root::Utils.trivial?(path) Configurable::Utils.load_file(path, true) do |base, key, value| base[key] ||= value if base.kind_of?(Hash) end end |
.parse!(argv = ARGV, app = Tap::App.instance) ⇒ Object
Same as parse, but removes arguments destructively.
162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/tap/task.rb', line 162 def parse!(argv=ARGV, app=Tap::App.instance) parser = self.parser # (note defaults are not added so they will not # conflict with string keys from a config file) argv = parser.parse!(argv, :add_defaults => false) enque = parser.config.delete('enque') instance = build({'config' => parser.nested_config}, app) instance.enq(*argv) if enque [instance, argv] end |
.parser ⇒ Object
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/tap/task.rb', line 128 def parser opts = super # add option to print help opts.on!("--help", "Print this help") do lines = desc.kind_of?(Lazydoc::Comment) ? desc.wrap(77, 2, nil) : [] lines.collect! {|line| " #{line}"} unless lines.empty? line = '-' * 80 lines.unshift(line) lines.push(line) end puts "#{self}#{desc.empty? ? '' : ' -- '}#{desc.to_s}" puts help puts "usage: tap run -- #{to_s.underscore} #{args}" puts puts opts exit end opts.on('--enque', 'Manually enques self') do opts['enque'] = true end # add option to specify a config file opts.on('--config FILE', 'Specifies a config file') do |config_file| opts.config.merge!(load_config(config_file)) end opts end |
Instance Method Details
#associations ⇒ Object
289 290 291 |
# File 'lib/tap/task.rb', line 289 def associations [nil, joins] end |
#call(*inputs) ⇒ Object
301 302 303 |
# File 'lib/tap/task.rb', line 301 def call(*inputs) process(*inputs) end |
#enq(*inputs) ⇒ Object
Enqueues self to app with the inputs. The number of inputs provided should match the number of inputs for the method_name method.
332 333 334 335 |
# File 'lib/tap/task.rb', line 332 def enq(*inputs) app.queue.enq(self, inputs) self end |
#execute(*inputs) ⇒ Object
Auditing method call. Resolves dependencies, executes method_name, and sends the audited result to the on_complete_block (if set).
Returns the audited result.
297 298 299 |
# File 'lib/tap/task.rb', line 297 def execute(*inputs) app.dispatch(self, inputs) end |
#fork(*targets) ⇒ Object
Sets a fork workflow pattern for self; each target will enque the results of self.
351 352 353 354 |
# File 'lib/tap/task.rb', line 351 def fork(*targets) = targets[-1].kind_of?(Hash) ? targets.pop : {} Join.new(, app).join([self], targets) end |
#inspect ⇒ Object
Provides an abbreviated version of the default inspect, with only the task class, object_id, and configurations listed.
390 391 392 |
# File 'lib/tap/task.rb', line 390 def inspect "#<#{self.class.to_s}:#{object_id} #{config.to_hash.inspect} >" end |
#log(action, msg = nil, level = Logger::INFO) ⇒ Object
Logs the inputs to the application logger (via app.log)
384 385 386 |
# File 'lib/tap/task.rb', line 384 def log(action, msg=nil, level=Logger::INFO) app.log(action, msg, level) { yield } end |
#merge(*sources) ⇒ Object
Sets a simple merge workflow pattern for the source tasks. Each source enques self with it’s result; no synchronization occurs, nor are results grouped before being enqued.
359 360 361 362 |
# File 'lib/tap/task.rb', line 359 def merge(*sources) = sources[-1].kind_of?(Hash) ? sources.pop : {} Join.new(, app).join(sources, [self]) end |
#process(*inputs) ⇒ Object
The method for processing inputs into outputs. Override this method in subclasses to provide class-specific process logic. The number of arguments specified by process corresponds to the number of arguments the task should have when enqued or executed.
class TaskWithTwoInputs < Tap::Task
def process(a, b)
[b,a]
end
end
results = []
app = Tap::App.new {|result| results << result }
t = TaskWithTwoInputs.new({}, app)
t.enq(1,2).enq(3,4)
app.run
results # => [[2,1], [4,3]]
By default, process simply returns the inputs.
326 327 328 |
# File 'lib/tap/task.rb', line 326 def process(*inputs) inputs end |
#sequence(*tasks) ⇒ Object
Sets a sequence workflow pattern for the tasks; each task enques the next task with it’s results, starting with self.
339 340 341 342 343 344 345 346 347 |
# File 'lib/tap/task.rb', line 339 def sequence(*tasks) = tasks[-1].kind_of?(Hash) ? tasks.pop : {} current_task = self tasks.each do |next_task| Join.new(, app).join([current_task], [next_task]) current_task = next_task end end |
#switch(*targets, &block) ⇒ Object
Sets a switch workflow pattern for self. On complete, switch yields the result to the block and the block should return the index of the target to enque with the results. No target will be enqued if the index is false or nil. An error is raised if no target can be found for the specified index. See Joins::Switch.
378 379 380 381 |
# File 'lib/tap/task.rb', line 378 def switch(*targets, &block) # :yields: result = targets[-1].kind_of?(Hash) ? targets.pop : {} Joins::Switch.new(, app).join([self], targets, &block) end |
#sync_merge(*sources) ⇒ Object
Sets a synchronized merge workflow for the source tasks. Results from each source are collected and enqued as a single group to self. The collective results are not enqued until all sources have completed. See Joins::Sync.
368 369 370 371 |
# File 'lib/tap/task.rb', line 368 def sync_merge(*sources) = sources[-1].kind_of?(Hash) ? sources.pop : {} Joins::Sync.new(, app).join(sources, [self]) end |