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,44 @@
|
|
1
|
+
class ProgressBar
|
2
|
+
module Components
|
3
|
+
class Bar
|
4
|
+
include Progressable
|
5
|
+
|
6
|
+
DEFAULT_PROGRESS_MARK = '='
|
7
|
+
|
8
|
+
attr_accessor :progress_mark
|
9
|
+
attr_accessor :length
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
super
|
13
|
+
|
14
|
+
self.progress_mark = options[:progress_mark] || DEFAULT_PROGRESS_MARK
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s(options = {:format => :standard})
|
18
|
+
completed_string = send(:"#{options[:format]}_complete_string")
|
19
|
+
empty_string = ' ' * (length - completed_string.length)
|
20
|
+
|
21
|
+
"#{completed_string}#{empty_string}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def integrated_percentage_complete_string
|
25
|
+
return standard_complete_string if completed_length < 5
|
26
|
+
|
27
|
+
" #{percentage_completed} ".to_s.center(completed_length, progress_mark)
|
28
|
+
end
|
29
|
+
|
30
|
+
def standard_complete_string
|
31
|
+
progress_mark * completed_length
|
32
|
+
end
|
33
|
+
|
34
|
+
def empty_string
|
35
|
+
' ' * (length - completed_length)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def completed_length
|
40
|
+
length * percentage_completed / 100
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class ProgressBar
|
2
|
+
module Components
|
3
|
+
class ElapsedTimer
|
4
|
+
include Timer
|
5
|
+
|
6
|
+
def to_s
|
7
|
+
"Time: #{elapsed_time}"
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
def elapsed_time
|
12
|
+
return '--:--:--' unless started?
|
13
|
+
|
14
|
+
hours, minutes, seconds = divide_seconds(elapsed_seconds)
|
15
|
+
|
16
|
+
sprintf TIME_FORMAT, hours, minutes, seconds
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
class ProgressBar
|
2
|
+
module Components
|
3
|
+
class EstimatedTimer
|
4
|
+
include Timer
|
5
|
+
include Progressable
|
6
|
+
|
7
|
+
VALID_OOB_TIME_FORMATS = [:unknown, :friendly, nil]
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def start(options = {})
|
14
|
+
as(Timer).start
|
15
|
+
as(Progressable).start(options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def reset
|
19
|
+
as(Timer).reset
|
20
|
+
as(Progressable).reset
|
21
|
+
end
|
22
|
+
|
23
|
+
def out_of_bounds_time_format=(format)
|
24
|
+
raise "Invalid Out Of Bounds time format. Valid formats are #{VALID_OOB_TIME_FORMATS.inspect}" unless VALID_OOB_TIME_FORMATS.include? format
|
25
|
+
|
26
|
+
@out_of_bounds_time_format = format
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
" ETA: #{estimated_time}"
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def estimated_time
|
35
|
+
return '??:??:??' if progress_made.zero?
|
36
|
+
|
37
|
+
hours, minutes, seconds = divide_seconds(estimated_seconds_remaining)
|
38
|
+
|
39
|
+
if hours > 99 && @out_of_bounds_time_format
|
40
|
+
out_of_bounds_time
|
41
|
+
else
|
42
|
+
sprintf TIME_FORMAT, hours, minutes, seconds
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def average_seconds_per_each
|
47
|
+
return 0 if self.running_average.zero?
|
48
|
+
|
49
|
+
elapsed_seconds.to_f / self.running_average
|
50
|
+
end
|
51
|
+
|
52
|
+
def estimated_seconds_remaining
|
53
|
+
((average_seconds_per_each * self.total) - elapsed_seconds.to_f).floor
|
54
|
+
end
|
55
|
+
|
56
|
+
def out_of_bounds_time
|
57
|
+
case @out_of_bounds_time_format
|
58
|
+
when :unknown
|
59
|
+
'??:??:??'
|
60
|
+
when :friendly
|
61
|
+
'> 4 Days'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def as(ancestor, &blk)
|
66
|
+
@__as ||= {}
|
67
|
+
unless r = @__as[ancestor]
|
68
|
+
r = (@__as[ancestor] = As.new(self, ancestor))
|
69
|
+
end
|
70
|
+
r.instance_eval(&blk) if block_given?
|
71
|
+
r
|
72
|
+
end
|
73
|
+
|
74
|
+
class As
|
75
|
+
private *instance_methods.select { |m| m !~ /(^__|^\W|^binding$)/ }
|
76
|
+
|
77
|
+
def initialize(subject, ancestor)
|
78
|
+
@subject = subject
|
79
|
+
@ancestor = ancestor
|
80
|
+
end
|
81
|
+
|
82
|
+
def method_missing(sym, *args, &blk)
|
83
|
+
@ancestor.instance_method(sym).bind(@subject).call(*args,&blk)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
class ProgressBar
|
2
|
+
module Components
|
3
|
+
module Progressable
|
4
|
+
DEFAULT_TOTAL = 100
|
5
|
+
DEFAULT_BEGINNING_POSITION = 0
|
6
|
+
DEFAULT_SMOOTHING = 0.1
|
7
|
+
|
8
|
+
attr_reader :total
|
9
|
+
attr_reader :progress
|
10
|
+
attr_accessor :starting_position
|
11
|
+
attr_accessor :running_average
|
12
|
+
attr_accessor :smoothing
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
self.total = options[:total] || DEFAULT_TOTAL
|
16
|
+
self.smoothing = options[:smoothing] || DEFAULT_SMOOTHING
|
17
|
+
|
18
|
+
start :at => DEFAULT_BEGINNING_POSITION
|
19
|
+
end
|
20
|
+
|
21
|
+
def start(options = {})
|
22
|
+
self.running_average = 0
|
23
|
+
|
24
|
+
self.progress = \
|
25
|
+
self.starting_position = options[:at] || self.progress
|
26
|
+
end
|
27
|
+
|
28
|
+
def started?
|
29
|
+
!!self.starting_position
|
30
|
+
end
|
31
|
+
|
32
|
+
def increment
|
33
|
+
self.progress += 1 unless progress == total
|
34
|
+
end
|
35
|
+
|
36
|
+
def decrement
|
37
|
+
self.progress -= 1 unless progress == 0
|
38
|
+
end
|
39
|
+
|
40
|
+
def reset
|
41
|
+
start :at => self.starting_position
|
42
|
+
end
|
43
|
+
|
44
|
+
def progress=(new_progress)
|
45
|
+
validate_progress(new_progress)
|
46
|
+
|
47
|
+
@progress = new_progress
|
48
|
+
|
49
|
+
update_running_average
|
50
|
+
end
|
51
|
+
|
52
|
+
def total=(new_total)
|
53
|
+
validate_total(new_total)
|
54
|
+
@total = new_total
|
55
|
+
end
|
56
|
+
|
57
|
+
def finish
|
58
|
+
self.progress = self.total
|
59
|
+
end
|
60
|
+
|
61
|
+
def percentage_completed
|
62
|
+
# progress / total * 100
|
63
|
+
#
|
64
|
+
# Doing this way so we can avoid converting each
|
65
|
+
# number to a float and then back to an integer.
|
66
|
+
#
|
67
|
+
self.progress * 100 / total
|
68
|
+
end
|
69
|
+
|
70
|
+
def percentage_completed_with_precision
|
71
|
+
format('%5.2f', (progress.to_f * 100.0 / total * 100.0).floor / 100.0)
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
def validate_total(new_total)
|
76
|
+
(progress.nil? || new_total >= progress) || raise("You can't set the item's total value to be less than the current progress.")
|
77
|
+
end
|
78
|
+
|
79
|
+
def validate_progress(new_progress)
|
80
|
+
(total.nil? || new_progress <= total) || raise("You can't set the item's current value to be greater than the total.")
|
81
|
+
end
|
82
|
+
|
83
|
+
def progress_made
|
84
|
+
started? ? self.progress - self.starting_position : 0
|
85
|
+
end
|
86
|
+
|
87
|
+
def update_running_average
|
88
|
+
self.running_average = RunningAverageCalculator.calculate(self.running_average, self.progress, self.smoothing)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'progress_bar/time'
|
2
|
+
|
3
|
+
class ProgressBar
|
4
|
+
module Components
|
5
|
+
module Timer
|
6
|
+
TIME_FORMAT = '%02d:%02d:%02d'
|
7
|
+
|
8
|
+
def start
|
9
|
+
@started_at = stopped? ? now - (@stopped_at - @started_at) : now
|
10
|
+
@stopped_at = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def stop
|
14
|
+
@stopped_at = now
|
15
|
+
end
|
16
|
+
|
17
|
+
def pause
|
18
|
+
stop
|
19
|
+
end
|
20
|
+
|
21
|
+
def resume
|
22
|
+
start
|
23
|
+
end
|
24
|
+
|
25
|
+
def started?
|
26
|
+
!!@started_at
|
27
|
+
end
|
28
|
+
|
29
|
+
def stopped?
|
30
|
+
!!@stopped_at
|
31
|
+
end
|
32
|
+
|
33
|
+
def reset
|
34
|
+
@started_at = nil
|
35
|
+
@stopped_at = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def now
|
40
|
+
ProgressBar::Time.now
|
41
|
+
end
|
42
|
+
|
43
|
+
def elapsed_seconds
|
44
|
+
((@stopped_at || now) - @started_at).floor
|
45
|
+
end
|
46
|
+
|
47
|
+
def elapsed_time
|
48
|
+
return '--:--:--' unless started?
|
49
|
+
|
50
|
+
hours, seconds = elapsed_seconds.divmod(3600)
|
51
|
+
minutes, seconds = seconds.divmod(60)
|
52
|
+
|
53
|
+
sprintf TIME_FORMAT, hours, minutes, seconds
|
54
|
+
end
|
55
|
+
|
56
|
+
def divide_seconds(seconds)
|
57
|
+
hours, seconds = seconds.divmod(3600)
|
58
|
+
minutes, seconds = seconds.divmod(60)
|
59
|
+
|
60
|
+
[hours, minutes, seconds]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
class ProgressBar
|
2
|
+
module Depreciable
|
3
|
+
DEPRECATION_DATE = "June 30th, 2013"
|
4
|
+
|
5
|
+
def backwards_compatible_args_to_options_conversion(args)
|
6
|
+
options = {}
|
7
|
+
|
8
|
+
if args.size > 1
|
9
|
+
puts "DEPRECATION WARNING: Creating progress bars using ProgressBar.new(title, total, output_io) has been deprecated and will be removed on or after #{DEPRECATION_DATE}. Please use ProgressBar.create(:title => title, :total => total, :output => output_io) instead. The full list of options can be found here: https://github.com/jfelchner/ruby-progressbar."
|
10
|
+
options[:title] = args[0]
|
11
|
+
options[:total] = args[1]
|
12
|
+
options[:output] = args[2]
|
13
|
+
else
|
14
|
+
options = args[0]
|
15
|
+
end
|
16
|
+
|
17
|
+
options
|
18
|
+
end
|
19
|
+
|
20
|
+
def inc(value = nil)
|
21
|
+
if value.nil?
|
22
|
+
method_deprecation_message 'inc', 'increment'
|
23
|
+
|
24
|
+
increment
|
25
|
+
else
|
26
|
+
method_removal_message 'inc', 'Rather than passing a step increment to the new #increment method, you can simply do `my_progress_bar.progress += step`.'
|
27
|
+
|
28
|
+
progress = progress + value.to_i
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def set(new_value)
|
33
|
+
method_deprecation_message 'set', 'progress='
|
34
|
+
|
35
|
+
progress = new_value
|
36
|
+
end
|
37
|
+
|
38
|
+
def halt
|
39
|
+
method_deprecation_message 'halt', 'stop'
|
40
|
+
|
41
|
+
stop
|
42
|
+
end
|
43
|
+
|
44
|
+
def bar_mark=(mark)
|
45
|
+
method_deprecation_message 'bar_mark=', 'progress_mark='
|
46
|
+
|
47
|
+
progress_mark = mark
|
48
|
+
end
|
49
|
+
|
50
|
+
def title_width
|
51
|
+
method_removal_message 'title_width', 'The formatter is now smart enough to handle any title you use. Set the format for the bar as described here: https://github.com/jfelchner/ruby-progressbar'
|
52
|
+
end
|
53
|
+
|
54
|
+
def title_width=(value)
|
55
|
+
method_removal_message 'title_width=', 'The formatter is now smart enough to handle any title you use. Set the format for the bar as described here: https://github.com/jfelchner/ruby-progressbar'
|
56
|
+
end
|
57
|
+
|
58
|
+
def start_time
|
59
|
+
method_deprecation_message 'start_time'
|
60
|
+
|
61
|
+
@elapsed_time.instance_variable_get(:@started_at)
|
62
|
+
end
|
63
|
+
|
64
|
+
def start_time=(value)
|
65
|
+
method_removal_message 'start_time=', 'It is no longer appropriate to set the start time of the bar. Using #start, #stop, #pause and #resume all work as expected.'
|
66
|
+
end
|
67
|
+
|
68
|
+
def format=(value)
|
69
|
+
method_removal_message 'format=', 'The formatter has been completely rewriten for v1.0. Please use `#format(format_string)`. See https://github.com/jfelchner/ruby-progressbar for all the formatting options.'
|
70
|
+
end
|
71
|
+
|
72
|
+
def format_arguments=(value)
|
73
|
+
method_removal_message 'format_arguments=', 'The formatter has been completely rewriten for v1.0. Please use `#format(format_string)`. See https://github.com/jfelchner/ruby-progressbar for all the formatting options.'
|
74
|
+
end
|
75
|
+
|
76
|
+
def current
|
77
|
+
method_deprecation_message 'current', 'progress'
|
78
|
+
|
79
|
+
@bar.progress
|
80
|
+
end
|
81
|
+
|
82
|
+
def smoothing
|
83
|
+
method_removal_message 'smoothing'
|
84
|
+
end
|
85
|
+
|
86
|
+
def smoothing=(value)
|
87
|
+
method_removal_message 'smoothing=', 'This value can only be set via the options hash when creating a new progress bar like so: ProgressBar.create(:smoothing => 0.2)'
|
88
|
+
end
|
89
|
+
|
90
|
+
def file_transfer_mode
|
91
|
+
method_removal_message 'file_transfer_mode', 'We will be implementing a much better file transfer progress bar in the upcoming version however it has been removed for v1.0. If you still require this functionality, you should remain at v0.11.0'
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def method_deprecation_message(old_item, new_item = '')
|
97
|
+
message_has_not_been_shown?(old_item) do
|
98
|
+
replacement_message = new_item.empty? ? 'There will be no replacement.' : "Please use ##{new_item} instead."
|
99
|
+
puts "DEPRECATION WARNING: ##{old_item} will be removed on or after #{DEPRECATION_DATE}. #{replacement_message}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def method_removal_message(old_item, message = '')
|
104
|
+
message_has_not_been_shown?(old_item) do
|
105
|
+
puts "REMOVAL WARNING: ##{old_item} has been removed. There is no replacement. #{message}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def safe_string(item)
|
110
|
+
item.gsub('=', '_setter')
|
111
|
+
end
|
112
|
+
|
113
|
+
def message_has_not_been_shown?(item)
|
114
|
+
unless instance_variable_get(:"@#{safe_string item}_deprecation_warning")
|
115
|
+
instance_variable_set(:"@#{safe_string item}_deprecation_warning", true)
|
116
|
+
|
117
|
+
yield
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class ProgressBar
|
2
|
+
module Format
|
3
|
+
class Base
|
4
|
+
attr_reader :molecules
|
5
|
+
|
6
|
+
def initialize(format_string)
|
7
|
+
@molecules = parse(format_string)
|
8
|
+
end
|
9
|
+
|
10
|
+
def non_bar_molecules
|
11
|
+
molecules.select { |molecule| !molecule.bar_molecule? }
|
12
|
+
end
|
13
|
+
|
14
|
+
def bar_molecules
|
15
|
+
molecules.select { |molecule| molecule.bar_molecule? }
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def parse(format_string)
|
20
|
+
molecules = []
|
21
|
+
|
22
|
+
format_string.scan(/%[a-zA-Z]/) do |match|
|
23
|
+
molecules << Molecule.new(match[1])
|
24
|
+
end
|
25
|
+
|
26
|
+
molecules
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|