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