taski 0.4.2 → 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.
@@ -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