tty-progressbar 0.15.1 → 0.18.2

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