Class: CompositeTask

Inherits:
Object
  • Object
show all
Defined in:
lib/composite_task.rb,
lib/composite_task/version.rb

Overview

Simple implementation of GoF Composite pattern.

Constant Summary collapse

VERSION =
'0.2.1'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name = nil, io = STDOUT, &action) ⇒ CompositeTask

Creates a new CompositeTask, and can be used in several fashions.

For an ananymous top level class:

task = CompositeTask.new()

For a named task:

task = CompositeTask.new("Task witohut action")

For a named task, with an action block:

task = CompositeTask.new("Task with action") do |task|
  puts "Executing action for #{task.name}"
end

Once created, you can compose your task with #add_sub_task and then #execute it.

Progress reporting is done to given io. can be set to nil to disable reporting. :call-seq: initialize() initialize(nil, io=STDOUT) initialize(name, io=STDOUT) initialize(name, io=STDOUT) {|task| … }



34
35
36
37
38
39
40
41
42
# File 'lib/composite_task.rb', line 34

def initialize(name=nil, io=STDOUT, &action)
  @name = name
  @io = io
  @action = action
  if action && !name
    raise ArgumentError.new('Anonymous tasks are only allowed without a block.')
  end
  @sub_tasks = []
end

Instance Attribute Details

#actionObject (readonly)

Task action (ie: given block). Can be nil for grouping only tasks.



11
12
13
# File 'lib/composite_task.rb', line 11

def action
  @action
end

#ioObject (readonly)

IO like object where to write progress to.



14
15
16
# File 'lib/composite_task.rb', line 14

def io
  @io
end

#nameObject (readonly)

Task name (can be nil for top level task).



5
6
7
# File 'lib/composite_task.rb', line 5

def name
  @name
end

#sub_tasksObject (readonly)

Array of all CompositeTask instances that compose this Task



8
9
10
# File 'lib/composite_task.rb', line 8

def sub_tasks
  @sub_tasks
end

Instance Method Details

#[](name) ⇒ Object

Returns the first task with action with given name.



128
129
130
# File 'lib/composite_task.rb', line 128

def [] name
  tasks_with_action.select{|s| s.name == name}.first
end

#add_group(name) {|sub_task| ... } ⇒ Object

Adds a sub task without an action defined. Yields newly created task, so it can be used to compose the task:

task.add_group("Group of tasks") do |g|
   g.add_task('task1') { puts 'from task1 inside group' }
   g.add_task('task2') { puts 'from task2 inside group' }
end

Yields:

  • (sub_task)


61
62
63
64
65
# File 'lib/composite_task.rb', line 61

def add_group name # :yields: sub_task
  sub_tasks << ( sub_task = self.class.new(name) )
  yield sub_task
  self
end

#add_sub_task(task_or_name, &action) ⇒ Object

Adds a new sub task directly, or by passing its arguments (same as #initialize). :call-seq: add_sub_task(task) add_sub_task(name) {|task| … }



48
49
50
51
52
53
54
# File 'lib/composite_task.rb', line 48

def add_sub_task(task_or_name, &action)
  if task_or_name.kind_of?(self.class)
    sub_tasks << task_or_name
  else
    sub_tasks << self.class.new(task_or_name, &action)
  end
end

#call_action(indent = 0) ⇒ Object

Execute self action only, without executing any of its sub tasks. :call-seq: call_action



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/composite_task.rb', line 134

def call_action indent = 0
  if action
    write_bright "#{'  ' * indent}#{name}... "
    begin
      @action.call(self)
    rescue
      write_red "[FAIL]\n"
      raise $!
    else
      write_green "[OK]\n"
    end
  else
    if leaf?
      raise RuntimeError.new("Leaf #{name ? "\"#{name}\" " : nil}with undefined action is not allowed.")
    end
  end
end

#empty?Boolean

Returns true only if self has no actions define in itself, or in any of its sub tasks.

Returns:

  • (Boolean)


123
124
125
# File 'lib/composite_task.rb', line 123

def empty?
  tasks_with_action.count == 0
end

#execute(indent = 0) ⇒ Object

Execute all added sub tasks (#sub_tasks) in order, then execute itself (#call_action). :call-seq: execute()



70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/composite_task.rb', line 70

def execute(indent = 0)
  if leaf?
    call_action(indent)
  else
    write_bright("#{'  ' * indent}#{name}\n") if name
    increment = name ? 1 : 0
    sub_tasks.each do |task|
      task.execute(indent + increment)
    end
    call_action(indent + increment)
  end
end

#has_action?Boolean

Whether self has an action.

Returns:

  • (Boolean)


107
108
109
# File 'lib/composite_task.rb', line 107

def has_action?
  !!action
end

#leaf?Boolean

Whether it has sub tasks.

Returns:

  • (Boolean)


84
85
86
# File 'lib/composite_task.rb', line 84

def leaf?
  sub_tasks.empty?
end

#lengthObject Also known as: size

Total number tasks with action that compose this task (exclude “grouping only” tasks).



89
90
91
# File 'lib/composite_task.rb', line 89

def length
  sub_tasks.reduce(action ? 1 : 0) {|acc, sub_task| acc + sub_task.length}
end

#tasks {|_self| ... } ⇒ Object

All tasks that self is composed (including self). :call-seq: tasks -> Enumerator tasks {|task| … }

Yields:

  • (_self)

Yield Parameters:

  • _self (CompositeTask)

    the object that the method was called on



98
99
100
101
102
103
104
# File 'lib/composite_task.rb', line 98

def tasks &block
  return to_enum(__method__) unless block_given?
  yield self
  sub_tasks.each do |sub_task|
    sub_task.tasks(&block)
  end
end

#tasks_with_actionObject

All tasks that self is composed of(including self), but excluding the ones what #has_action? is false. :call-seq: tasks_with_action -> Enumerator tasks_with_action {|task| … }



115
116
117
118
119
120
# File 'lib/composite_task.rb', line 115

def tasks_with_action
  return to_enum(__method__) unless block_given?
  tasks.each do |task|
    yield task if task.has_action?
  end
end