Class: Tap::Task
- Inherits:
-
Object
- Object
- Tap::Task
- Includes:
- Configurable, 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]]
Tasks may be created with new, or with intern. Intern overrides process using a block that receives the task and the inputs.
no_inputs = Task.intern {|task| [] }
one_input = Task.intern {|task, input| [input] }
mixed_inputs = Task.intern {|task, a, b, *args| [a, b, args] }
no_inputs.execute # => []
one_input.execute(:a) # => [:a]
mixed_inputs.execute(:a, :b) # => [:a, :b, []]
mixed_inputs.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
Constant Summary collapse
- DEFAULT_HELP_TEMPLATE =
%Q{<% desc = task_class::desc %> <%= task_class %><%= desc.empty? ? '' : ' -- ' %><%= desc.to_s %> <% desc = desc.kind_of?(Lazydoc::Comment) ? desc.wrap(77, 2, nil) : [] %> <% unless desc.empty? %> <%= '-' * 80 %> <% desc.each do |line| %> <%= line %> <% end %> <%= '-' * 80 %> <% end %> }
Class Attribute Summary collapse
-
.dependencies ⇒ Object
readonly
Returns class dependencies.
Instance Attribute Summary collapse
-
#app ⇒ Object
readonly
The App receiving self during enq.
Attributes included from App::Node
Class Method Summary collapse
- .desc(resolve = true) ⇒ Object
-
.help ⇒ Object
Returns the class help.
-
.inherited(child) ⇒ Object
:nodoc:.
-
.instance(app = Tap::App.instance, auto_initialize = true) ⇒ Object
Returns or initializes the instance of self cached with app.
-
.instantiate(argh = {}, app = Tap::App.instance) ⇒ Object
Instantiates an instance of self and returns an instance of self and an array of arguments (implicitly to be enqued to the instance).
-
.intern(config = {}, app = Tap::App.instance, &block) ⇒ Object
Instantiates a new task with the input arguments and overrides process with the block.
-
.load_config(path) ⇒ Object
Recursively loads path into a nested configuration file.
- .manifest ⇒ Object
-
.parse(argv = ARGV, app = Tap::App.instance) ⇒ Object
Parses the argv into an instance of self.
-
.parse!(argv = ARGV, app = Tap::App.instance) ⇒ Object
Same as parse, but removes arguments destructively.
Instance Method Summary collapse
- #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 = "", 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
#depends_on, extended, #on_complete
Constructor Details
#initialize(config = {}, app = Tap::App.instance) ⇒ Task
Initializes a new Task.
407 408 409 410 411 412 413 414 415 416 417 418 419 |
# File 'lib/tap/task.rb', line 407 def initialize(config={}, app=Tap::App.instance) @app = app @joins = [] @dependencies = [] # initialize configs initialize_config(config) # setup class dependencies self.class.dependencies.each do |dependency_class| depends_on dependency_class.instance(app) end end |
Class Attribute Details
.dependencies ⇒ Object (readonly)
Returns class dependencies
128 129 130 |
# File 'lib/tap/task.rb', line 128 def dependencies @dependencies end |
Instance Attribute Details
#app ⇒ Object (readonly)
The App receiving self during enq
404 405 406 |
# File 'lib/tap/task.rb', line 404 def app @app end |
Class Method Details
.desc(resolve = true) ⇒ Object
391 392 393 394 |
# File 'lib/tap/task.rb', line 391 def self.desc(resolve=true) comment = const_attrs['task'] ||= self.manifest resolve && comment.kind_of?(Lazydoc::Comment) ? comment.resolve : comment end |
.help ⇒ Object
Returns the class help.
236 237 238 |
# File 'lib/tap/task.rb', line 236 def help Tap::Support::Templater.new(DEFAULT_HELP_TEMPLATE, :task_class => self).build end |
.inherited(child) ⇒ Object
:nodoc:
135 136 137 138 139 140 141 142 143 |
# File 'lib/tap/task.rb', line 135 def inherited(child) # :nodoc: unless child.instance_variable_defined?(:@source_file) caller[0] =~ Lazydoc::CALLER_REGEXP child.instance_variable_set(:@source_file, File.($1)) end child.instance_variable_set(:@dependencies, dependencies.dup) super end |
.instance(app = Tap::App.instance, auto_initialize = true) ⇒ Object
Returns or initializes the instance of self cached with app.
131 132 133 |
# File 'lib/tap/task.rb', line 131 def instance(app=Tap::App.instance, auto_initialize=true) app.cache[self] ||= (auto_initialize ? new({}, app) : nil) end |
.instantiate(argh = {}, app = Tap::App.instance) ⇒ Object
Instantiates an instance of self and returns an instance of self and an array of arguments (implicitly to be enqued to the instance).
205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/tap/task.rb', line 205 def instantiate(argh={}, app=Tap::App.instance) config = argh[:config] || {} instance = new(config, app) if argh[:cache] if app.cache.has_key?(self) && app.cache[self] != instance raise "cache already has an instance for: #{self}" end app.cache[self] = instance end instance end |
.intern(config = {}, app = Tap::App.instance, &block) ⇒ Object
Instantiates a new task with the input arguments and overrides process with the block. The block will be called with the task instance, plus any inputs.
Simply instantiates a new task if no block is given.
150 151 152 153 154 155 156 157 |
# File 'lib/tap/task.rb', line 150 def intern(config={}, app=Tap::App.instance, &block) # :yields: task, inputs... instance = new(config, app) if block_given? instance.extend Support::Intern(:process) instance.process_block = block end instance end |
.load_config(path) ⇒ Object
Recursively loads path into a nested configuration file.
241 242 243 244 245 246 247 248 |
# File 'lib/tap/task.rb', line 241 def load_config(path) # 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 |
.manifest ⇒ Object
395 396 397 398 399 400 |
# File 'lib/tap/task.rb', line 395 def self.manifest # :::- #"warn manifest is depreciated, use ::task instead" # :::+ const_attrs['manifest'] ||= Lazydoc::Subject.new(nil, lazydoc) end |
.parse(argv = ARGV, app = Tap::App.instance) ⇒ Object
Parses the argv into an instance of self. By default parse parses an argh then calls instantiate, but there is no requirement that this occurs in subclasses.
162 163 164 |
# File 'lib/tap/task.rb', line 162 def parse(argv=ARGV, app=Tap::App.instance) parse!(argv.dup, app) end |
.parse!(argv = ARGV, app = Tap::App.instance) ⇒ Object
Same as parse, but removes arguments destructively.
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/tap/task.rb', line 167 def parse!(argv=ARGV, app=Tap::App.instance) opts = ConfigParser.new unless configurations.empty? opts.separator "configurations:" opts.add(configurations) opts.separator "" end opts.separator "options:" # add option to print help opts.on("--help", "Print this help") do prg = case $0 when /rap$/ then 'rap' else 'tap run --' end puts "#{help}usage: #{prg} #{to_s.underscore} #{args}" puts puts opts exit 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 # (note defaults are not added so they will not # conflict with string keys from a config file) argv = opts.parse!(argv, :add_defaults => false) instantiate({:config => opts.nested_config}, app) end |
Instance Method Details
#call(*inputs) ⇒ Object
429 430 431 |
# File 'lib/tap/task.rb', line 429 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.
460 461 462 463 |
# File 'lib/tap/task.rb', line 460 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.
425 426 427 |
# File 'lib/tap/task.rb', line 425 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.
479 480 481 482 |
# File 'lib/tap/task.rb', line 479 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.
518 519 520 |
# File 'lib/tap/task.rb', line 518 def inspect "#<#{self.class.to_s}:#{object_id} #{config.to_hash.inspect} >" end |
#log(action, msg = "", level = Logger::INFO) ⇒ Object
Logs the inputs to the application logger (via app.log)
512 513 514 |
# File 'lib/tap/task.rb', line 512 def log(action, msg="", level=Logger::INFO) app.log(action, msg, level) 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.
487 488 489 490 |
# File 'lib/tap/task.rb', line 487 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.
454 455 456 |
# File 'lib/tap/task.rb', line 454 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.
467 468 469 470 471 472 473 474 475 |
# File 'lib/tap/task.rb', line 467 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.
506 507 508 509 |
# File 'lib/tap/task.rb', line 506 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.
496 497 498 499 |
# File 'lib/tap/task.rb', line 496 def sync_merge(*sources) = sources[-1].kind_of?(Hash) ? sources.pop : {} Joins::Sync.new(, app).join(sources, [self]) end |