ruby-progressbar 0.11.0 → 1.0.0rc1
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/.gitignore +5 -0
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +42 -0
- data/Guardfile +6 -0
- data/LICENSE +22 -0
- data/README.md +229 -63
- data/lib/progress_bar/base.rb +166 -0
- data/lib/progress_bar/components.rb +5 -0
- data/lib/progress_bar/components/bar.rb +44 -0
- data/lib/progress_bar/components/elapsed_timer.rb +20 -0
- data/lib/progress_bar/components/estimated_timer.rb +88 -0
- data/lib/progress_bar/components/progressable.rb +92 -0
- data/lib/progress_bar/components/timer.rb +64 -0
- data/lib/progress_bar/depreciable.rb +121 -0
- data/lib/progress_bar/format.rb +2 -0
- data/lib/progress_bar/format/base.rb +30 -0
- data/lib/progress_bar/format/molecule.rb +37 -0
- data/lib/progress_bar/formatter.rb +109 -0
- data/lib/progress_bar/length_calculator.rb +53 -0
- data/lib/progress_bar/running_average_calculator.rb +7 -0
- data/lib/progress_bar/time.rb +22 -0
- data/lib/progress_bar/version.rb +3 -0
- data/lib/progressbar.rb +8 -295
- data/lib/ruby-progressbar.rb +19 -0
- data/ruby-progressbar.gemspec +44 -0
- data/spec/progress_bar/base_spec.rb +434 -0
- data/spec/progress_bar/components/bar_spec.rb +198 -0
- data/spec/progress_bar/components/elapsed_timer_spec.rb +79 -0
- data/spec/progress_bar/components/estimated_timer_spec.rb +173 -0
- data/spec/progress_bar/components/progressable_spec.rb +30 -0
- data/spec/progress_bar/format/molecule_spec.rb +22 -0
- data/spec/progress_bar/running_average_calculator_spec.rb +11 -0
- data/spec/progress_bar/time_spec.rb +51 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/support/focused.rb +7 -0
- data/spec/support/timecop.rb +19 -0
- metadata +170 -19
- data/GPL_LICENSE +0 -340
- data/RUBY_LICENSE +0 -53
- data/test.rb +0 -247
@@ -0,0 +1,37 @@
|
|
1
|
+
class ProgressBar
|
2
|
+
module Format
|
3
|
+
class Molecule
|
4
|
+
MOLECULES = {
|
5
|
+
:t => [:left_justified_title, :title],
|
6
|
+
:T => [:right_justified_title, :title],
|
7
|
+
:c => [:current_progress, :progress],
|
8
|
+
:C => [:total_capacity, :total],
|
9
|
+
:p => [:percentage_complete_as_integer, :percentage],
|
10
|
+
:P => [:percentage_complete_as_float, :percentage_with_precision],
|
11
|
+
:a => [:elapsed_time, :elapsed_time],
|
12
|
+
:e => [:estimated_time_with_unknown, :estimated_time_with_unknown_oob],
|
13
|
+
:E => [:estimated_time_with_greater_than, :estimated_time_with_friendly_oob],
|
14
|
+
:f => [:force_estimated_time, :estimated_time_with_no_oob],
|
15
|
+
:B => [:complete_bar, :complete_bar],
|
16
|
+
:b => [:bar, :bar],
|
17
|
+
:w => [:bar_with_percentage, :bar_with_percentage],
|
18
|
+
:i => [:incomplete_space, :incomplete_space]
|
19
|
+
}
|
20
|
+
|
21
|
+
BAR_MOLECULES = %w{w B b i}
|
22
|
+
|
23
|
+
attr_reader :key
|
24
|
+
attr_reader :method_name
|
25
|
+
attr_reader :method_arguments
|
26
|
+
|
27
|
+
def initialize(letter)
|
28
|
+
@key = letter
|
29
|
+
@description, @method_name, @method_arguments = MOLECULES.fetch(@key.to_sym)
|
30
|
+
end
|
31
|
+
|
32
|
+
def bar_molecule?
|
33
|
+
BAR_MOLECULES.include? @key
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
class ProgressBar
|
2
|
+
module Formatter
|
3
|
+
DEFAULT_FORMAT_STRING = '%t: |%B|'
|
4
|
+
DEFAULT_TITLE = 'Progress'
|
5
|
+
|
6
|
+
def initialize(options)
|
7
|
+
@format_string = options[:format] || DEFAULT_FORMAT_STRING
|
8
|
+
@title = options[:title] || DEFAULT_TITLE
|
9
|
+
|
10
|
+
super(options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def format(format_string = DEFAULT_FORMAT_STRING)
|
14
|
+
@format_string = format_string
|
15
|
+
@format = ProgressBar::Format::Base.new(format_string)
|
16
|
+
|
17
|
+
process
|
18
|
+
end
|
19
|
+
|
20
|
+
def title=(title)
|
21
|
+
@title = title
|
22
|
+
end
|
23
|
+
|
24
|
+
def progress
|
25
|
+
@bar.progress
|
26
|
+
end
|
27
|
+
|
28
|
+
def total
|
29
|
+
@bar.total
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def process
|
34
|
+
@format_string.dup.tap do |processed_string|
|
35
|
+
@format.non_bar_molecules.each do |molecule|
|
36
|
+
processed_string.gsub!("%#{molecule.key}", self.send(molecule.method_name).to_s)
|
37
|
+
end
|
38
|
+
|
39
|
+
remaining_molecule_match_data = processed_string.scan(/%[a-zA-Z]/) || []
|
40
|
+
remaining_molecules = remaining_molecule_match_data.size
|
41
|
+
placeholder_length = remaining_molecules * 2
|
42
|
+
|
43
|
+
processed_string.gsub! '%%', '%'
|
44
|
+
|
45
|
+
leftover_bar_length = length - processed_string.length + placeholder_length
|
46
|
+
|
47
|
+
@format.bar_molecules.each do |molecule|
|
48
|
+
processed_string.gsub!("%#{molecule.key}", self.send(molecule.method_name, leftover_bar_length).to_s)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Format Methods
|
54
|
+
def title
|
55
|
+
@title
|
56
|
+
end
|
57
|
+
|
58
|
+
def percentage
|
59
|
+
@bar.percentage_completed
|
60
|
+
end
|
61
|
+
|
62
|
+
def percentage_with_precision
|
63
|
+
@bar.percentage_completed_with_precision
|
64
|
+
end
|
65
|
+
|
66
|
+
def elapsed_time
|
67
|
+
@elapsed_time
|
68
|
+
end
|
69
|
+
|
70
|
+
def estimated_time_with_no_oob
|
71
|
+
@estimated_time.out_of_bounds_time_format = nil
|
72
|
+
estimated_time
|
73
|
+
end
|
74
|
+
|
75
|
+
def estimated_time_with_unknown_oob
|
76
|
+
@estimated_time.out_of_bounds_time_format = :unknown
|
77
|
+
estimated_time
|
78
|
+
end
|
79
|
+
|
80
|
+
def estimated_time_with_friendly_oob
|
81
|
+
@estimated_time.out_of_bounds_time_format = :friendly
|
82
|
+
estimated_time
|
83
|
+
end
|
84
|
+
|
85
|
+
def bar(length)
|
86
|
+
@bar.length = length
|
87
|
+
@bar.standard_complete_string
|
88
|
+
end
|
89
|
+
|
90
|
+
def complete_bar(length)
|
91
|
+
@bar.length = length
|
92
|
+
@bar.to_s
|
93
|
+
end
|
94
|
+
|
95
|
+
def incomplete_space(length)
|
96
|
+
@bar.length = length
|
97
|
+
@bar.empty_string
|
98
|
+
end
|
99
|
+
|
100
|
+
def bar_with_percentage(length)
|
101
|
+
@bar.length = length
|
102
|
+
@bar.integrated_percentage_complete_string
|
103
|
+
end
|
104
|
+
|
105
|
+
def estimated_time
|
106
|
+
finished? ? @elapsed_time : @estimated_time
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class ProgressBar
|
2
|
+
module LengthCalculator
|
3
|
+
def initialize(options)
|
4
|
+
@length_override = ENV['RUBY_PROGRESS_BAR_LENGTH'] || options[:length]
|
5
|
+
|
6
|
+
super()
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
def length
|
11
|
+
@current_length || reset_length
|
12
|
+
end
|
13
|
+
|
14
|
+
def length_changed?
|
15
|
+
@current_length != calculate_length
|
16
|
+
end
|
17
|
+
|
18
|
+
def calculate_length
|
19
|
+
@length_override || terminal_width || 80
|
20
|
+
end
|
21
|
+
|
22
|
+
def reset_length
|
23
|
+
@current_length = calculate_length
|
24
|
+
end
|
25
|
+
|
26
|
+
# This code was copied and modified from Rake, available under MIT-LICENSE
|
27
|
+
# Copyright (c) 2003, 2004 Jim Weirich
|
28
|
+
def terminal_width
|
29
|
+
return 80 unless unix?
|
30
|
+
|
31
|
+
result = dynamic_width
|
32
|
+
(result < 20) ? 80 : result
|
33
|
+
rescue
|
34
|
+
80
|
35
|
+
end
|
36
|
+
|
37
|
+
def dynamic_width
|
38
|
+
dynamic_width_stty.nonzero? || dynamic_width_tput
|
39
|
+
end
|
40
|
+
|
41
|
+
def dynamic_width_stty
|
42
|
+
%x{stty size 2>/dev/null}.split[1].to_i
|
43
|
+
end
|
44
|
+
|
45
|
+
def dynamic_width_tput
|
46
|
+
%x{tput cols 2>/dev/null}.to_i
|
47
|
+
end
|
48
|
+
|
49
|
+
def unix?
|
50
|
+
RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class ProgressBar
|
2
|
+
class Time
|
3
|
+
def self.now(time = ::Time)
|
4
|
+
@@time = time
|
5
|
+
|
6
|
+
@@time.send unmocked_time_method
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
def self.unmocked_time_method
|
11
|
+
time_mocking_library_mapping.values.find { |method| @@time.respond_to? method }
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.time_mocking_library_mapping
|
15
|
+
{
|
16
|
+
:timecop => :now_without_mock_time,
|
17
|
+
:delorean => :now_without_delorean,
|
18
|
+
:actual => :now
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/progressbar.rb
CHANGED
@@ -1,297 +1,10 @@
|
|
1
|
+
require 'progress_bar/depreciable'
|
2
|
+
|
3
|
+
# This file is provided for standardization purposes only.
|
1
4
|
#
|
2
|
-
# Ruby
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# All rights reserved.
|
6
|
-
# This is free software with ABSOLUTELY NO WARRANTY.
|
7
|
-
#
|
8
|
-
# You can redistribute it and/or modify it under the terms
|
9
|
-
# of Ruby's license.
|
5
|
+
# This gem didn't follow standard Ruby naming conventions and therefore, both `ruby-progressbar.rb` and `progressbar.rb` are needed.
|
6
|
+
# `ruby-progressbar.rb` because the gem name should match the filename under `lib` and `progressbar.rb` because this gem was copied
|
7
|
+
# directly from another gem and wasn't properly modified. There are other applications which require this file.
|
10
8
|
#
|
11
|
-
|
12
|
-
|
13
|
-
VERSION = "0.0.10"
|
14
|
-
|
15
|
-
def initialize (title, total, out = STDERR)
|
16
|
-
@title = title
|
17
|
-
@total = total
|
18
|
-
@out = out
|
19
|
-
@terminal_width = 80
|
20
|
-
@bar_mark = "o"
|
21
|
-
@current = 0
|
22
|
-
@previous = 0
|
23
|
-
@finished_p = false
|
24
|
-
@start_time = time_now
|
25
|
-
@previous_time = @start_time
|
26
|
-
@format_arguments = [:title, :percentage, :bar, :stat]
|
27
|
-
@smoothing = 0.9
|
28
|
-
@running_average = 0
|
29
|
-
clear
|
30
|
-
show
|
31
|
-
end
|
32
|
-
attr_reader :title
|
33
|
-
attr_reader :current
|
34
|
-
attr_reader :total
|
35
|
-
attr_accessor :start_time
|
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
|
52
|
-
|
53
|
-
private
|
54
|
-
def fmt_bar
|
55
|
-
sprintf("|%s%s|",
|
56
|
-
@bar_mark * bar_width,
|
57
|
-
" " * (@terminal_width - bar_width))
|
58
|
-
end
|
59
|
-
|
60
|
-
def fmt_percentage
|
61
|
-
do_percentage
|
62
|
-
end
|
63
|
-
|
64
|
-
def fmt_stat
|
65
|
-
if @finished_p then elapsed else eta end
|
66
|
-
end
|
67
|
-
|
68
|
-
def fmt_stat_for_file_transfer
|
69
|
-
if @finished_p then
|
70
|
-
sprintf("%s %s %s", bytes, transfer_rate, elapsed)
|
71
|
-
else
|
72
|
-
sprintf("%s %s %s", bytes, transfer_rate, eta)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def fmt_title
|
77
|
-
@title[0,(title_width - 1)] + ":"
|
78
|
-
end
|
79
|
-
|
80
|
-
def bar_width
|
81
|
-
do_percentage * @terminal_width / 100
|
82
|
-
end
|
83
|
-
|
84
|
-
def convert_bytes (bytes)
|
85
|
-
if bytes < 1024
|
86
|
-
sprintf("%6dB", bytes)
|
87
|
-
elsif bytes < 1024 * 1000 # 1000kb
|
88
|
-
sprintf("%5.1fKB", bytes.to_f / 1024)
|
89
|
-
elsif bytes < 1024 * 1024 * 1000 # 1000mb
|
90
|
-
sprintf("%5.1fMB", bytes.to_f / 1024 / 1024)
|
91
|
-
else
|
92
|
-
sprintf("%5.1fGB", bytes.to_f / 1024 / 1024 / 1024)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
def transfer_rate
|
97
|
-
bytes_per_second = @current.to_f / (time_now - @start_time)
|
98
|
-
sprintf("%s/s", convert_bytes(bytes_per_second))
|
99
|
-
end
|
100
|
-
|
101
|
-
def bytes
|
102
|
-
convert_bytes(@current)
|
103
|
-
end
|
104
|
-
|
105
|
-
def format_time (t)
|
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
|
115
|
-
end
|
116
|
-
|
117
|
-
# ETA stands for Estimated Time of Arrival.
|
118
|
-
def eta
|
119
|
-
elapsed = time_now - @start_time
|
120
|
-
eta = elapsed * @total / @running_average - elapsed;
|
121
|
-
sprintf("ETA: %s", format_time(eta))
|
122
|
-
end
|
123
|
-
|
124
|
-
def elapsed
|
125
|
-
elapsed = time_now - @start_time
|
126
|
-
sprintf("Time: %s", format_time(elapsed))
|
127
|
-
end
|
128
|
-
|
129
|
-
def eol
|
130
|
-
if @finished_p then "\n" else "\r" end
|
131
|
-
end
|
132
|
-
|
133
|
-
def do_percentage
|
134
|
-
if @total.zero?
|
135
|
-
100
|
136
|
-
else
|
137
|
-
@current * 100 / @total
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
def get_width
|
142
|
-
# FIXME: I don't know how portable it is.
|
143
|
-
default_width = 80
|
144
|
-
begin
|
145
|
-
tiocgwinsz = 0x5413
|
146
|
-
data = [0, 0, 0, 0].pack("SSSS")
|
147
|
-
if @out.ioctl(tiocgwinsz, data) >= 0 then
|
148
|
-
rows, cols, xpixels, ypixels = data.unpack("SSSS")
|
149
|
-
cols > 0 ? cols : default_width
|
150
|
-
else
|
151
|
-
default_width
|
152
|
-
end
|
153
|
-
rescue Exception
|
154
|
-
default_width
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
def show
|
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|
|
166
|
-
method = sprintf("fmt_%s", method)
|
167
|
-
send(method)
|
168
|
-
}
|
169
|
-
line = sprintf(format, *arguments)
|
170
|
-
|
171
|
-
width = get_width
|
172
|
-
if line.length == width - 1
|
173
|
-
@out.write(line + eol)
|
174
|
-
elsif line.length >= width
|
175
|
-
@terminal_width = [@terminal_width - (line.length - width + 1), 0].max
|
176
|
-
if @terminal_width == 0 then @out.write(line + eol) else show end
|
177
|
-
else # line.length < width - 1
|
178
|
-
@terminal_width += width - line.length + 1
|
179
|
-
show
|
180
|
-
end
|
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
|
200
|
-
end
|
201
|
-
|
202
|
-
def show_if_needed
|
203
|
-
if @total.zero?
|
204
|
-
cur_percentage = 100
|
205
|
-
prev_percentage = 0
|
206
|
-
else
|
207
|
-
cur_percentage = (@current * 100 / @total).to_i
|
208
|
-
prev_percentage = (@previous * 100 / @total).to_i
|
209
|
-
end
|
210
|
-
|
211
|
-
# Use "!=" instead of ">" to support negative changes
|
212
|
-
if cur_percentage != prev_percentage ||
|
213
|
-
time_now - @previous_time >= 1 || @finished_p
|
214
|
-
show
|
215
|
-
end
|
216
|
-
end
|
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
|
-
|
234
|
-
public
|
235
|
-
def clear
|
236
|
-
return unless tty?
|
237
|
-
@out.print "\r"
|
238
|
-
@out.print(" " * (get_width - 1))
|
239
|
-
@out.print "\r"
|
240
|
-
end
|
241
|
-
|
242
|
-
def finish
|
243
|
-
@current = @previous = @running_average = @total
|
244
|
-
@finished_p = true
|
245
|
-
show
|
246
|
-
end
|
247
|
-
|
248
|
-
def finished?
|
249
|
-
@finished_p
|
250
|
-
end
|
251
|
-
|
252
|
-
def file_transfer_mode
|
253
|
-
@format_arguments = [:title, :percentage, :bar, :stat_for_file_transfer]
|
254
|
-
end
|
255
|
-
|
256
|
-
def format= (format)
|
257
|
-
@format = format
|
258
|
-
end
|
259
|
-
|
260
|
-
def format_arguments= (arguments)
|
261
|
-
@format_arguments = arguments
|
262
|
-
end
|
263
|
-
|
264
|
-
def halt
|
265
|
-
@finished_p = true
|
266
|
-
show
|
267
|
-
end
|
268
|
-
|
269
|
-
def inc (step = 1)
|
270
|
-
set(@current + step)
|
271
|
-
end
|
272
|
-
|
273
|
-
def set (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
|
282
|
-
show_if_needed
|
283
|
-
|
284
|
-
# Update for the next iteration
|
285
|
-
@previous = @current
|
286
|
-
end
|
287
|
-
|
288
|
-
def inspect
|
289
|
-
"#<ProgressBar:#{@current}/#{@total}>"
|
290
|
-
end
|
291
|
-
end
|
292
|
-
|
293
|
-
class ReversedProgressBar < ProgressBar
|
294
|
-
def do_percentage
|
295
|
-
100 - super
|
296
|
-
end
|
297
|
-
end
|
9
|
+
puts "DEPRECATION WARNING: Requiring ruby-progressbar using `require progressbar` or `gem progressbar` has been deprecated and will be disabled on or after #{ProgressBar::Depreciable::DEPRECATION_DATE}. Please use `require ruby-progressbar` or `gem ruby-progressbar` instead"
|
10
|
+
require File.join(File.dirname(__FILE__), 'ruby-progressbar')
|