ruby-progressbar 0.0.10 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/lib/progressbar.rb +103 -43
  2. data/test.rb +132 -1
  3. metadata +20 -30
@@ -21,11 +21,11 @@ class ProgressBar
21
21
  @current = 0
22
22
  @previous = 0
23
23
  @finished_p = false
24
- @start_time = Time.now
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
- bar_width = do_percentage * @terminal_width / 100
41
- sprintf("|%s%s|",
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,(@title_width - 1)] + ":"
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 / (Time.now - @start_time)
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 = t.to_i
89
- sec = t % 60
90
- min = (t / 60) % 60
91
- hour = t / 3600
92
- sprintf("%02d:%02d:%02d", hour, min, sec);
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
- if @current == 0
98
- "ETA: --:--:--"
99
- else
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 = Time.now - @start_time
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
- arguments = @format_arguments.map {|method|
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(@format, *arguments)
169
+ line = sprintf(format, *arguments)
146
170
 
147
171
  width = get_width
148
- if line.length == width - 1
149
- @out.print(line + eol)
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.print(line + eol) else show end
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
- @previous_time = Time.now
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
- Time.now - @previous_time >= 1 || @finished_p
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 += step
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
- if count < 0 || count > @total
220
- raise "invalid count: #{count} (total: #{@total})"
221
- end
222
- @current = count
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: "0"
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: "0"
39
+ requirements:
40
+ - - ! '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
52
43
  requirements: []
53
-
54
44
  rubyforge_project:
55
- rubygems_version: 1.6.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 progress with percentage, a progress bar, and estimated remaining time.
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
-