Class: Taski::Task

Inherits:
Object
  • Object
show all
Defined in:
lib/taski/task.rb

Overview

Base class for all tasks in the Taski framework. Tasks define units of work with dependencies and exported values.

Examples:

Defining a simple task

class MyTask < Taski::Task
  exports :result

  def run
    @result = "completed"
  end
end

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.cached_dependenciesSet<Class>

Returns cached static dependencies for this task class. Dependencies are analyzed from the run method body using static analysis.

Returns:

  • (Set<Class>)

    The set of task classes this task depends on.



62
63
64
# File 'lib/taski/task.rb', line 62

def cached_dependencies
  @dependencies_cache ||= StaticAnalysis::Analyzer.analyze(self)
end

.clear_dependency_cacheObject

Clears the cached dependency analysis. Useful when task code has changed and dependencies need to be re-analyzed.



69
70
71
# File 'lib/taski/task.rb', line 69

def clear_dependency_cache
  @dependencies_cache = nil
end

.exported_methodsArray<Symbol>

Returns the list of exported method names.

Returns:

  • (Array<Symbol>)

    The exported method names.



52
53
54
# File 'lib/taski/task.rb', line 52

def exported_methods
  @exported_methods ||= []
end

.exports(*export_methods) ⇒ Object

Declares exported methods that will be accessible after task execution. Creates instance reader and class accessor methods for each export.

Parameters:

  • export_methods (Array<Symbol>)

    The method names to export.



40
41
42
43
44
45
46
47
# File 'lib/taski/task.rb', line 40

def exports(*export_methods)
  @exported_methods = export_methods

  export_methods.each do |method|
    define_instance_reader(method)
    define_class_accessor(method)
  end
end

.inherited(subclass) ⇒ Object

Callback invoked when a subclass is created. Automatically creates a task-specific Error class for each subclass.

Parameters:

  • subclass (Class)

    The newly created subclass.



29
30
31
32
33
34
# File 'lib/taski/task.rb', line 29

def inherited(subclass)
  super
  # Create TaskClass::Error that inherits from Taski::TaskError
  error_class = Class.new(Taski::TaskError)
  subclass.const_set(:Error, error_class)
end

.reset!Object

Resets the task state and progress display. Useful for testing or re-running tasks from scratch.



106
107
108
109
110
# File 'lib/taski/task.rb', line 106

def reset!
  Taski.reset_args!
  Taski.reset_progress_display!
  @circular_dependency_checked = false
end

.run(args: {}, workers: nil) ⇒ Object

Executes the task and all its dependencies. Creates a fresh registry each time for independent execution.

Parameters:

  • args (Hash) (defaults to: {})

    User-defined arguments accessible via Taski.args.

  • workers (Integer, nil) (defaults to: nil)

    Number of worker threads for parallel execution. Must be a positive integer or nil. Use workers: 1 for sequential execution (useful for debugging).

Returns:

  • (Object)

    The result of task execution.

Raises:

  • (ArgumentError)

    If workers is not a positive integer or nil.



82
83
84
# File 'lib/taski/task.rb', line 82

def run(args: {}, workers: nil)
  with_execution_setup(args: args, workers: workers) { |wrapper| wrapper.run }
end

.run_and_clean(args: {}, workers: nil, clean_on_failure: false) { ... } ⇒ Object

Execute run followed by clean in a single operation. By default, clean is skipped when run fails. Use clean_on_failure: true to always execute clean for resource release. An optional block is executed between run and clean phases.

Parameters:

  • args (Hash) (defaults to: {})

    User-defined arguments accessible via Taski.args.

  • workers (Integer, nil) (defaults to: nil)

    Number of worker threads for parallel execution. Must be a positive integer or nil.

  • clean_on_failure (Boolean) (defaults to: false)

    When true, clean runs even if run raises (default: false).

Yields:

  • Optional block executed between run and clean phases

Returns:

  • (Object)

    The result of task execution

Raises:

  • (ArgumentError)

    If workers is not a positive integer or nil.



99
100
101
# File 'lib/taski/task.rb', line 99

def run_and_clean(args: {}, workers: nil, clean_on_failure: false, &block)
  with_execution_setup(args: args, workers: workers) { |wrapper| wrapper.run_and_clean(clean_on_failure: clean_on_failure, &block) }
end

.treeString

Renders a static tree representation of the task dependencies.

Returns:

  • (String)

    The rendered tree string.



115
116
117
118
119
120
121
122
123
# File 'lib/taski/task.rb', line 115

def tree
  output = StringIO.new
  theme = Progress::Theme::Plain.new
  layout = Progress::Layout::Tree.for(output: output, theme: theme)
  context = Execution::ExecutionFacade.new(root_task_class: self)
  layout.context = context
  layout.on_ready
  layout.render_tree
end

Instance Method Details

#cleanObject

Cleans up resources after task execution. Override in subclasses to implement cleanup logic.



263
264
# File 'lib/taski/task.rb', line 263

def clean
end

#group(name) { ... } ⇒ Object

Groups related output within a task for organized progress display. The group name is shown in the progress tree as a child of the task. Groups cannot be nested.

Examples:

def run
  group("Preparing") do
    puts "Checking dependencies..."
    puts "Validating config..."
  end
  group("Deploying") do
    puts "Uploading files..."
  end
end

Parameters:

  • name (String)

    The group name to display

Yields:

  • The block to execute within the group

Returns:

  • (Object)

    The result of the block

Raises:

  • Re-raises any exception from the block after marking group as failed



303
304
305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/taski/task.rb', line 303

def group(name)
  context = Execution::ExecutionFacade.current
  phase = Thread.current[:taski_current_phase] || :run
  context&.notify_group_started(self.class, name, phase: phase, timestamp: Time.now)

  begin
    result = yield
    context&.notify_group_completed(self.class, name, phase: phase, timestamp: Time.now)
    result
  rescue
    context&.notify_group_completed(self.class, name, phase: phase, timestamp: Time.now)
    raise
  end
end

#reset!Object

Resets the instance's exported values to nil.



320
321
322
323
324
# File 'lib/taski/task.rb', line 320

def reset!
  self.class.exported_methods.each do |method|
    instance_variable_set("@#{method}", nil)
  end
end

#runObject

Executes the task's main logic. Subclasses must override this method to implement task behavior.

Raises:

  • (NotImplementedError)

    If not overridden in a subclass.



256
257
258
# File 'lib/taski/task.rb', line 256

def run
  raise NotImplementedError, "Subclasses must implement the run method"
end

#system(*args, **opts) ⇒ Boolean?

Override system() to capture subprocess output through the pipe-based architecture. Uses Kernel.system with :out option to redirect output to the task's pipe. If user provides :out or :err options, they are respected (no automatic redirection).

Parameters:

  • args (Array)

    Command arguments (shell mode if single string, exec mode if array)

  • opts (Hash)

    Options passed to Kernel.system

Returns:

  • (Boolean, nil)

    true if command succeeded, false if failed, nil if command not found



272
273
274
275
276
277
278
279
280
281
282
# File 'lib/taski/task.rb', line 272

def system(*args, **opts)
  write_io = $stdout.respond_to?(:current_write_io) ? $stdout.current_write_io : nil

  if write_io && !opts.key?(:out)
    # Redirect subprocess output to the task's pipe (stderr merged into stdout)
    Kernel.system(*args, out: write_io, err: [:child, :out], **opts)
  else
    # No capture active or user provided custom :out, use normal system
    Kernel.system(*args, **opts)
  end
end