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