tapsoob 0.6.1-java → 0.7.0-java

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,234 +0,0 @@
1
- # -*- encoding : utf-8 -*-
2
- require 'tapsoob/progress_bar'
3
-
4
- # MultiProgressBar manages multiple progress bars in parallel
5
- # Each bar gets its own line in the terminal
6
- class MultiProgressBar
7
- def initialize(max_bars = 4)
8
- @max_bars = max_bars
9
- @bars = []
10
- @mutex = Mutex.new
11
- @active = true
12
- @out = STDOUT
13
- @last_update = Time.now
14
- @reserved_lines = 0 # Track how many lines we've actually reserved
15
- @max_title_width = 14 # Minimum width, will grow with longer titles
16
- end
17
-
18
- # Create a new progress bar and return it
19
- def create_bar(title, total)
20
- @mutex.synchronize do
21
- # Remove any existing bar with the same title to prevent duplicates
22
- @bars.reject! { |b| b.title == title }
23
-
24
- # Update max title width to accommodate longer titles
25
- @max_title_width = [@max_title_width, title.length].max
26
-
27
- bar = ThreadSafeProgressBar.new(title, total, self)
28
-
29
- # Reserve a line for this new bar during active updates
30
- # Cap at 2 * max_bars to show active workers + some recent finished bars
31
- if @reserved_lines < @max_bars * 2
32
- @out.print "\n"
33
- @out.flush
34
- @reserved_lines += 1
35
- end
36
-
37
- @bars << bar
38
- bar
39
- end
40
- end
41
-
42
- # Get the current maximum title width for alignment
43
- # Note: Always called from within synchronized methods, so no mutex needed
44
- def max_title_width
45
- @max_title_width
46
- end
47
-
48
- # Called by individual bars when they update
49
- def update
50
- @mutex.synchronize do
51
- return unless @active
52
- return unless should_redraw?
53
-
54
- @last_update = Time.now
55
- redraw_all
56
- end
57
- end
58
-
59
- # Finish a specific bar - mark it as completed
60
- def finish_bar(bar)
61
- @mutex.synchronize do
62
- return unless @active
63
-
64
- bar.mark_finished
65
-
66
- # Respect throttle when finishing to avoid spamming redraws
67
- if should_redraw?
68
- @last_update = Time.now
69
- redraw_all
70
- end
71
- # If throttled, the next regular update will show the finished state
72
- end
73
- end
74
-
75
- # Stop all progress bars and keep them visible
76
- def stop
77
- @mutex.synchronize do
78
- @active = false
79
-
80
- # Final cleanup: remove any duplicate titles (keep the last occurrence of each unique title)
81
- @bars = @bars.reverse.uniq { |bar| bar.title }.reverse
82
-
83
- # Final redraw to show completed state (skip active check)
84
- redraw_all(true)
85
- # Move cursor past all bars
86
- @out.print "\n"
87
- @out.flush
88
- end
89
- end
90
-
91
- private
92
-
93
- # Check if enough time has passed to redraw (throttle to 10 updates/sec)
94
- def should_redraw?
95
- Time.now - @last_update >= 0.1
96
- end
97
-
98
- def redraw_all(force = false)
99
- return unless force || @active
100
- return if @bars.empty?
101
-
102
- if force && !@active
103
- render_final_display
104
- else
105
- render_active_display
106
- end
107
- end
108
-
109
- # Final display: show all completed bars
110
- def render_final_display
111
- # Clear the reserved lines first
112
- if @reserved_lines > 0
113
- @out.print "\r\e[#{@reserved_lines}A"
114
- @reserved_lines.times { @out.print "\r\e[K\n" }
115
- end
116
-
117
- # Print all bars (adds new lines as needed)
118
- @bars.each do |bar|
119
- @out.print "\r\e[K"
120
- bar.render_to(@out)
121
- @out.print "\n"
122
- end
123
-
124
- @out.flush
125
- end
126
-
127
- # Normal operation: show active bars + recent finished in reserved space
128
- def render_active_display
129
- return if @reserved_lines == 0
130
-
131
- # Partition bars in a single pass for efficiency
132
- active_bars, finished_bars = @bars.partition { |b| !b.finished? }
133
-
134
- # Build display: active bars first, then recent finished to fill remaining space
135
- # Ensure we don't request negative count from .last()
136
- remaining_space = [@reserved_lines - active_bars.length, 0].max
137
- bars_to_draw = active_bars + finished_bars.last(remaining_space)
138
-
139
- # If we have more bars than reserved lines, show only the most recent
140
- bars_to_draw = bars_to_draw.last(@reserved_lines) if bars_to_draw.length > @reserved_lines
141
-
142
- # Move up and redraw in reserved space
143
- @out.print "\r\e[#{@reserved_lines}A"
144
- @reserved_lines.times do |i|
145
- @out.print "\r\e[K"
146
- bars_to_draw[i].render_to(@out) if i < bars_to_draw.length
147
- @out.print "\n"
148
- end
149
-
150
- @out.flush
151
- end
152
- end
153
-
154
- # Thread-safe progress bar that reports to a MultiProgressBar
155
- class ThreadSafeProgressBar < ProgressBar
156
- attr_reader :title
157
-
158
- def initialize(title, total, multi_progress_bar)
159
- @multi_progress_bar = multi_progress_bar
160
- @out = STDOUT # Need this for get_width to work
161
- # Don't call parent initialize, we'll manage output ourselves
162
- @title = title
163
- @total = total
164
- @terminal_width = 80
165
- @bar_mark = "="
166
- @current = 0
167
- @previous = 0
168
- @finished_p = false
169
- @start_time = ::Time.now
170
- @previous_time = @start_time
171
- @format_arguments = [:title, :percentage, :bar, :stat]
172
- end
173
-
174
- # Override show to notify multi-progress instead of direct output
175
- def show
176
- @previous_time = ::Time.now # Update to prevent time-based refresh spam
177
- @multi_progress_bar.update
178
- end
179
-
180
- # Render this bar to the given output stream
181
- def render_to(out)
182
- # Get dynamic title width from MultiProgressBar for consistent alignment
183
- # Store as instance variable so parent class fmt_* methods can use it
184
- @title_width = @multi_progress_bar.max_title_width
185
-
186
- # Recalculate terminal width to handle resizes and use full width
187
- width = get_width
188
- # Calculate bar width: total_width - fixed_elements - padding
189
- # Fixed: title(variable) + " "(1) + percentage(4) + " "(1) + "|"(1) + "|"(1) + " "(1) + timer(15) = title_width + 25
190
- # Padding: +3 for timer fluctuations and safety
191
- fixed_chars = @title_width + 28
192
- @terminal_width = [width - fixed_chars, 20].max
193
-
194
- # Build format string with dynamic title width
195
- format = "%-#{@title_width}s %3d%% %s %s"
196
- arguments = @format_arguments.map { |method| send("fmt_#{method}") }
197
- line = sprintf(format, *arguments)
198
-
199
- # Ensure line doesn't exceed terminal width to prevent wrapping
200
- # Leave 2 chars margin for safety
201
- line = line[0, width - 2] if line.length > width - 2
202
-
203
- out.print(line)
204
- end
205
-
206
- # Override clear to do nothing (managed by MultiProgressBar)
207
- def clear
208
- # no-op
209
- end
210
-
211
- # Mark this bar as finished (for tracking)
212
- def mark_finished
213
- @finished_p = true
214
- end
215
-
216
- # Override to use the same @finished_p flag
217
- def finished?
218
- @finished_p
219
- end
220
-
221
- # Override finish to notify multi-progress
222
- def finish
223
- @current = @total
224
- @multi_progress_bar.finish_bar(self)
225
- end
226
-
227
- # Override inc to check if we need to update
228
- def inc(step = 1)
229
- @current += step
230
- @current = @total if @current > @total
231
- show_if_needed
232
- @previous = @current
233
- end
234
- end
@@ -1,237 +0,0 @@
1
- # -*- encoding : utf-8 -*-
2
- #
3
- # Ruby/ProgressBar - a text progress bar library
4
- #
5
- # Copyright (C) 2001-2005 Satoru Takabayashi <satoru@namazu.org>
6
- # All rights reserved.
7
- # This is free software with ABSOLUTELY NO WARRANTY.
8
- #
9
- # You can redistribute it and/or modify it under the terms
10
- # of Ruby's license.
11
- #
12
-
13
- class ProgressBar
14
- VERSION = "0.9"
15
-
16
- def initialize (title, total, out = STDOUT, title_width = nil)
17
- @title = title
18
- @total = total
19
- @out = out
20
- @terminal_width = 80
21
- @bar_mark = "="
22
- @current = 0
23
- @previous = 0
24
- @finished_p = false
25
- @start_time = ::Time.now
26
- @previous_time = @start_time
27
- # Set title width: use provided width, or accommodate the title, with a minimum of 14
28
- @title_width = title_width || [title.length, 14].max
29
- @format = "%-#{@title_width}s %3d%% %s %s"
30
- @format_arguments = [:title, :percentage, :bar, :stat]
31
- clear
32
- show
33
- end
34
- attr_reader :title
35
- attr_reader :current
36
- attr_reader :total
37
- attr_accessor :start_time
38
-
39
- private
40
- def fmt_bar
41
- bar_width = do_percentage * @terminal_width / 100
42
- sprintf("|%s%s|",
43
- @bar_mark * bar_width,
44
- " " * (@terminal_width - bar_width))
45
- end
46
-
47
- def fmt_percentage
48
- do_percentage
49
- end
50
-
51
- def fmt_stat
52
- if @finished_p then elapsed else eta end
53
- end
54
-
55
- def fmt_stat_for_file_transfer
56
- if @finished_p then
57
- sprintf("%s %s %s", bytes, transfer_rate, elapsed)
58
- else
59
- sprintf("%s %s %s", bytes, transfer_rate, eta)
60
- end
61
- end
62
-
63
- def fmt_title
64
- @title[0,(@title_width - 1)] + ":"
65
- end
66
-
67
- def convert_bytes (bytes)
68
- if bytes < 1024
69
- sprintf("%6dB", bytes)
70
- elsif bytes < 1024 * 1000 # 1000kb
71
- sprintf("%5.1fKB", bytes.to_f / 1024)
72
- elsif bytes < 1024 * 1024 * 1000 # 1000mb
73
- sprintf("%5.1fMB", bytes.to_f / 1024 / 1024)
74
- else
75
- sprintf("%5.1fGB", bytes.to_f / 1024 / 1024 / 1024)
76
- end
77
- end
78
-
79
- def transfer_rate
80
- bytes_per_second = @current.to_f / (::Time.now - @start_time)
81
- sprintf("%s/s", convert_bytes(bytes_per_second))
82
- end
83
-
84
- def bytes
85
- convert_bytes(@current)
86
- end
87
-
88
- def format_time (t)
89
- t = t.to_i
90
- sec = t % 60
91
- min = (t / 60) % 60
92
- hour = t / 3600
93
- sprintf("%02d:%02d:%02d", hour, min, sec);
94
- end
95
-
96
- # ETA stands for Estimated Time of Arrival.
97
- def eta
98
- if @current == 0
99
- "ETA: --:--:--"
100
- else
101
- elapsed = ::Time.now - @start_time
102
- eta = elapsed * @total / @current - elapsed;
103
- sprintf("ETA: %s", format_time(eta))
104
- end
105
- end
106
-
107
- def elapsed
108
- elapsed = ::Time.now - @start_time
109
- sprintf("Time: %s", format_time(elapsed))
110
- end
111
-
112
- def eol
113
- if @finished_p then "\n" else "\r" end
114
- end
115
-
116
- def do_percentage
117
- if @total.zero?
118
- 100
119
- else
120
- @current * 100 / @total
121
- end
122
- end
123
-
124
- def get_width
125
- # FIXME: I don't know how portable it is.
126
- default_width = 80
127
- begin
128
- tiocgwinsz = 0x5413
129
- data = [0, 0, 0, 0].pack("SSSS")
130
- if @out.ioctl(tiocgwinsz, data) >= 0 then
131
- rows, cols, xpixels, ypixels = data.unpack("SSSS")
132
- if cols > 0 then cols else default_width end
133
- else
134
- default_width
135
- end
136
- rescue Exception
137
- default_width
138
- end
139
- end
140
-
141
- def show
142
- arguments = @format_arguments.map {|method|
143
- method = sprintf("fmt_%s", method)
144
- send(method)
145
- }
146
- line = sprintf(@format, *arguments)
147
-
148
- width = get_width
149
- if line.length == width - 1
150
- @out.print(line + eol)
151
- @out.flush
152
- elsif line.length >= width
153
- @terminal_width = [@terminal_width - (line.length - width + 1), 0].max
154
- if @terminal_width == 0 then @out.print(line + eol) else show end
155
- else # line.length < width - 1
156
- @terminal_width += width - line.length + 1
157
- show
158
- end
159
- @previous_time = ::Time.now
160
- end
161
-
162
- def show_if_needed
163
- if @total.zero?
164
- cur_percentage = 100
165
- prev_percentage = 0
166
- else
167
- cur_percentage = (@current * 100 / @total).to_i
168
- prev_percentage = (@previous * 100 / @total).to_i
169
- end
170
-
171
- # Use "!=" instead of ">" to support negative changes
172
- if cur_percentage != prev_percentage ||
173
- ::Time.now - @previous_time >= 1 || @finished_p
174
- show
175
- end
176
- end
177
-
178
- public
179
- def clear
180
- @out.print "\r"
181
- @out.print(" " * (get_width - 1))
182
- @out.print "\r"
183
- end
184
-
185
- def finish
186
- @current = @total
187
- @finished_p = true
188
- show
189
- end
190
-
191
- def finished?
192
- @finished_p
193
- end
194
-
195
- def file_transfer_mode
196
- @format_arguments = [:title, :percentage, :bar, :stat_for_file_transfer]
197
- end
198
-
199
- def format= (format)
200
- @format = format
201
- end
202
-
203
- def format_arguments= (arguments)
204
- @format_arguments = arguments
205
- end
206
-
207
- def halt
208
- @finished_p = true
209
- show
210
- end
211
-
212
- def inc (step = 1)
213
- @current += step
214
- @current = @total if @current > @total
215
- show_if_needed
216
- @previous = @current
217
- end
218
-
219
- def set (count)
220
- if count < 0 || count > @total
221
- raise "invalid count: #{count} (total: #{@total})"
222
- end
223
- @current = count
224
- show_if_needed
225
- @previous = @current
226
- end
227
-
228
- def inspect
229
- "#<ProgressBar:#{@current}/#{@total}>"
230
- end
231
- end
232
-
233
- class ReversedProgressBar < ProgressBar
234
- def do_percentage
235
- 100 - super
236
- end
237
- end