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.
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
-