tty-progressbar 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -0
  3. data/README.md +302 -57
  4. data/examples/failure.rb +14 -0
  5. data/examples/iterator.rb +7 -0
  6. data/examples/multi/main_bar.rb +17 -0
  7. data/examples/multi/simple.rb +15 -0
  8. data/examples/threaded.rb +16 -0
  9. data/lib/tty-progressbar.rb +3 -2
  10. data/lib/tty/progressbar.rb +221 -58
  11. data/lib/tty/progressbar/configuration.rb +4 -0
  12. data/lib/tty/progressbar/converter.rb +2 -1
  13. data/lib/tty/progressbar/formatter.rb +2 -1
  14. data/lib/tty/progressbar/formatter/bar.rb +3 -1
  15. data/lib/tty/progressbar/formatter/byte_rate.rb +2 -1
  16. data/lib/tty/progressbar/formatter/current.rb +2 -1
  17. data/lib/tty/progressbar/formatter/current_byte.rb +2 -1
  18. data/lib/tty/progressbar/formatter/elapsed.rb +2 -1
  19. data/lib/tty/progressbar/formatter/estimated.rb +2 -1
  20. data/lib/tty/progressbar/formatter/mean_byte.rb +2 -1
  21. data/lib/tty/progressbar/formatter/mean_rate.rb +2 -1
  22. data/lib/tty/progressbar/formatter/percent.rb +2 -1
  23. data/lib/tty/progressbar/formatter/rate.rb +2 -1
  24. data/lib/tty/progressbar/formatter/total.rb +2 -1
  25. data/lib/tty/progressbar/formatter/total_byte.rb +2 -1
  26. data/lib/tty/progressbar/meter.rb +2 -1
  27. data/lib/tty/progressbar/multi.rb +221 -0
  28. data/lib/tty/progressbar/version.rb +1 -1
  29. data/spec/unit/events_spec.rb +35 -0
  30. data/spec/unit/formatter/elapsed_spec.rb +0 -1
  31. data/spec/unit/head_spec.rb +34 -0
  32. data/spec/unit/inspect_spec.rb +1 -1
  33. data/spec/unit/iterate_spec.rb +48 -0
  34. data/spec/unit/multi/advance_spec.rb +139 -0
  35. data/spec/unit/multi/events_spec.rb +64 -0
  36. data/spec/unit/multi/finish_spec.rb +19 -0
  37. data/spec/unit/multi/line_inset_spec.rb +67 -0
  38. data/spec/unit/multi/register_spec.rb +36 -0
  39. data/spec/unit/multi/stop_spec.rb +17 -0
  40. data/spec/unit/new_spec.rb +6 -0
  41. data/spec/unit/reset_spec.rb +0 -2
  42. data/spec/unit/stop_spec.rb +21 -0
  43. data/spec/unit/update_spec.rb +24 -0
  44. data/tty-progressbar.gemspec +1 -0
  45. 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
- # coding: utf-8
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  class ProgressBar
@@ -1,4 +1,5 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative 'pipeline'
4
5
 
@@ -1,4 +1,5 @@
1
- # coding: utf-8
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
@@ -1,4 +1,5 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative '../converter'
4
5
 
@@ -1,4 +1,5 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  class ProgressBar
@@ -1,4 +1,5 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative '../converter'
4
5
 
@@ -1,4 +1,5 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative '../converter'
4
5
 
@@ -1,4 +1,5 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative '../converter'
4
5
 
@@ -1,4 +1,5 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative '../converter'
4
5
 
@@ -1,4 +1,5 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative '../converter'
4
5
 
@@ -1,4 +1,5 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  class ProgressBar
@@ -1,4 +1,5 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative '../converter'
4
5
 
@@ -1,4 +1,5 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  class ProgressBar
@@ -1,4 +1,5 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative '../converter'
4
5
 
@@ -1,4 +1,5 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  class ProgressBar
@@ -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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module TTY
4
4
  class ProgressBar
5
- VERSION = '0.11.0'
5
+ VERSION = '0.12.0'
6
6
  end # ProgressBar
7
7
  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
@@ -53,7 +53,6 @@ RSpec.describe TTY::ProgressBar, ':elapsed token' do
53
53
  "\e[1G 3s",
54
54
  "\e[1G 4s\n",
55
55
  "\e[1G 0s",
56
- "\e[1G 0s",
57
56
  "\e[1G 1s"
58
57
  ].join)
59
58
  Timecop.return
@@ -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