lib/tap/task.rb in bahuvrihi-tap-0.10.8 vs lib/tap/task.rb in bahuvrihi-tap-0.11.0
- old
+ new
@@ -4,80 +4,74 @@
require 'tap/support/intern'
autoload(:OptionParser, 'optparse')
module Tap
- # Tasks are the basic organizational unit of Tap. Tasks provide
- # a standard backbone for creating the working parts of an application
- # by facilitating configuration, batched execution of methods, and
- # documentation.
- #
- # The functionality of Task is built from several base modules:
- # - Tap::Support::Batchable
- # - Tap::Support::Configurable
- # - Tap::Support::Executable
- #
- # Tap::Workflow is built on the same foundations; the sectons on
- # configuration and batching apply equally to Workflows as Tasks.
- #
# === Task Definition
#
- # Tasks are instantiated with a task block; when the task is run
- # the block gets called with the enqued inputs. As such, the block
- # should specify the same number of inputs as you enque (plus the
- # task itself, which is a standard input).
+ # 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.
#
- # no_inputs = Task.new {|task| }
- # one_input = Task.new {|task, input| }
- # mixed_inputs = Task.new {|task, a, b, *args| }
- #
- # no_inputs.enq
- # one_input.enq(:a)
- # mixed_inputs.enq(:a, :b)
- # mixed_inputs.enq(:a, :b, 1, 2, 3)
- #
- # Subclasses of Task specify executable code by overridding the process
- # method. In this case the number of enqued inputs should correspond to
- # process (passing the task would be redundant).
- #
# class NoInput < Tap::Task
- # def process() end
+ # def process(); []; end
# end
#
# class OneInput < Tap::Task
- # def process(input) end
+ # def process(input); [input]; end
# end
#
# class MixedInputs < Tap::Task
- # def process(a, b, *args) end
+ # def process(a, b, *args); [a,b,args]; end
# end
#
- # NoInput.new.enq
- # OneInput.new.enq(:a)
- # MixedInputs.new.enq(:a, :b)
- # MixedInputs.new.enq(:a, :b, 1, 2, 3)
+ # 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 create with new, or with intern. Intern overrides
+ # process with a custom block that gets called with the task instance
+ # 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
- # with the default class configurations, which can be set when the
- # class is defined.
+ # 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.name # => "configured_task"
- # t.config # => {:one => 'one', :two => 'two'}
+ # t.config # => {:one => 'one', :two => 'two'}
+ # t.one # => 'one'
+ # t.one = 'ONE'
+ # t.config # => {:one => 'ONE', :two => 'two'}
#
- # Configurations can be validated or processed using an optional
- # block. Tap::Support::Validation pre-packages several common
- # validation/processing blocks, and can be accessed through the
- # class method 'c':
+ # 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.
+ # Tap::Support::Validation pre-packages many common blocks which 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
@@ -89,46 +83,11 @@
# t.integer = 1.1 # !> ValidationError
#
# t.integer = "1"
# t.integer == 1 # => true
#
- # Tasks have a name that gets used in auditing, and as a relative
- # filepath to find associated files (for instance config files).
- # By default the task name is based on the task class, such that
- # Tap::Task has the default name 'tap/task'. Configurations
- # and custom names can be provided when a task is initialized.
- #
- # t = ConfiguredTask.new({:one => 'ONE', :three => 'three'}, "example")
- # t.name # => "example"
- # t.config # => {:one => 'ONE', :two => 'two', :three => 'three'}
- #
- # === Batches
- #
- # Tasks can be assembled into batches that enque and execute collectively.
- # Batched tasks are often alternatively-configured derivatives of one
- # parent task, although they can be manually assembled using Task.batch.
- #
- # app = Tap::App.instance
- # t1 = Tap::Task.intern(:key => 'one') do |task, input|
- # input + task.config[:key]
- # end
- # t1.batch # => [t1]
- #
- # t2 = t1.initialize_batch_obj(:key => 'two')
- # t1.batch # => [t1, t2]
- # t2.batch # => [t1, t2]
- #
- # t1.enq 't1_by_'
- # t2.enq 't2_by_'
- # app.run
- #
- # app.results(t1) # => ["t1_by_one", "t2_by_one"]
- # app.results(t2) # => ["t1_by_two", "t2_by_two"]
- #
- # Here the results reflects that t1 and t2 were run in succession with the
- # input to t1, and then the input to t2.
- #
+ #--
# === Subclassing
# Tasks can be subclassed normally, with one reminder related to batching.
#
# Batched tasks are generated by duplicating an existing instance, hence
# all instance variables will point to the same object in the batched
@@ -164,13 +123,14 @@
class << self
# Returns class dependencies
attr_reader :dependencies
- # Returns the default name for the class: to_s.underscore
+ # Sets the class default_name
attr_writer :default_name
+ # Returns the default name for the class: to_s.underscore
def default_name
# lazy-setting default_name like this (rather than
# within inherited, for example) is an optimization
# since many subclass operations end up setting
# default_name themselves.
@@ -181,33 +141,37 @@
# instance used in class-level dependencies. See depends_on.
def instance
@instance ||= new
end
- def inherited(child)
+ def inherited(child) # :nodoc:
unless child.instance_variable_defined?(:@source_file)
caller.first =~ Support::Lazydoc::CALLER_REGEXP
child.instance_variable_set(:@source_file, File.expand_path($1))
end
child.instance_variable_set(:@dependencies, dependencies.dup)
super
end
- def intern(*args, &block)
+ # Instantiates a new task with the input arguments and overrides
+ # process with the block. The block will be called with the
+ # instance, plus any inputs.
+ #
+ # Simply instantiates a new task if no block is given.
+ def intern(*args, &block) # :yields: task, inputs...
instance = new(*args)
if block_given?
instance.extend Support::Intern
instance.process_block = block
end
instance
end
- # Parses the argv into an instance of self and an array of arguments (implicitly
- # to be enqued to the instance and run by app). Yields a help string to the
- # block when the argv indicates 'help'.
- #
+ # Parses the argv into an instance of self and an array of arguments
+ # (implicitly to be enqued to the instance). Yields a help string to
+ # the block when the argv indicates 'help'.
def parse(argv=ARGV, app=Tap::App.instance, &block) # :yields: help_str
parse!(argv.dup, &block)
end
# Same as parse, but removes switches destructively.
@@ -282,19 +246,23 @@
obj.reconfigure(path_configs).reconfigure(argv_config)
[obj, (argv + use_args)]
end
+ # A convenience method to parse the argv and execute the instance
+ # with the remaining arguments. If 'help' is specified in the argv,
+ # execute prints the help and exits.
def execute(argv=ARGV)
instance, args = parse(ARGV) do |help|
puts help
exit
end
instance.execute(*args)
end
-
+
+ # Returns the class lazydoc, resolving if specified.
def lazydoc(resolve=true)
lazydoc = super(false)
lazydoc[self.to_s]['args'] ||= lazydoc.register_method(:process, Support::Lazydoc::Method)
super
end
@@ -310,10 +278,11 @@
<% end %>
<%= '-' * 80 %>
<% end %>
}
+ # Returns the class help.
def help
Tap::Support::Templater.new(DEFAULT_HELP_TEMPLATE, :task_class => self).build
end
protected
@@ -464,14 +433,11 @@
#--
# Currently names may be any object. Audit makes use of name
# via to_s, as does app when figuring configuration filepaths.
attr_accessor :name
- # Initializes a new instance and associated batch objects. Batch
- # objects will be initialized for each configuration template
- # specified by app.each_config_template(config_file) where
- # config_file = app.config_filepath(name).
+ # Initializes a new Task.
def initialize(config={}, name=nil, app=App.instance)
super()
@name = name || self.class.default_name
@app = app
@@ -495,32 +461,23 @@
end
workflow
end
- # Creates a new batched object and adds the object to batch. The batched object
- # will be a duplicate of the current object but with a new name and/or
- # configurations.
+ # Creates a new batched object and adds the object to batch. The batched
+ # object will be a duplicate of the current object but with a new name
+ # and/or configurations.
def initialize_batch_obj(overrides={}, name=nil)
obj = super().reconfigure(overrides)
obj.name = name if name
obj
end
-
- # Executes self with the given inputs. Execute provides hooks for subclasses
- # to insert standard execution code: before_execute, on_execute_error,
- # and after_execute. Override any/all of these methods as needed.
- #
- # Execute passes the inputs to process and returns the result.
- def execute(*inputs)
- _execute(*inputs)._current
- end
# 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.
+ # the task should have when enqued or executed.
#
# class TaskWithTwoInputs < Tap::Task
# def process(a, b)
# [b,a]
# end
@@ -571,10 +528,11 @@
raise err
end
private
- def execute_with_callbacks(*inputs)
+ # execute_with_callbacks is the method called by _execute
+ def execute_with_callbacks(*inputs) # :nodoc:
before_execute
begin
result = process(*inputs)
rescue
on_execute_error($!)
\ No newline at end of file