tty-progressbar 0.15.1 → 0.18.2

Sign up to get free protection for your applications and to get access to all the features.
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