taski 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +12 -13
- data/Steepfile +1 -0
- data/examples/README.md +3 -3
- data/examples/data_pipeline_demo.rb +3 -3
- data/examples/nested_section_demo.rb +161 -0
- data/examples/parallel_progress_demo.rb +1 -1
- data/examples/section_progress_demo.rb +78 -0
- data/examples/tree_progress_demo.rb +164 -0
- data/lib/taski/execution/executor.rb +247 -0
- data/lib/taski/execution/registry.rb +9 -1
- data/lib/taski/execution/task_wrapper.rb +126 -147
- data/lib/taski/execution/tree_progress_display.rb +506 -0
- data/lib/taski/section.rb +17 -0
- data/lib/taski/static_analysis/analyzer.rb +12 -1
- data/lib/taski/static_analysis/visitor.rb +100 -11
- data/lib/taski/task.rb +11 -76
- data/lib/taski/version.rb +1 -1
- data/lib/taski.rb +10 -6
- data/sig/taski.rbs +127 -64
- metadata +6 -3
- data/lib/taski/execution/coordinator.rb +0 -63
- data/lib/taski/execution/parallel_progress_display.rb +0 -201
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: taski
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ahogappa
|
|
@@ -60,16 +60,19 @@ files:
|
|
|
60
60
|
- examples/README.md
|
|
61
61
|
- examples/context_demo.rb
|
|
62
62
|
- examples/data_pipeline_demo.rb
|
|
63
|
+
- examples/nested_section_demo.rb
|
|
63
64
|
- examples/parallel_progress_demo.rb
|
|
64
65
|
- examples/quick_start.rb
|
|
65
66
|
- examples/reexecution_demo.rb
|
|
66
67
|
- examples/section_demo.rb
|
|
68
|
+
- examples/section_progress_demo.rb
|
|
69
|
+
- examples/tree_progress_demo.rb
|
|
67
70
|
- lib/taski.rb
|
|
68
71
|
- lib/taski/context.rb
|
|
69
|
-
- lib/taski/execution/
|
|
70
|
-
- lib/taski/execution/parallel_progress_display.rb
|
|
72
|
+
- lib/taski/execution/executor.rb
|
|
71
73
|
- lib/taski/execution/registry.rb
|
|
72
74
|
- lib/taski/execution/task_wrapper.rb
|
|
75
|
+
- lib/taski/execution/tree_progress_display.rb
|
|
73
76
|
- lib/taski/section.rb
|
|
74
77
|
- lib/taski/static_analysis/analyzer.rb
|
|
75
78
|
- lib/taski/static_analysis/dependency_graph.rb
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Taski
|
|
4
|
-
module Execution
|
|
5
|
-
class Coordinator
|
|
6
|
-
def initialize(registry:, analyzer:)
|
|
7
|
-
@registry = registry
|
|
8
|
-
@analyzer = analyzer
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
# @param task_class [Class] The task class whose dependencies should be started
|
|
12
|
-
def start_dependencies(task_class)
|
|
13
|
-
dependencies = get_dependencies(task_class)
|
|
14
|
-
return if dependencies.empty?
|
|
15
|
-
|
|
16
|
-
dependencies.each do |dep_class|
|
|
17
|
-
start_dependency_execution(dep_class)
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# @param task_class [Class] The task class whose dependencies should be cleaned
|
|
22
|
-
def start_clean_dependencies(task_class)
|
|
23
|
-
dependencies = get_dependencies(task_class)
|
|
24
|
-
return if dependencies.empty?
|
|
25
|
-
|
|
26
|
-
dependencies.each do |dep_class|
|
|
27
|
-
start_dependency_clean(dep_class)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
private
|
|
32
|
-
|
|
33
|
-
def get_dependencies(task_class)
|
|
34
|
-
if task_class.respond_to?(:cached_dependencies)
|
|
35
|
-
task_class.cached_dependencies
|
|
36
|
-
else
|
|
37
|
-
@analyzer.analyze(task_class)
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def start_thread_with(&block)
|
|
42
|
-
thread = Thread.new(&block)
|
|
43
|
-
@registry.register_thread(thread)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def start_dependency_execution(dep_class)
|
|
47
|
-
exported_methods = dep_class.exported_methods
|
|
48
|
-
|
|
49
|
-
exported_methods.each do |method|
|
|
50
|
-
start_thread_with do
|
|
51
|
-
dep_class.public_send(method)
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def start_dependency_clean(dep_class)
|
|
57
|
-
start_thread_with do
|
|
58
|
-
dep_class.public_send(:clean)
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "monitor"
|
|
4
|
-
|
|
5
|
-
module Taski
|
|
6
|
-
module Execution
|
|
7
|
-
class ParallelProgressDisplay
|
|
8
|
-
SPINNER_FRAMES = %w[⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏].freeze
|
|
9
|
-
|
|
10
|
-
class TaskProgress
|
|
11
|
-
attr_accessor :state, :start_time, :end_time, :error, :duration
|
|
12
|
-
|
|
13
|
-
def initialize
|
|
14
|
-
@state = :pending
|
|
15
|
-
@start_time = nil
|
|
16
|
-
@end_time = nil
|
|
17
|
-
@error = nil
|
|
18
|
-
@duration = nil
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def initialize(output: $stdout)
|
|
23
|
-
@output = output
|
|
24
|
-
@tasks = {}
|
|
25
|
-
@monitor = Monitor.new
|
|
26
|
-
@spinner_index = 0
|
|
27
|
-
@renderer_thread = nil
|
|
28
|
-
@running = false
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# @param task_class [Class] The task class to register
|
|
32
|
-
def register_task(task_class)
|
|
33
|
-
@monitor.synchronize do
|
|
34
|
-
@tasks[task_class] = TaskProgress.new
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# @param task_class [Class] The task class to check
|
|
39
|
-
# @return [Boolean] true if the task is registered
|
|
40
|
-
def task_registered?(task_class)
|
|
41
|
-
@monitor.synchronize do
|
|
42
|
-
@tasks.key?(task_class)
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# @param task_class [Class] The task class to update
|
|
47
|
-
# @param state [Symbol] The new state (:pending, :running, :completed, :failed)
|
|
48
|
-
# @param duration [Float] Duration in milliseconds (for completed tasks)
|
|
49
|
-
# @param error [Exception] Error object (for failed tasks)
|
|
50
|
-
def update_task(task_class, state:, duration: nil, error: nil)
|
|
51
|
-
@monitor.synchronize do
|
|
52
|
-
progress = @tasks[task_class]
|
|
53
|
-
return unless progress
|
|
54
|
-
|
|
55
|
-
progress.state = state
|
|
56
|
-
progress.duration = duration if duration
|
|
57
|
-
progress.error = error if error
|
|
58
|
-
|
|
59
|
-
case state
|
|
60
|
-
when :running
|
|
61
|
-
progress.start_time = Time.now
|
|
62
|
-
when :completed, :failed
|
|
63
|
-
progress.end_time = Time.now
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# @param task_class [Class] The task class
|
|
69
|
-
# @return [Symbol] The task state
|
|
70
|
-
def task_state(task_class)
|
|
71
|
-
@monitor.synchronize do
|
|
72
|
-
@tasks[task_class]&.state
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def render
|
|
77
|
-
@monitor.synchronize do
|
|
78
|
-
@tasks.each do |task_class, progress|
|
|
79
|
-
line = format_task_line(task_class, progress)
|
|
80
|
-
@output.puts line
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def start
|
|
86
|
-
return if @running
|
|
87
|
-
|
|
88
|
-
@running = true
|
|
89
|
-
@renderer_thread = Thread.new do
|
|
90
|
-
loop do
|
|
91
|
-
break unless @running
|
|
92
|
-
render_live
|
|
93
|
-
sleep 0.1
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def stop
|
|
99
|
-
return unless @running
|
|
100
|
-
|
|
101
|
-
@running = false
|
|
102
|
-
@renderer_thread&.join
|
|
103
|
-
render_final
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
private
|
|
107
|
-
|
|
108
|
-
# @return [Array<String>] Array of formatted task lines
|
|
109
|
-
def collect_task_lines
|
|
110
|
-
@tasks.map do |task_class, progress|
|
|
111
|
-
format_task_line(task_class, progress)
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def render_live
|
|
116
|
-
return unless @output.tty?
|
|
117
|
-
|
|
118
|
-
@monitor.synchronize do
|
|
119
|
-
@spinner_index += 1
|
|
120
|
-
|
|
121
|
-
lines = collect_task_lines
|
|
122
|
-
|
|
123
|
-
lines.each_with_index do |line, index|
|
|
124
|
-
@output.print "\r\e[K#{line}"
|
|
125
|
-
@output.print "\n" unless index == lines.length - 1
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
@output.print "\e[#{lines.length - 1}A" if lines.length > 1
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def render_final
|
|
133
|
-
@monitor.synchronize do
|
|
134
|
-
lines = collect_task_lines
|
|
135
|
-
|
|
136
|
-
if @output.tty? && lines.length > 0
|
|
137
|
-
lines.each_with_index do |_, index|
|
|
138
|
-
@output.print "\r\e[K"
|
|
139
|
-
@output.print "\e[1B" unless index == lines.length - 1
|
|
140
|
-
end
|
|
141
|
-
@output.print "\e[#{lines.length - 1}A" if lines.length > 1
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
lines.each do |line|
|
|
145
|
-
@output.puts line
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
# @param task_class [Class] The task class
|
|
151
|
-
# @param progress [TaskProgress] The task progress
|
|
152
|
-
# @return [String] Formatted line
|
|
153
|
-
def format_task_line(task_class, progress)
|
|
154
|
-
icon = task_icon(progress.state)
|
|
155
|
-
name = task_class.name || "AnonymousTask"
|
|
156
|
-
details = task_details(progress)
|
|
157
|
-
|
|
158
|
-
"#{icon} #{name}#{details}"
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
# @param state [Symbol] The task state
|
|
162
|
-
# @return [String] The icon character
|
|
163
|
-
def task_icon(state)
|
|
164
|
-
case state
|
|
165
|
-
when :completed
|
|
166
|
-
"✅"
|
|
167
|
-
when :failed
|
|
168
|
-
"❌"
|
|
169
|
-
when :running
|
|
170
|
-
spinner_char
|
|
171
|
-
when :pending
|
|
172
|
-
"⏳"
|
|
173
|
-
else
|
|
174
|
-
"❓"
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
# @return [String] Current spinner frame
|
|
179
|
-
def spinner_char
|
|
180
|
-
SPINNER_FRAMES[@spinner_index % SPINNER_FRAMES.length]
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
# @param progress [TaskProgress] The task progress
|
|
184
|
-
# @return [String] Details string
|
|
185
|
-
def task_details(progress)
|
|
186
|
-
case progress.state
|
|
187
|
-
when :completed
|
|
188
|
-
" (#{progress.duration}ms)"
|
|
189
|
-
when :failed
|
|
190
|
-
" (failed)"
|
|
191
|
-
when :running
|
|
192
|
-
" (running)"
|
|
193
|
-
when :pending
|
|
194
|
-
" (pending)"
|
|
195
|
-
else
|
|
196
|
-
""
|
|
197
|
-
end
|
|
198
|
-
end
|
|
199
|
-
end
|
|
200
|
-
end
|
|
201
|
-
end
|