tty-progressbar 0.11.0 → 0.12.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/README.md +302 -57
- data/examples/failure.rb +14 -0
- data/examples/iterator.rb +7 -0
- data/examples/multi/main_bar.rb +17 -0
- data/examples/multi/simple.rb +15 -0
- data/examples/threaded.rb +16 -0
- data/lib/tty-progressbar.rb +3 -2
- data/lib/tty/progressbar.rb +221 -58
- data/lib/tty/progressbar/configuration.rb +4 -0
- data/lib/tty/progressbar/converter.rb +2 -1
- data/lib/tty/progressbar/formatter.rb +2 -1
- data/lib/tty/progressbar/formatter/bar.rb +3 -1
- data/lib/tty/progressbar/formatter/byte_rate.rb +2 -1
- data/lib/tty/progressbar/formatter/current.rb +2 -1
- data/lib/tty/progressbar/formatter/current_byte.rb +2 -1
- data/lib/tty/progressbar/formatter/elapsed.rb +2 -1
- data/lib/tty/progressbar/formatter/estimated.rb +2 -1
- data/lib/tty/progressbar/formatter/mean_byte.rb +2 -1
- data/lib/tty/progressbar/formatter/mean_rate.rb +2 -1
- data/lib/tty/progressbar/formatter/percent.rb +2 -1
- data/lib/tty/progressbar/formatter/rate.rb +2 -1
- data/lib/tty/progressbar/formatter/total.rb +2 -1
- data/lib/tty/progressbar/formatter/total_byte.rb +2 -1
- data/lib/tty/progressbar/meter.rb +2 -1
- data/lib/tty/progressbar/multi.rb +221 -0
- data/lib/tty/progressbar/version.rb +1 -1
- data/spec/unit/events_spec.rb +35 -0
- data/spec/unit/formatter/elapsed_spec.rb +0 -1
- data/spec/unit/head_spec.rb +34 -0
- data/spec/unit/inspect_spec.rb +1 -1
- data/spec/unit/iterate_spec.rb +48 -0
- data/spec/unit/multi/advance_spec.rb +139 -0
- data/spec/unit/multi/events_spec.rb +64 -0
- data/spec/unit/multi/finish_spec.rb +19 -0
- data/spec/unit/multi/line_inset_spec.rb +67 -0
- data/spec/unit/multi/register_spec.rb +36 -0
- data/spec/unit/multi/stop_spec.rb +17 -0
- data/spec/unit/new_spec.rb +6 -0
- data/spec/unit/reset_spec.rb +0 -2
- data/spec/unit/stop_spec.rb +21 -0
- data/spec/unit/update_spec.rb +24 -0
- data/tty-progressbar.gemspec +1 -0
- metadata +44 -2
@@ -14,6 +14,8 @@ module TTY
|
|
14
14
|
|
15
15
|
attr_accessor :complete
|
16
16
|
|
17
|
+
attr_accessor :head
|
18
|
+
|
17
19
|
attr_accessor :hide_cursor
|
18
20
|
|
19
21
|
attr_accessor :clear
|
@@ -30,10 +32,12 @@ module TTY
|
|
30
32
|
@no_width = options.fetch(:no_width) { false }
|
31
33
|
@incomplete = options.fetch(:incomplete) { ' ' }
|
32
34
|
@complete = options.fetch(:complete) { '=' }
|
35
|
+
@head = options.fetch(:head) { @complete || '=' }
|
33
36
|
@hide_cursor = options.fetch(:hide_cursor) { false }
|
34
37
|
@clear = options.fetch(:clear) { false }
|
35
38
|
@output = options.fetch(:output) { $stderr }
|
36
39
|
@frequency = options.fetch(:frequency) { 0 } # 0Hz
|
40
|
+
@interval = options.fetch(:interval) { 1 } # 1 sec
|
37
41
|
end
|
38
42
|
|
39
43
|
def total=(value)
|
@@ -1,4 +1,5 @@
|
|
1
|
-
#
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module TTY
|
4
5
|
class ProgressBar
|
@@ -35,6 +36,7 @@ module TTY
|
|
35
36
|
complete_length = (width * @progress.ratio).round
|
36
37
|
complete = Array.new(complete_length, @progress.complete)
|
37
38
|
incomplete = Array.new(width - complete_length, @progress.incomplete)
|
39
|
+
complete[-1] = @progress.head if complete_length > 0
|
38
40
|
|
39
41
|
bar = ''
|
40
42
|
bar += complete.join
|
@@ -0,0 +1,221 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'forwardable'
|
5
|
+
require 'monitor'
|
6
|
+
|
7
|
+
require_relative '../progressbar'
|
8
|
+
|
9
|
+
module TTY
|
10
|
+
class ProgressBar
|
11
|
+
# Used for managing multiple terminal progress bars
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
class Multi
|
15
|
+
include Enumerable
|
16
|
+
include MonitorMixin
|
17
|
+
|
18
|
+
extend Forwardable
|
19
|
+
|
20
|
+
def_delegators :@bars, :each, :empty?, :length, :[]
|
21
|
+
|
22
|
+
DEFAULT_INSET = {
|
23
|
+
top: Gem.win_platform? ? '+ ' : "\u250c ",
|
24
|
+
middle: Gem.win_platform? ? '|-- ' : "\u251c\u2500\u2500 ",
|
25
|
+
bottom: Gem.win_platform? ? '|__ ' : "\u2514\u2500\u2500 "
|
26
|
+
}.freeze
|
27
|
+
|
28
|
+
# Number of currently occupied rows in terminal display
|
29
|
+
attr_reader :rows
|
30
|
+
|
31
|
+
# Create a multibar
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
# bars = TTY::ProgressBar::Multi.new
|
35
|
+
#
|
36
|
+
# @example
|
37
|
+
# bars = TTY::ProgressBar::Multi.new("main [:bar]")
|
38
|
+
#
|
39
|
+
# @param [String] format
|
40
|
+
# the formatting string to display this bar
|
41
|
+
#
|
42
|
+
# @param [Hash] options
|
43
|
+
#
|
44
|
+
# @api public
|
45
|
+
def initialize(*args)
|
46
|
+
super()
|
47
|
+
@options = args.last.is_a?(::Hash) ? args.pop : {}
|
48
|
+
format = args.empty? ? nil : args.pop
|
49
|
+
@inset_opts = @options.delete(:style) { DEFAULT_INSET }
|
50
|
+
@bars = []
|
51
|
+
@rows = 0
|
52
|
+
@top_bar = nil
|
53
|
+
@top_bar = register(format) if format
|
54
|
+
|
55
|
+
@callbacks = {
|
56
|
+
progress: [],
|
57
|
+
stopped: [],
|
58
|
+
done: []
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
# Register a new progress bar
|
63
|
+
#
|
64
|
+
# @param [String] format
|
65
|
+
# the formatting string to display the bar
|
66
|
+
#
|
67
|
+
# @api public
|
68
|
+
def register(format, options = {})
|
69
|
+
bar = TTY::ProgressBar.new(format, @options.merge(options))
|
70
|
+
|
71
|
+
synchronize do
|
72
|
+
bar.attach_to(self)
|
73
|
+
@bars << bar
|
74
|
+
|
75
|
+
if @top_bar
|
76
|
+
@top_bar.update(total: total, width: total)
|
77
|
+
observe(bar)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
bar
|
82
|
+
end
|
83
|
+
|
84
|
+
# Increase row count
|
85
|
+
#
|
86
|
+
# @api public
|
87
|
+
def next_row
|
88
|
+
synchronize do
|
89
|
+
@rows += 1
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Observe a bar for emitted events
|
94
|
+
#
|
95
|
+
# @param [TTY::ProgressBar] bar
|
96
|
+
# the bar to observe for events
|
97
|
+
#
|
98
|
+
# @api public
|
99
|
+
def observe(bar)
|
100
|
+
bar.on(:progress) { top_bar.current = current; emit(:progress) }
|
101
|
+
.on(:done) { top_bar.finish; emit(:done) if complete? }
|
102
|
+
.on(:stopped) { top_bar.stop; emit(:stopped) if stopped? }
|
103
|
+
end
|
104
|
+
|
105
|
+
# Get the top level bar if it exists
|
106
|
+
#
|
107
|
+
# @api public
|
108
|
+
def top_bar
|
109
|
+
raise "No top level progress bar" unless @top_bar
|
110
|
+
|
111
|
+
@top_bar
|
112
|
+
end
|
113
|
+
|
114
|
+
def start
|
115
|
+
raise "No top level progress bar" unless @top_bar
|
116
|
+
|
117
|
+
@top_bar.start
|
118
|
+
end
|
119
|
+
|
120
|
+
# Calculate total maximum progress of all bars
|
121
|
+
#
|
122
|
+
# @return [Integer]
|
123
|
+
#
|
124
|
+
# @api public
|
125
|
+
def total
|
126
|
+
(@bars - [@top_bar]).dup.map(&:total).reduce(&:+)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Calculate total current progress of all bars
|
130
|
+
#
|
131
|
+
# @return [Integer]
|
132
|
+
#
|
133
|
+
# @api public
|
134
|
+
def current
|
135
|
+
(@bars - [@top_bar]).dup.map(&:current).reduce(&:+)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Check if all progress bars are complete
|
139
|
+
#
|
140
|
+
# @return [Boolean]
|
141
|
+
#
|
142
|
+
# @api public
|
143
|
+
def complete?
|
144
|
+
(@bars - [@top_bar]).dup.all?(&:complete?)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Check if any of the registered progress bars is stopped
|
148
|
+
#
|
149
|
+
# @return [Boolean]
|
150
|
+
#
|
151
|
+
# @api public
|
152
|
+
def stopped?
|
153
|
+
(@bars - [@top_bar]).dup.any?(&:stopped?)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Stop all progress bars
|
157
|
+
#
|
158
|
+
# @api public
|
159
|
+
def stop
|
160
|
+
@bars.dup.each(&:stop)
|
161
|
+
end
|
162
|
+
|
163
|
+
# Finish all progress bars
|
164
|
+
#
|
165
|
+
# @api public
|
166
|
+
def finish
|
167
|
+
@top_bar.finish if @top_bar
|
168
|
+
@bars.dup.each(&:finish)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Find the number of characters to move into the line
|
172
|
+
# before printing the bar
|
173
|
+
#
|
174
|
+
# @param [TTY::ProgressBar] bar
|
175
|
+
# the progress bar for which line inset is calculated
|
176
|
+
#
|
177
|
+
# @return [String]
|
178
|
+
# the inset
|
179
|
+
#
|
180
|
+
# @api public
|
181
|
+
def line_inset(bar)
|
182
|
+
return '' if @top_bar.nil?
|
183
|
+
|
184
|
+
case bar.row
|
185
|
+
when @top_bar.row
|
186
|
+
@inset_opts[:top]
|
187
|
+
when rows
|
188
|
+
@inset_opts[:bottom]
|
189
|
+
else
|
190
|
+
@inset_opts[:middle]
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Listen on event
|
195
|
+
#
|
196
|
+
# @param [Symbol] name
|
197
|
+
# the event name to listen on
|
198
|
+
#
|
199
|
+
# @api public
|
200
|
+
def on(name, &callback)
|
201
|
+
unless @callbacks.key?(name)
|
202
|
+
raise ArgumentError, "The event #{name} does not exist. "\
|
203
|
+
" Use :progress, :stopped, or :done instead"
|
204
|
+
end
|
205
|
+
@callbacks[name] << callback
|
206
|
+
self
|
207
|
+
end
|
208
|
+
|
209
|
+
private
|
210
|
+
|
211
|
+
# Fire an event by name
|
212
|
+
#
|
213
|
+
# @api private
|
214
|
+
def emit(name, *args)
|
215
|
+
@callbacks[name].each do |callback|
|
216
|
+
callback.(*args)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end # Multi
|
220
|
+
end # ProgressBar
|
221
|
+
end # TTY
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
RSpec.describe TTY::ProgressBar, 'events' do
|
4
|
+
let(:output) { StringIO.new('', 'w+') }
|
5
|
+
|
6
|
+
it "emits :progress event when advancing" do
|
7
|
+
events = []
|
8
|
+
bar = TTY::ProgressBar.new("[:bar]", output: output, total: 5)
|
9
|
+
bar.on(:progress) { events << :progress }
|
10
|
+
|
11
|
+
bar.advance
|
12
|
+
|
13
|
+
expect(events).to eq([:progress])
|
14
|
+
end
|
15
|
+
|
16
|
+
it "emits :done event when finished" do
|
17
|
+
events = []
|
18
|
+
bar = TTY::ProgressBar.new("[:bar]", output: output, total: 5)
|
19
|
+
bar.on(:done) { events << :done }
|
20
|
+
|
21
|
+
bar.finish
|
22
|
+
|
23
|
+
expect(events).to eq([:done])
|
24
|
+
end
|
25
|
+
|
26
|
+
it "emits :stopped event" do
|
27
|
+
events = []
|
28
|
+
bar = TTY::ProgressBar.new("[:bar]", output: output, total: 5)
|
29
|
+
bar.on(:stopped) { events << :stopped }
|
30
|
+
|
31
|
+
bar.stop
|
32
|
+
|
33
|
+
expect(events).to eq([:stopped])
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
RSpec.describe TTY::ProgressBar, ':head' do
|
4
|
+
let(:output) { StringIO.new('', 'w+')}
|
5
|
+
|
6
|
+
it "animates head" do
|
7
|
+
progress = TTY::ProgressBar.new("[:bar]", output: output, head: '>', total: 5)
|
8
|
+
5.times { progress.advance }
|
9
|
+
output.rewind
|
10
|
+
expect(output.read).to eq([
|
11
|
+
"\e[1G[> ]",
|
12
|
+
"\e[1G[=> ]",
|
13
|
+
"\e[1G[==> ]",
|
14
|
+
"\e[1G[===> ]",
|
15
|
+
"\e[1G[====>]\n"
|
16
|
+
].join)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "customises all output characters" do
|
20
|
+
progress = TTY::ProgressBar.new("[:bar]",
|
21
|
+
output: output,
|
22
|
+
head: 'ᗧ',
|
23
|
+
complete: '-', incomplete: '.', total: 5)
|
24
|
+
5.times { progress.advance }
|
25
|
+
output.rewind
|
26
|
+
expect(output.read).to eq([
|
27
|
+
"\e[1G[ᗧ....]",
|
28
|
+
"\e[1G[-ᗧ...]",
|
29
|
+
"\e[1G[--ᗧ..]",
|
30
|
+
"\e[1G[---ᗧ.]",
|
31
|
+
"\e[1G[----ᗧ]\n"
|
32
|
+
].join)
|
33
|
+
end
|
34
|
+
end
|