lib/tap/support/executable.rb in bahuvrihi-tap-0.10.2 vs lib/tap/support/executable.rb in bahuvrihi-tap-0.10.3

- old
+ new

@@ -1,9 +1,10 @@ require 'tap/support/audit' module Tap module Support + # Executable wraps methods to make them executable by App. Methods are # wrapped by extending the object that receives them; the easiest way # to make an object executable is to use Object#_method. module Executable @@ -13,23 +14,73 @@ # Indicates whether or not to execute in multithread mode. attr_accessor :multithread # Stores the on complete block. attr_reader :on_complete_block - + + attr_reader :dependencies + public # Extends obj with Executable and sets up all required variables. The # specified method will be called on _execute. def self.initialize(obj, method_name, multithread=false, &on_complete_block) obj.extend Executable obj.instance_variable_set(:@_method_name, method_name) obj.instance_variable_set(:@multithread, multithread) obj.instance_variable_set(:@on_complete_block, on_complete_block) + obj.instance_variable_set(:@dependencies, []) obj end - + + def self.clear_dependencies + @registry = [] + @results = [] + end + + def self.registry + @registry + end + + def self.results + @results + end + + def self.index(instance, args) + @registry.each_with_index do |entry, index| + return index if entry[0] == instance && entry[1] == args + end + nil + end + + def self.resolved?(index) + @results[index] != nil + end + + def self.resolve(indicies) + indicies.each do |index| + next if @results[index] + instance, inputs = @registry[index] + @results[index] = instance._execute(*inputs) + end + end + + def self.reset(indicies) + indicies.each {|index| @results[index] = nil } + end + + def self.register(instance, args) + if existing = index(instance, args) + return existing + end + + @registry << [instance, args] + @registry.length - 1 + end + + clear_dependencies + # Sets a block to receive the results of _execute. Raises an error # if an on_complete block is already set. Override an existing # on_complete block by specifying override = true. # # Note the block recieves an audited result and not @@ -38,10 +89,34 @@ unless on_complete_block == nil || override raise "on_complete_block already set: #{self}" end @on_complete_block = block end + + # Adds the dependency to self, making self dependent on the dependency. + # The dependency will be called with the input arguments during + # resolve_dependencies. + def depends_on(dependency, *inputs) + raise ArgumentError, "not an Executable: #{dependency}" unless dependency.kind_of?(Executable) + raise ArgumentError, "cannot depend on self" if dependency == self + + index = Executable.register(dependency, inputs) + dependencies << index unless dependencies.include?(index) + index + end + + # Resolves dependencies by calling dependency.resolve with + # the dependency arguments. + def resolve_dependencies + Executable.resolve(dependencies) + self + end + + def reset_dependencies + Executable.reset(dependencies) + self + end # Auditing method call. Executes _method_name for self, but audits # the result. Sends the audited result to the on_complete_block if set. # # Audits are initialized in the follwing manner: @@ -49,10 +124,14 @@ # will be the result of call # one input:: forks the input if it is an audit, otherwise initializes # a new audit using the input # multiple inputs:: merges the inputs into a new Audit. # + # Dependencies are resolved using resolve_dependencies before + # _method_name is executed. def _execute(*inputs) + resolve_dependencies + audit = case inputs.length when 0 then Audit.new when 1 audit = inputs.first if audit.kind_of?(Audit) \ No newline at end of file