ruby-progressbar 0.0.10 → 0.11.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.
- data/lib/progressbar.rb +103 -43
- data/test.rb +132 -1
- metadata +20 -30
data/lib/progressbar.rb
CHANGED
@@ -21,11 +21,11 @@ class ProgressBar
|
|
21
21
|
@current = 0
|
22
22
|
@previous = 0
|
23
23
|
@finished_p = false
|
24
|
-
@start_time =
|
24
|
+
@start_time = time_now
|
25
25
|
@previous_time = @start_time
|
26
|
-
@title_width = 14
|
27
|
-
@format = "%-#{@title_width}s %3d%% %s %s"
|
28
26
|
@format_arguments = [:title, :percentage, :bar, :stat]
|
27
|
+
@smoothing = 0.9
|
28
|
+
@running_average = 0
|
29
29
|
clear
|
30
30
|
show
|
31
31
|
end
|
@@ -34,12 +34,26 @@ class ProgressBar
|
|
34
34
|
attr_reader :total
|
35
35
|
attr_accessor :start_time
|
36
36
|
attr_writer :bar_mark
|
37
|
+
attr_writer :title_width
|
38
|
+
|
39
|
+
def title_width
|
40
|
+
@title_width ||= 14
|
41
|
+
end
|
42
|
+
|
43
|
+
def format
|
44
|
+
@format || "%-#{title_width}s %3d%% %s %s"
|
45
|
+
end
|
46
|
+
|
47
|
+
# Exponential smoothing helps keep jitter out of the time-remaining estimate.
|
48
|
+
# The value may be anything from 0.0 to 1.0. Contrary to intuition, LOWER
|
49
|
+
# values make the average smoother, and 1.0 is equivalent to no smoothing
|
50
|
+
# whatsoever (the classic behavior). Default value is 0.9.
|
51
|
+
attr_accessor :smoothing
|
37
52
|
|
38
53
|
private
|
39
54
|
def fmt_bar
|
40
|
-
|
41
|
-
|
42
|
-
@bar_mark * bar_width,
|
55
|
+
sprintf("|%s%s|",
|
56
|
+
@bar_mark * bar_width,
|
43
57
|
" " * (@terminal_width - bar_width))
|
44
58
|
end
|
45
59
|
|
@@ -52,15 +66,19 @@ class ProgressBar
|
|
52
66
|
end
|
53
67
|
|
54
68
|
def fmt_stat_for_file_transfer
|
55
|
-
if @finished_p then
|
69
|
+
if @finished_p then
|
56
70
|
sprintf("%s %s %s", bytes, transfer_rate, elapsed)
|
57
|
-
else
|
71
|
+
else
|
58
72
|
sprintf("%s %s %s", bytes, transfer_rate, eta)
|
59
73
|
end
|
60
74
|
end
|
61
75
|
|
62
76
|
def fmt_title
|
63
|
-
@title[0,(
|
77
|
+
@title[0,(title_width - 1)] + ":"
|
78
|
+
end
|
79
|
+
|
80
|
+
def bar_width
|
81
|
+
do_percentage * @terminal_width / 100
|
64
82
|
end
|
65
83
|
|
66
84
|
def convert_bytes (bytes)
|
@@ -76,7 +94,7 @@ class ProgressBar
|
|
76
94
|
end
|
77
95
|
|
78
96
|
def transfer_rate
|
79
|
-
bytes_per_second = @current.to_f / (
|
97
|
+
bytes_per_second = @current.to_f / (time_now - @start_time)
|
80
98
|
sprintf("%s/s", convert_bytes(bytes_per_second))
|
81
99
|
end
|
82
100
|
|
@@ -85,29 +103,29 @@ class ProgressBar
|
|
85
103
|
end
|
86
104
|
|
87
105
|
def format_time (t)
|
88
|
-
t
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
106
|
+
if t < 0 or t.infinite? or t.nan? # "not a number"
|
107
|
+
'--:--:--'
|
108
|
+
else
|
109
|
+
t = t.to_i
|
110
|
+
sec = t % 60
|
111
|
+
min = (t / 60) % 60
|
112
|
+
hour = t / 3600
|
113
|
+
sprintf("%02d:%02d:%02d", hour, min, sec);
|
114
|
+
end
|
93
115
|
end
|
94
116
|
|
95
117
|
# ETA stands for Estimated Time of Arrival.
|
96
118
|
def eta
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
elapsed = Time.now - @start_time
|
101
|
-
eta = elapsed * @total / @current - elapsed;
|
102
|
-
sprintf("ETA: %s", format_time(eta))
|
103
|
-
end
|
119
|
+
elapsed = time_now - @start_time
|
120
|
+
eta = elapsed * @total / @running_average - elapsed;
|
121
|
+
sprintf("ETA: %s", format_time(eta))
|
104
122
|
end
|
105
123
|
|
106
124
|
def elapsed
|
107
|
-
elapsed =
|
125
|
+
elapsed = time_now - @start_time
|
108
126
|
sprintf("Time: %s", format_time(elapsed))
|
109
127
|
end
|
110
|
-
|
128
|
+
|
111
129
|
def eol
|
112
130
|
if @finished_p then "\n" else "\r" end
|
113
131
|
end
|
@@ -138,24 +156,47 @@ class ProgressBar
|
|
138
156
|
end
|
139
157
|
|
140
158
|
def show
|
141
|
-
|
159
|
+
tty? ? show_tty : show_no_tty
|
160
|
+
@previous_time = time_now
|
161
|
+
end
|
162
|
+
|
163
|
+
# Print output to a tty device.
|
164
|
+
def show_tty
|
165
|
+
arguments = @format_arguments.map {|method|
|
142
166
|
method = sprintf("fmt_%s", method)
|
143
167
|
send(method)
|
144
168
|
}
|
145
|
-
line = sprintf(
|
169
|
+
line = sprintf(format, *arguments)
|
146
170
|
|
147
171
|
width = get_width
|
148
|
-
if line.length == width - 1
|
149
|
-
@out.
|
150
|
-
@out.flush
|
172
|
+
if line.length == width - 1
|
173
|
+
@out.write(line + eol)
|
151
174
|
elsif line.length >= width
|
152
175
|
@terminal_width = [@terminal_width - (line.length - width + 1), 0].max
|
153
|
-
if @terminal_width == 0 then @out.
|
176
|
+
if @terminal_width == 0 then @out.write(line + eol) else show end
|
154
177
|
else # line.length < width - 1
|
155
178
|
@terminal_width += width - line.length + 1
|
156
179
|
show
|
157
180
|
end
|
158
|
-
|
181
|
+
end
|
182
|
+
|
183
|
+
# Print output to a non-terminal device, such as a log file.
|
184
|
+
# The terminal width is set to 80 columns.
|
185
|
+
def show_no_tty
|
186
|
+
@out.print("| " + elapsed + eol) and return if finished?
|
187
|
+
|
188
|
+
# Draw title the first time
|
189
|
+
if @last_bar_width.nil?
|
190
|
+
@last_bar_width = 0
|
191
|
+
@terminal_width = @terminal_width - fmt_title.size - elapsed.size - 4
|
192
|
+
@out.print(fmt_title + " |")
|
193
|
+
else
|
194
|
+
bar_width_change = bar_width - @last_bar_width
|
195
|
+
if bar_width_change > 0
|
196
|
+
@out.print(@bar_mark * bar_width_change)
|
197
|
+
@last_bar_width = bar_width
|
198
|
+
end
|
199
|
+
end
|
159
200
|
end
|
160
201
|
|
161
202
|
def show_if_needed
|
@@ -168,21 +209,38 @@ class ProgressBar
|
|
168
209
|
end
|
169
210
|
|
170
211
|
# Use "!=" instead of ">" to support negative changes
|
171
|
-
if cur_percentage != prev_percentage ||
|
172
|
-
|
212
|
+
if cur_percentage != prev_percentage ||
|
213
|
+
time_now - @previous_time >= 1 || @finished_p
|
173
214
|
show
|
174
215
|
end
|
175
216
|
end
|
176
217
|
|
218
|
+
def time_now
|
219
|
+
# Ignore Timecop time mocking
|
220
|
+
if Time.respond_to?(:now_without_mock_time)
|
221
|
+
Time.now_without_mock_time
|
222
|
+
# Ignore Delorean time mocking
|
223
|
+
elsif Time.respond_to?(:now_without_delorean)
|
224
|
+
Time.now_without_delorean
|
225
|
+
else
|
226
|
+
Time.now
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def tty?
|
231
|
+
@out.tty?
|
232
|
+
end
|
233
|
+
|
177
234
|
public
|
178
235
|
def clear
|
236
|
+
return unless tty?
|
179
237
|
@out.print "\r"
|
180
238
|
@out.print(" " * (get_width - 1))
|
181
239
|
@out.print "\r"
|
182
240
|
end
|
183
241
|
|
184
242
|
def finish
|
185
|
-
@current = @total
|
243
|
+
@current = @previous = @running_average = @total
|
186
244
|
@finished_p = true
|
187
245
|
show
|
188
246
|
end
|
@@ -209,18 +267,21 @@ class ProgressBar
|
|
209
267
|
end
|
210
268
|
|
211
269
|
def inc (step = 1)
|
212
|
-
@current
|
213
|
-
@current = @total if @current > @total
|
214
|
-
show_if_needed
|
215
|
-
@previous = @current
|
270
|
+
set(@current + step)
|
216
271
|
end
|
217
272
|
|
218
273
|
def set (count)
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
274
|
+
# Constrain input to 0 <= count <= 100
|
275
|
+
@current = [ [count, @total].min, 0 ].max
|
276
|
+
|
277
|
+
# Update the exponentially-smoothed average
|
278
|
+
@running_average = @previous * @smoothing +
|
279
|
+
@running_average * (1.0 - @smoothing)
|
280
|
+
|
281
|
+
# If this makes the percentage change by a tick or more, show it
|
223
282
|
show_if_needed
|
283
|
+
|
284
|
+
# Update for the next iteration
|
224
285
|
@previous = @current
|
225
286
|
end
|
226
287
|
|
@@ -234,4 +295,3 @@ class ReversedProgressBar < ProgressBar
|
|
234
295
|
100 - super
|
235
296
|
end
|
236
297
|
end
|
237
|
-
|
data/test.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
require 'lib/progressbar'
|
2
|
+
require File.expand_path('../lib/progressbar', __FILE__)
|
3
3
|
|
4
4
|
class ProgressBarTest < Test::Unit::TestCase
|
5
5
|
SleepUnit = 0.01
|
6
6
|
|
7
|
+
def teardown
|
8
|
+
Time.clear_stubs
|
9
|
+
end
|
10
|
+
|
7
11
|
def do_make_progress_bar (title, total)
|
8
12
|
ProgressBar.new(title, total)
|
9
13
|
end
|
@@ -106,6 +110,89 @@ class ProgressBarTest < Test::Unit::TestCase
|
|
106
110
|
}
|
107
111
|
pbar.finish
|
108
112
|
end
|
113
|
+
|
114
|
+
def test_custom_bar
|
115
|
+
custom_bar_class = Class.new(ProgressBar) do
|
116
|
+
def title_width
|
117
|
+
20
|
118
|
+
end
|
119
|
+
end
|
120
|
+
pbar = custom_bar_class.new('test(custom)', 100)
|
121
|
+
total = 100
|
122
|
+
total.times {
|
123
|
+
sleep(SleepUnit)
|
124
|
+
pbar.inc
|
125
|
+
}
|
126
|
+
pbar.finish
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_timecop
|
130
|
+
offset = 3905
|
131
|
+
total = 10000
|
132
|
+
pbar = do_make_progress_bar("test(timecop)", total)
|
133
|
+
Time.stub(:now_without_mock_time, lambda { Time.now_without_stubbing })
|
134
|
+
Time.stub(:now, lambda { Time.now_without_stubbing - offset })
|
135
|
+
0.step(500, 1) {|x|
|
136
|
+
Time.stub(:now, lambda { Time.now_without_stubbing + offset }) if x == 250
|
137
|
+
sleep(SleepUnit)
|
138
|
+
pbar.set(x)
|
139
|
+
}
|
140
|
+
pbar.halt
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_delorean
|
144
|
+
offset = 3905
|
145
|
+
total = 10000
|
146
|
+
pbar = do_make_progress_bar("test(delorean)", total)
|
147
|
+
Time.stub(:now_without_delorean, lambda { Time.now_without_stubbing })
|
148
|
+
Time.stub(:now, lambda { Time.now_without_stubbing - offset })
|
149
|
+
0.step(500, 1) {|x|
|
150
|
+
Time.stub(:now, lambda { Time.now_without_stubbing + offset }) if x == 250
|
151
|
+
sleep(SleepUnit)
|
152
|
+
pbar.set(x)
|
153
|
+
}
|
154
|
+
pbar.halt
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_vary
|
158
|
+
total = 5000
|
159
|
+
pbar = do_make_progress_bar("test(vary)", total)
|
160
|
+
0.step(500, 1) {|x|
|
161
|
+
pbar.set(x)
|
162
|
+
sleep(SleepUnit)
|
163
|
+
}
|
164
|
+
500.step(2000, 3) {|x|
|
165
|
+
pbar.set(x)
|
166
|
+
sleep(SleepUnit)
|
167
|
+
}
|
168
|
+
500.times {
|
169
|
+
pbar.set(2000)
|
170
|
+
sleep(SleepUnit)
|
171
|
+
}
|
172
|
+
2000.step(1500, -1) {|x|
|
173
|
+
pbar.set(x)
|
174
|
+
sleep(SleepUnit)
|
175
|
+
}
|
176
|
+
1500.step(5000, 5) {|x|
|
177
|
+
pbar.set(x)
|
178
|
+
sleep(SleepUnit)
|
179
|
+
}
|
180
|
+
pbar.halt
|
181
|
+
end
|
182
|
+
|
183
|
+
class StubbedTtyProgressBar < ProgressBar
|
184
|
+
def tty?; false; end
|
185
|
+
end
|
186
|
+
|
187
|
+
def test_non_tty
|
188
|
+
total = 1024 * 1024
|
189
|
+
pbar = StubbedTtyProgressBar.new("non-tty compatible", total)
|
190
|
+
0.step(total, 2**14) {|x|
|
191
|
+
pbar.set(x)
|
192
|
+
sleep(SleepUnit)
|
193
|
+
}
|
194
|
+
pbar.finish
|
195
|
+
end
|
109
196
|
end
|
110
197
|
|
111
198
|
class ReversedProgressBarTest < ProgressBarTest
|
@@ -114,3 +201,47 @@ class ReversedProgressBarTest < ProgressBarTest
|
|
114
201
|
end
|
115
202
|
end
|
116
203
|
|
204
|
+
module Stubbing
|
205
|
+
def stub(method_name, value=nil)
|
206
|
+
stubs[method_name.to_sym] = value
|
207
|
+
end
|
208
|
+
|
209
|
+
def clear_stubs
|
210
|
+
stubs.clear
|
211
|
+
end
|
212
|
+
|
213
|
+
def respond_to?(method_name, include_private=false)
|
214
|
+
has_stub?(method_name) || super
|
215
|
+
end
|
216
|
+
|
217
|
+
def method_missing(method_name, *args, &blk)
|
218
|
+
has_stub?(method_name) ? invoke_stub(method_name) : super
|
219
|
+
end
|
220
|
+
|
221
|
+
private
|
222
|
+
|
223
|
+
def stubs
|
224
|
+
@stubs ||= {}
|
225
|
+
end
|
226
|
+
|
227
|
+
def has_stub?(method_name)
|
228
|
+
stubs.keys.include? method_name.to_sym
|
229
|
+
end
|
230
|
+
|
231
|
+
def invoke_stub(method_name)
|
232
|
+
stub = stubs[method_name]
|
233
|
+
stub.respond_to?(:call) ? stub.call : stub
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
class Time
|
238
|
+
extend Stubbing
|
239
|
+
|
240
|
+
class << self
|
241
|
+
alias_method :now_without_stubbing, :now
|
242
|
+
|
243
|
+
def now
|
244
|
+
has_stub?(:now) ? invoke_stub(:now) : now_without_stubbing
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
metadata
CHANGED
@@ -1,60 +1,50 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-progressbar
|
3
|
-
version: !ruby/object:Gem::Version
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.11.0
|
4
5
|
prerelease:
|
5
|
-
version: 0.0.10
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- Satoru Takabayashi
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
|
13
|
-
date: 2009-02-16 00:00:00 -06:00
|
14
|
-
default_executable:
|
12
|
+
date: 2012-07-30 00:00:00.000000000 Z
|
15
13
|
dependencies: []
|
16
|
-
|
17
14
|
description: Ruby/ProgressBar is a text progress bar library for Ruby.
|
18
15
|
email: satoru@namazu.org
|
19
16
|
executables: []
|
20
|
-
|
21
17
|
extensions: []
|
22
|
-
|
23
18
|
extra_rdoc_files: []
|
24
|
-
|
25
|
-
files:
|
19
|
+
files:
|
26
20
|
- GPL_LICENSE
|
27
21
|
- RUBY_LICENSE
|
28
22
|
- README.md
|
29
23
|
- lib/progressbar.rb
|
30
24
|
- test.rb
|
31
|
-
has_rdoc: true
|
32
25
|
homepage: http://github.com/nex3/ruby-progressbar
|
33
26
|
licenses: []
|
34
|
-
|
35
27
|
post_install_message:
|
36
28
|
rdoc_options: []
|
37
|
-
|
38
|
-
require_paths:
|
29
|
+
require_paths:
|
39
30
|
- lib
|
40
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
41
32
|
none: false
|
42
|
-
requirements:
|
43
|
-
- -
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
version:
|
46
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - ! '>='
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
37
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
38
|
none: false
|
48
|
-
requirements:
|
49
|
-
- -
|
50
|
-
- !ruby/object:Gem::Version
|
51
|
-
version:
|
39
|
+
requirements:
|
40
|
+
- - ! '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
52
43
|
requirements: []
|
53
|
-
|
54
44
|
rubyforge_project:
|
55
|
-
rubygems_version: 1.
|
45
|
+
rubygems_version: 1.8.24
|
56
46
|
signing_key:
|
57
47
|
specification_version: 3
|
58
|
-
summary: Ruby/ProgressBar is a text progress bar library for Ruby. It can indicate
|
48
|
+
summary: Ruby/ProgressBar is a text progress bar library for Ruby. It can indicate
|
49
|
+
progress with percentage, a progress bar, and estimated remaining time.
|
59
50
|
test_files: []
|
60
|
-
|