tty-progressbar 0.17.0 → 0.18.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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +492 -126
  5. data/lib/tty-progressbar.rb +2 -2
  6. data/lib/tty/progressbar.rb +168 -68
  7. data/lib/tty/progressbar/configuration.rb +121 -27
  8. data/lib/tty/progressbar/converter.rb +16 -19
  9. data/lib/tty/progressbar/formats.rb +120 -0
  10. data/lib/tty/progressbar/formatter.rb +33 -38
  11. data/lib/tty/progressbar/formatter/bar.rb +74 -27
  12. data/lib/tty/progressbar/formatter/byte_rate.rb +6 -20
  13. data/lib/tty/progressbar/formatter/current.rb +4 -19
  14. data/lib/tty/progressbar/formatter/current_byte.rb +9 -17
  15. data/lib/tty/progressbar/formatter/elapsed.rb +9 -18
  16. data/lib/tty/progressbar/formatter/estimated.rb +14 -18
  17. data/lib/tty/progressbar/formatter/estimated_time.rb +37 -0
  18. data/lib/tty/progressbar/formatter/mean_byte.rb +6 -20
  19. data/lib/tty/progressbar/formatter/mean_rate.rb +6 -20
  20. data/lib/tty/progressbar/formatter/percent.rb +10 -16
  21. data/lib/tty/progressbar/formatter/rate.rb +5 -19
  22. data/lib/tty/progressbar/formatter/total.rb +10 -16
  23. data/lib/tty/progressbar/formatter/total_byte.rb +14 -18
  24. data/lib/tty/progressbar/formatters.rb +53 -0
  25. data/lib/tty/progressbar/meter.rb +2 -2
  26. data/lib/tty/progressbar/multi.rb +61 -21
  27. data/lib/tty/progressbar/pipeline.rb +13 -6
  28. data/lib/tty/progressbar/timer.rb +89 -0
  29. data/lib/tty/progressbar/version.rb +1 -1
  30. metadata +44 -103
  31. data/Rakefile +0 -8
  32. data/examples/color.rb +0 -18
  33. data/examples/failure.rb +0 -12
  34. data/examples/iterator.rb +0 -5
  35. data/examples/lazy.rb +0 -6
  36. data/examples/multi/main_bar.rb +0 -13
  37. data/examples/multi/simple.rb +0 -13
  38. data/examples/multi/width.rb +0 -13
  39. data/examples/simple.rb +0 -7
  40. data/examples/slow_process.rb +0 -29
  41. data/examples/speed.rb +0 -11
  42. data/examples/threaded.rb +0 -14
  43. data/examples/tokens.rb +0 -12
  44. data/examples/unicode.rb +0 -7
  45. data/spec/spec_helper.rb +0 -53
  46. data/spec/unit/advance_spec.rb +0 -25
  47. data/spec/unit/clear_spec.rb +0 -17
  48. data/spec/unit/complete_spec.rb +0 -16
  49. data/spec/unit/converter/to_bytes_spec.rb +0 -47
  50. data/spec/unit/converter/to_seconds_spec.rb +0 -15
  51. data/spec/unit/converter/to_time_spec.rb +0 -19
  52. data/spec/unit/custom_formatter_spec.rb +0 -26
  53. data/spec/unit/custom_token_spec.rb +0 -14
  54. data/spec/unit/events_spec.rb +0 -33
  55. data/spec/unit/finish_spec.rb +0 -15
  56. data/spec/unit/formatter/bar_spec.rb +0 -33
  57. data/spec/unit/formatter/byte_rate_spec.rb +0 -32
  58. data/spec/unit/formatter/current_byte_spec.rb +0 -16
  59. data/spec/unit/formatter/current_spec.rb +0 -14
  60. data/spec/unit/formatter/elapsed_spec.rb +0 -58
  61. data/spec/unit/formatter/estimated_spec.rb +0 -27
  62. data/spec/unit/formatter/mean_byte_spec.rb +0 -32
  63. data/spec/unit/formatter/mean_rate_spec.rb +0 -31
  64. data/spec/unit/formatter/percent_spec.rb +0 -16
  65. data/spec/unit/formatter/rate_spec.rb +0 -31
  66. data/spec/unit/formatter/total_byte_spec.rb +0 -16
  67. data/spec/unit/formatter/total_spec.rb +0 -16
  68. data/spec/unit/frequency_spec.rb +0 -27
  69. data/spec/unit/head_spec.rb +0 -32
  70. data/spec/unit/hide_cursor_spec.rb +0 -27
  71. data/spec/unit/inspect_spec.rb +0 -11
  72. data/spec/unit/iterate_spec.rb +0 -79
  73. data/spec/unit/log_spec.rb +0 -29
  74. data/spec/unit/meter_spec.rb +0 -70
  75. data/spec/unit/multi/advance_spec.rb +0 -123
  76. data/spec/unit/multi/events_spec.rb +0 -115
  77. data/spec/unit/multi/finish_spec.rb +0 -41
  78. data/spec/unit/multi/line_inset_spec.rb +0 -65
  79. data/spec/unit/multi/register_spec.rb +0 -35
  80. data/spec/unit/multi/reset_spec.rb +0 -28
  81. data/spec/unit/multi/stop_spec.rb +0 -15
  82. data/spec/unit/multi/width_spec.rb +0 -118
  83. data/spec/unit/new_spec.rb +0 -76
  84. data/spec/unit/pipeline_spec.rb +0 -19
  85. data/spec/unit/ratio_spec.rb +0 -31
  86. data/spec/unit/render_spec.rb +0 -25
  87. data/spec/unit/reset_spec.rb +0 -31
  88. data/spec/unit/resize_spec.rb +0 -35
  89. data/spec/unit/set_current_spec.rb +0 -43
  90. data/spec/unit/start_spec.rb +0 -14
  91. data/spec/unit/stop_spec.rb +0 -19
  92. data/spec/unit/update_spec.rb +0 -22
  93. data/spec/unit/width_spec.rb +0 -86
  94. data/tasks/console.rake +0 -9
  95. data/tasks/coverage.rake +0 -9
  96. data/tasks/spec.rake +0 -27
  97. data/tty-progressbar.gemspec +0 -32
@@ -1,2 +1,2 @@
1
- require_relative 'tty/progressbar'
2
- require_relative 'tty/progressbar/multi'
1
+ require_relative "tty/progressbar"
2
+ require_relative "tty/progressbar/multi"
@@ -1,17 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'io/console'
4
- require 'forwardable'
5
- require 'monitor'
6
- require 'tty-cursor'
7
- require 'tty-screen'
8
- require 'strings-ansi'
9
- require 'unicode/display_width'
10
-
11
- require_relative 'progressbar/configuration'
12
- require_relative 'progressbar/formatter'
13
- require_relative 'progressbar/meter'
14
- require_relative 'progressbar/version'
3
+ require "io/console"
4
+ require "forwardable"
5
+ require "monitor"
6
+ require "tty-cursor"
7
+ require "tty-screen"
8
+ require "strings-ansi"
9
+ require "unicode/display_width"
10
+
11
+ require_relative "progressbar/timer"
12
+ require_relative "progressbar/configuration"
13
+ require_relative "progressbar/formatters"
14
+ require_relative "progressbar/meter"
15
+ require_relative "progressbar/version"
15
16
 
16
17
  module TTY
17
18
  # Used for creating terminal progress bar
@@ -21,7 +22,9 @@ module TTY
21
22
  extend Forwardable
22
23
  include MonitorMixin
23
24
 
24
- ECMA_CSI = "\e[".freeze
25
+ ECMA_CSI = "\e["
26
+
27
+ NEWLINE = "\n"
25
28
 
26
29
  CURSOR_LOCK = Monitor.new
27
30
 
@@ -29,17 +32,15 @@ module TTY
29
32
 
30
33
  attr_reader :current
31
34
 
32
- attr_reader :start_at
33
-
34
35
  attr_reader :row
35
36
 
36
- def_delegators :@configuration, :total, :width, :no_width,
37
- :complete, :incomplete, :head, :hide_cursor, :clear,
38
- :output, :frequency, :interval, :inset, :width=
37
+ def_delegators :@configuration, :total, :width, :complete, :incomplete,
38
+ :head, :clear_head, :hide_cursor, :clear, :output,
39
+ :frequency, :interval, :inset, :width=, :unknown, :bar_format
39
40
 
40
41
  def_delegators :@meter, :rate, :mean_rate
41
42
 
42
- def_delegator :@formatter, :use
43
+ def_delegators :@timer, :elapsed_time, :start_time
43
44
 
44
45
  # Determine terminal width
45
46
  #
@@ -64,6 +65,12 @@ module TTY
64
65
 
65
66
  # Create progress bar
66
67
  #
68
+ # @example
69
+ # bar = TTY::Progressbar.new
70
+ # bar.configure do |config|
71
+ # config.total = 20
72
+ # end
73
+ #
67
74
  # @param [String] format
68
75
  # the tokenized string that displays the output
69
76
  #
@@ -71,20 +78,29 @@ module TTY
71
78
  # @option options [Numeric] :total
72
79
  # the total number of steps to completion
73
80
  # @option options [Numeric] :width
74
- # the maximum width for the bars display including
75
- # all formatting options
76
- # @option options [Boolean] :no_width
77
- # true when progression is unknown defaulting to false
78
- # @option options [Boolean] :clear
79
- # whether or not to clear the progress line
80
- # @option options [Boolean] :hide_cursor
81
- # display or hide cursor
81
+ # the maximum width for the progress bar except all formatting tokens
82
+ # @option option [String] :complete
83
+ # the complete character in progress animation
84
+ # @option options [String] :incomplete
85
+ # the incomplete character in progress animation
86
+ # @option options [String] :head
87
+ # the head character, defaults to complete
88
+ # @option options [String] :unknown
89
+ # the unknown character for indeterminate progress animation
90
+ # @option options [Boolean] :bar_format
91
+ # the preconfigured bar format name, defaults to :classic
82
92
  # @option options [Object] :output
83
- # the object that responds to print call defaulting to stderr
93
+ # the object that responds to print call, defaults to stderr
84
94
  # @option options [Number] :frequency
85
- # the frequency with which to display bars
95
+ # the frequency with which to display a progress bar per second
86
96
  # @option options [Number] :interval
87
- # the period for sampling of speed measurement
97
+ # the time interval for sampling of speed measurement, defaults to 1 second
98
+ # @option options [Boolean] :hide_cursor
99
+ # whether or not to hide the cursor, defaults to false
100
+ # @option options [Boolean] :clear
101
+ # whether or not to clear the progress line, defaults to false
102
+ # @option options [Boolean] :clear_head
103
+ # whether or not to replace head character with complete, defaults to false
88
104
  #
89
105
  # @api public
90
106
  def initialize(format, options = {})
@@ -97,34 +113,52 @@ module TTY
97
113
  @configuration = TTY::ProgressBar::Configuration.new(options)
98
114
  yield @configuration if block_given?
99
115
 
100
- @formatter = TTY::ProgressBar::Formatter.new
101
- @meter = TTY::ProgressBar::Meter.new(interval)
116
+ @formatters = TTY::ProgressBar::Formatters.new
117
+ @meter = TTY::ProgressBar::Meter.new(interval)
118
+ @timer = TTY::ProgressBar::Timer.new
102
119
  @callbacks = Hash.new { |h, k| h[k] = [] }
103
120
 
104
- @formatter.load
121
+ @formatters.load(self)
105
122
  reset
106
123
 
107
124
  @first_render = true
108
- @multibar = nil
109
- @row = nil
125
+ @multibar = nil
126
+ @row = nil
110
127
  end
111
128
 
112
129
  # Reset progress to default configuration
113
130
  #
114
131
  # @api public
115
132
  def reset
116
- @width = 0 if no_width
133
+ @width = 0 if indeterminate?
117
134
  @render_period = frequency == 0 ? 0 : 1.0 / frequency
118
135
  @current = 0
136
+ @unknown = 0
119
137
  @last_render_time = Time.now
120
138
  @last_render_width = 0
121
139
  @done = false
122
140
  @stopped = false
123
- @start_at = Time.now
124
- @started = false
141
+ @paused = false
125
142
  @tokens = {}
126
143
 
127
144
  @meter.clear
145
+ @timer.reset
146
+ end
147
+
148
+ # Access instance configuration
149
+ #
150
+ # @api public
151
+ def configure
152
+ yield @configuration
153
+ end
154
+
155
+ # Check if progress can be determined or not
156
+ #
157
+ # @return [Boolean]
158
+ #
159
+ # @api public
160
+ def indeterminate?
161
+ total.nil?
128
162
  end
129
163
 
130
164
  # Attach this bar to multi bar
@@ -137,13 +171,26 @@ module TTY
137
171
  @multibar = multibar
138
172
  end
139
173
 
174
+ # Use custom token formatter
175
+ #
176
+ # @param [Object] formatter_class
177
+ # the formatter class to add to formatting pipeline
178
+ #
179
+ # @api public
180
+ def use(formatter_class)
181
+ unless formatter_class.is_a?(Class)
182
+ raise ArgumentError, "Formatter needs to be a class"
183
+ end
184
+
185
+ @formatters.use(formatter_class.new(self))
186
+ end
187
+
140
188
  # Start progression by drawing bar and setting time
141
189
  #
142
190
  # @api public
143
191
  def start
144
192
  synchronize do
145
- @started = true
146
- @start_at = Time.now
193
+ @timer.start
147
194
  @meter.start
148
195
  end
149
196
 
@@ -163,17 +210,21 @@ module TTY
163
210
  if progress.respond_to?(:to_hash)
164
211
  tokens, progress = progress, 1
165
212
  end
166
- @start_at = Time.now if @current.zero? && !@started
167
- @current += progress
168
- @tokens = tokens
213
+ @timer.start
214
+ @current += progress
215
+ # When progress is unknown increase by 2% up to max 200%, after
216
+ # that reset back to 0%
217
+ @unknown += 2 if indeterminate?
218
+ @unknown = 0 if @unknown > 199
219
+ @tokens = tokens
169
220
  @meter.sample(Time.now, progress)
170
221
 
171
- if !no_width && @current >= total
222
+ if !indeterminate? && @current >= total
172
223
  finish && return
173
224
  end
174
225
 
175
- now = Time.now
176
- return if (now - @last_render_time) < @render_period
226
+ return if (Time.now - @last_render_time) < @render_period
227
+
177
228
  render
178
229
  end
179
230
  end
@@ -190,7 +241,7 @@ module TTY
190
241
  # be convenient in "unsure number of iterations" situations
191
242
  # (like downloading in chunks, when server may eventually send
192
243
  # more chunks than predicted), but be careful to not pass infinite
193
- # enumerators without previosly doing `.take(some_finite_number)`
244
+ # enumerators without previously doing `.take(some_finite_number)`
194
245
  # on them.
195
246
  #
196
247
  # @example
@@ -257,12 +308,20 @@ module TTY
257
308
 
258
309
  # Ratio of completed over total steps
259
310
  #
311
+ # When the total is unknown the progress ratio oscillates
312
+ # by going up from 0 to 1 and then down from 1 to 0 and
313
+ # up again to infinity.
314
+ #
260
315
  # @return [Float]
261
316
  #
262
317
  # @api public
263
318
  def ratio
264
319
  synchronize do
265
- proportion = total > 0 ? (@current.to_f / total) : 0
320
+ proportion = if total
321
+ total > 0 ? (@current.to_f / total) : 0
322
+ else
323
+ (@unknown > 100 ? 200 - @unknown : @unknown).to_f / 100
324
+ end
266
325
  [[proportion, 0].max, 1].min
267
326
  end
268
327
  end
@@ -272,7 +331,9 @@ module TTY
272
331
  # @api private
273
332
  def render
274
333
  return if done?
275
- if hide_cursor && @last_render_width == 0 && !(@current >= total)
334
+
335
+ if hide_cursor && @last_render_width == 0 &&
336
+ (indeterminate? || @current < total)
276
337
  write(TTY::Cursor.hide)
277
338
  end
278
339
 
@@ -281,7 +342,7 @@ module TTY
281
342
  update(inset: self.class.display_columns(characters_in))
282
343
  end
283
344
 
284
- formatted = @formatter.decorate(self, @format)
345
+ formatted = @formatters.decorate(@format)
285
346
  @tokens.each do |token, val|
286
347
  formatted = formatted.gsub(":#{token}", val)
287
348
  end
@@ -304,7 +365,7 @@ module TTY
304
365
  if @first_render
305
366
  @row = @multibar.next_row
306
367
  yield if block_given?
307
- output.print "\n"
368
+ output.print NEWLINE
308
369
  @first_render = false
309
370
  else
310
371
  lines_up = (@multibar.rows + 1) - @row
@@ -343,6 +404,7 @@ module TTY
343
404
  # @api public
344
405
  def resize(new_width = nil)
345
406
  return if done?
407
+
346
408
  synchronize do
347
409
  clear_line
348
410
  if new_width
@@ -356,12 +418,14 @@ module TTY
356
418
  # @api public
357
419
  def finish
358
420
  return if done?
359
- @current = total unless no_width
421
+
422
+ @current = total unless indeterminate?
360
423
  render
361
- clear ? clear_line : write("\n", false)
424
+ clear ? clear_line : write(NEWLINE, false)
362
425
  ensure
363
426
  @meter.clear
364
427
  @done = true
428
+ @timer.stop
365
429
 
366
430
  # reenable cursor if it is turned off
367
431
  if hide_cursor && @last_render_width != 0
@@ -371,31 +435,57 @@ module TTY
371
435
  emit(:done)
372
436
  end
373
437
 
438
+ # Resume rendering when bar is done, stopped or paused
439
+ #
440
+ # @api public
441
+ def resume
442
+ synchronize do
443
+ @done = false
444
+ @stopped = false
445
+ @paused = false
446
+ end
447
+ end
448
+
374
449
  # Stop and cancel the progress at the current position
375
450
  #
376
451
  # @api public
377
452
  def stop
378
- # reenable cursor if it is turned off
379
- if hide_cursor && @last_render_width != 0
380
- write(TTY::Cursor.show, false)
381
- end
382
453
  return if done?
454
+
383
455
  render
384
- clear ? clear_line : write("\n", false)
456
+ clear ? clear_line : write(NEWLINE, false)
385
457
  ensure
386
458
  @meter.clear
387
459
  @stopped = true
460
+ @timer.stop
461
+
462
+ # reenable cursor if it is turned off
463
+ if hide_cursor && @last_render_width != 0
464
+ write(TTY::Cursor.show, false)
465
+ end
466
+
388
467
  emit(:stopped)
389
468
  end
390
469
 
470
+ # Pause the progress at the current position
471
+ #
472
+ # @api public
473
+ def pause
474
+ synchronize do
475
+ @paused = true
476
+ @timer.stop
477
+ emit(:paused)
478
+ end
479
+ end
480
+
391
481
  # Clear current line
392
482
  #
393
483
  # @api public
394
484
  def clear_line
395
- output.print(ECMA_CSI + '0m' + TTY::Cursor.clear_line)
485
+ output.print("#{ECMA_CSI}0m#{TTY::Cursor.clear_line}")
396
486
  end
397
487
 
398
- # Check if progress is finised
488
+ # Check if progress is finished
399
489
  #
400
490
  # @return [Boolean]
401
491
  # true when progress finished, false otherwise
@@ -414,13 +504,22 @@ module TTY
414
504
  @stopped
415
505
  end
416
506
 
417
- # Check if progress is finished or stopped
507
+ # Check if progress is paused
508
+ #
509
+ # @return [Boolean]
510
+ #
511
+ # @api public
512
+ def paused?
513
+ @paused
514
+ end
515
+
516
+ # Check if progress is finished, stopped or paused
418
517
  #
419
518
  # @return [Boolean]
420
519
  #
421
520
  # @api public
422
521
  def done?
423
- @done || @stopped
522
+ @done || @stopped || @paused
424
523
  end
425
524
 
426
525
  # Register callback with this bar
@@ -445,14 +544,14 @@ module TTY
445
544
  #
446
545
  # @api public
447
546
  def log(message)
448
- sanitized_message = message.gsub(/\r|\n/, ' ')
547
+ sanitized_message = message.gsub(/\r|\n/, " ")
449
548
  if done?
450
- write(sanitized_message + "\n", false)
549
+ write("#{sanitized_message}#{NEWLINE}", false)
451
550
  return
452
551
  end
453
552
  sanitized_message = padout(sanitized_message)
454
553
 
455
- write(sanitized_message + "\n", true)
554
+ write("#{sanitized_message}#{NEWLINE}", true)
456
555
  render
457
556
  end
458
557
 
@@ -472,13 +571,14 @@ module TTY
472
571
  # @api public
473
572
  def inspect
474
573
  "#<#{self.class.name} " \
475
- "@format=\"#{format}\", " \
574
+ "@format=\"#{@format}\", " \
476
575
  "@current=\"#{@current}\", " \
477
576
  "@total=\"#{total}\", " \
478
577
  "@width=\"#{width}\", " \
479
578
  "@complete=\"#{complete}\", " \
480
579
  "@head=\"#{head}\", " \
481
580
  "@incomplete=\"#{incomplete}\", " \
581
+ "@unknown=\"#{unknown}\", " \
482
582
  "@interval=\"#{interval}\">"
483
583
  end
484
584
 
@@ -492,7 +592,7 @@ module TTY
492
592
 
493
593
  if @last_render_width > message_length
494
594
  remaining_width = @last_render_width - message_length
495
- message += ' ' * remaining_width
595
+ message += " " * remaining_width
496
596
  end
497
597
  message
498
598
  end
@@ -505,7 +605,7 @@ module TTY
505
605
  # @api private
506
606
  def emit(name, *args)
507
607
  @callbacks[name].each do |callback|
508
- callback.call(*args)
608
+ callback.(*args)
509
609
  end
510
610
  end
511
611