composite_task 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cf0b99f705295dcf7ccdfd879b5d2f5305a231fb
4
+ data.tar.gz: 4c044311656cf9df4ee077d35a6d0e706ccbbdb3
5
+ SHA512:
6
+ metadata.gz: 73e42f649a10568aee5e70932591b3580fb9a1f6940fbdb6e4c6326cf34a1b450bbc51bf01b16a4f30a0d56539e1f9b70f47fdbbf5000ffe982d4c0eeeab0efe
7
+ data.tar.gz: 506fb9b0b76856e9dcc27bc388cf1480d233266261631ee6a077f66731b139a16a1e46a602db6be9f0c8726049a4230955186b53d2e69435b5c630bdc0d696ad
data/README.md ADDED
File without changes
@@ -0,0 +1,3 @@
1
+ class CompositeTask
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,175 @@
1
+ # Simple implementation of GoF Composite pattern.
2
+ class CompositeTask
3
+
4
+ # Task name (can be nil for top level task).
5
+ attr_reader :name
6
+
7
+ # Array of all CompositeTask instances that compose this Task
8
+ attr_reader :sub_tasks
9
+
10
+ # Task action (ie: given block). Can be nil for grouping only tasks.
11
+ attr_reader :action
12
+
13
+ # IO like object where to write progress to.
14
+ attr_reader :io
15
+
16
+ # Creates a new CompositeTask, and can be used in several fashions.
17
+ #
18
+ # For an ananymous top level class:
19
+ # task = CompositeTask.new()
20
+ # For a named task:
21
+ # task = CompositeTask.new("Task witohut action")
22
+ # For a named task, with an action block:
23
+ # task = CompositeTask.new("Task with action") do |task|
24
+ # puts "Executing action for #{task.name}"
25
+ # end
26
+ # Once created, you can compose your task with #add_sub_task and then #execute it.
27
+ #
28
+ # Progress reporting is done to given io. can be set to nil to disable reporting.
29
+ # :call-seq:
30
+ # initialize()
31
+ # initialize(nil, io=STDOUT)
32
+ # initialize(name, io=STDOUT)
33
+ # initialize(name, io=STDOUT) {|task| ... }
34
+ def initialize(name=nil, io=STDOUT, &action)
35
+ @name = name
36
+ @io = io
37
+ @action = action
38
+ if action && !name
39
+ raise ArgumentError.new('Anonymous tasks are only allowed without a block.')
40
+ end
41
+ @sub_tasks = []
42
+ end
43
+
44
+ # Adds a new sub task directly, or by passing its arguments (same as \#initialize).
45
+ # :call-seq:
46
+ # add_sub_task(task)
47
+ # add_sub_task(name) {|task| ... }
48
+ def add_sub_task(task_or_name, &action)
49
+ if task_or_name.kind_of?(self.class)
50
+ sub_tasks << task_or_name
51
+ else
52
+ sub_tasks << self.class.new(task_or_name, &action)
53
+ end
54
+ end
55
+
56
+ # Adds a sub task without an action defined. Yields newly created task, so it can be used to compose the task:
57
+ # task.add_group("Group of tasks") do |g|
58
+ # g.add_task('task1') { puts 'from task1 inside group' }
59
+ # g.add_task('task2') { puts 'from task2 inside group' }
60
+ # end
61
+ def add_group name # :yields: sub_task
62
+ sub_tasks << ( sub_task = self.class.new(name) )
63
+ yield sub_task
64
+ self
65
+ end
66
+
67
+ # Execute all added sub tasks (#sub_tasks) in order, then execute itself (#call_action).
68
+ # :call-seq:
69
+ # execute()
70
+ def execute(indent = 0)
71
+ if leaf?
72
+ call_action(indent)
73
+ else
74
+ write_bright("#{' ' * indent}#{name}\n") if name
75
+ increment = name ? 1 : 0
76
+ sub_tasks.each do |task|
77
+ task.execute(indent + increment)
78
+ end
79
+ call_action(indent + increment)
80
+ end
81
+ end
82
+
83
+ # Whether it has sub tasks.
84
+ def leaf?
85
+ sub_tasks.empty?
86
+ end
87
+
88
+ # Total number tasks with action that compose this task (exclude "grouping only" tasks).
89
+ def length
90
+ sub_tasks.reduce(action ? 1 : 0) {|acc, sub_task| acc + sub_task.length}
91
+ end
92
+ alias_method :size, :length
93
+
94
+ # All tasks that self is composed (including self).
95
+ # :call-seq:
96
+ # tasks -> Enumerator
97
+ # tasks {|task| ... }
98
+ def tasks &block
99
+ return to_enum(__method__) unless block_given?
100
+ yield self
101
+ sub_tasks.each do |sub_task|
102
+ sub_task.tasks(&block)
103
+ end
104
+ end
105
+
106
+ # Whether self has an action.
107
+ def has_action?
108
+ !!action
109
+ end
110
+
111
+ # All tasks that self is composed (including self), only includen the ones where #has_action? is true.
112
+ # :call-seq:
113
+ # tasks -> Enumerator
114
+ # tasks {|task| ... }
115
+ def tasks_with_action
116
+ return to_enum(__method__) unless block_given?
117
+ tasks.each do |task|
118
+ yield task if task.has_action?
119
+ end
120
+ end
121
+
122
+ # Returns the first task with action with given name.
123
+ def [] name
124
+ tasks_with_action.select{|s| s.name == name}.first
125
+ end
126
+
127
+ # Execute self action only, without executing any of its sub tasks.
128
+ # :call-seq: call_action
129
+ def call_action indent = 0
130
+ if action
131
+ write_bright "#{' ' * indent}#{name}... "
132
+ begin
133
+ @action.call(self)
134
+ rescue
135
+ write_red "[FAIL]\n"
136
+ raise $!
137
+ else
138
+ write_green "[OK]\n"
139
+ end
140
+ else
141
+ if leaf?
142
+ raise RuntimeError.new("Leaf #{name ? "\"#{name}\" " : nil}with undefined action is not allowed.")
143
+ end
144
+ end
145
+ end
146
+
147
+ private
148
+
149
+ ANSI_RESET = "\e[0m"
150
+ ANSI_ATTR_BRIGHT = "\e[1m"
151
+ ANSI_FG_GREEN = "\e[32m"
152
+ ANSI_FG_RED = "\e[31m"
153
+
154
+ def colorize attribute, message
155
+ return unless io
156
+ if io.tty?
157
+ io.write "#{ANSI_RESET}#{Object.const_get("#{self.class}::ANSI_#{attribute.to_s.upcase}")}#{message}#{ANSI_RESET}"
158
+ else
159
+ io.write message
160
+ end
161
+ end
162
+
163
+ def write_bright message
164
+ colorize(:attr_bright, message)
165
+ end
166
+
167
+ def write_green message
168
+ colorize(:fg_green, message)
169
+ end
170
+
171
+ def write_red message
172
+ colorize(:fg_red, message)
173
+ end
174
+
175
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: composite_task
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Fabio Pugliese Ornellas
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-02-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '10.4'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '10.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: gem_polisher
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.4'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.4'
41
+ description: This Gem implement GoF's Composite pattern for Ruby. It comes with some
42
+ helper methods, and can generate progress output (even colored for terminal).
43
+ email: fabio.ornellas@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files:
47
+ - README.md
48
+ files:
49
+ - README.md
50
+ - lib/composite_task.rb
51
+ - lib/composite_task/version.rb
52
+ homepage: https://github.com/fornellas/composite_task
53
+ licenses: []
54
+ metadata: {}
55
+ post_install_message:
56
+ rdoc_options:
57
+ - "--main"
58
+ - README.md
59
+ - lib/
60
+ - README.md
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubyforge_project:
75
+ rubygems_version: 2.4.5.1
76
+ signing_key:
77
+ specification_version: 4
78
+ summary: Gang of Four Composite Design Pattern Implementation (with goodies)
79
+ test_files: []