tty-progressbar 0.17.0 → 0.18.0

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