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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +63 -0
- data/LICENSE.txt +1 -1
- data/README.md +495 -125
- data/lib/tty-progressbar.rb +2 -2
- data/lib/tty/progressbar.rb +169 -68
- data/lib/tty/progressbar/configuration.rb +121 -27
- data/lib/tty/progressbar/converter.rb +16 -19
- data/lib/tty/progressbar/formats.rb +120 -0
- data/lib/tty/progressbar/formatter.rb +33 -38
- data/lib/tty/progressbar/formatter/bar.rb +74 -27
- data/lib/tty/progressbar/formatter/byte_rate.rb +6 -20
- data/lib/tty/progressbar/formatter/current.rb +4 -19
- data/lib/tty/progressbar/formatter/current_byte.rb +9 -17
- data/lib/tty/progressbar/formatter/elapsed.rb +9 -18
- data/lib/tty/progressbar/formatter/estimated.rb +18 -20
- data/lib/tty/progressbar/formatter/estimated_time.rb +39 -0
- data/lib/tty/progressbar/formatter/mean_byte.rb +6 -20
- data/lib/tty/progressbar/formatter/mean_rate.rb +6 -20
- data/lib/tty/progressbar/formatter/percent.rb +10 -16
- data/lib/tty/progressbar/formatter/rate.rb +5 -19
- data/lib/tty/progressbar/formatter/total.rb +10 -16
- data/lib/tty/progressbar/formatter/total_byte.rb +14 -18
- data/lib/tty/progressbar/formatters.rb +53 -0
- data/lib/tty/progressbar/meter.rb +2 -2
- data/lib/tty/progressbar/multi.rb +61 -21
- data/lib/tty/progressbar/pipeline.rb +13 -6
- data/lib/tty/progressbar/timer.rb +89 -0
- data/lib/tty/progressbar/version.rb +3 -1
- metadata +56 -164
- data/.codeclimate.yml +0 -11
- data/.gitignore +0 -14
- data/.rspec +0 -3
- data/.travis.yml +0 -26
- data/CODE_OF_CONDUCT.md +0 -74
- data/Gemfile +0 -14
- data/Rakefile +0 -8
- data/appveyor.yml +0 -23
- data/examples/color.rb +0 -18
- data/examples/failure.rb +0 -12
- data/examples/iterator.rb +0 -5
- data/examples/lazy.rb +0 -6
- data/examples/multi/main_bar.rb +0 -13
- data/examples/multi/simple.rb +0 -13
- data/examples/multi/width.rb +0 -13
- data/examples/simple.rb +0 -7
- data/examples/slow_process.rb +0 -29
- data/examples/speed.rb +0 -11
- data/examples/threaded.rb +0 -14
- data/examples/tokens.rb +0 -12
- data/examples/unicode.rb +0 -7
- data/spec/spec_helper.rb +0 -51
- data/spec/unit/advance_spec.rb +0 -25
- data/spec/unit/clear_spec.rb +0 -17
- data/spec/unit/complete_spec.rb +0 -16
- data/spec/unit/converter/to_bytes_spec.rb +0 -47
- data/spec/unit/converter/to_seconds_spec.rb +0 -15
- data/spec/unit/converter/to_time_spec.rb +0 -19
- data/spec/unit/custom_formatter_spec.rb +0 -26
- data/spec/unit/custom_token_spec.rb +0 -14
- data/spec/unit/events_spec.rb +0 -33
- data/spec/unit/finish_spec.rb +0 -15
- data/spec/unit/formatter/bar_spec.rb +0 -16
- data/spec/unit/formatter/byte_rate_spec.rb +0 -32
- data/spec/unit/formatter/current_byte_spec.rb +0 -16
- data/spec/unit/formatter/current_spec.rb +0 -14
- data/spec/unit/formatter/elapsed_spec.rb +0 -58
- data/spec/unit/formatter/estimated_spec.rb +0 -27
- data/spec/unit/formatter/mean_byte_spec.rb +0 -32
- data/spec/unit/formatter/mean_rate_spec.rb +0 -31
- data/spec/unit/formatter/percent_spec.rb +0 -16
- data/spec/unit/formatter/rate_spec.rb +0 -31
- data/spec/unit/formatter/total_byte_spec.rb +0 -16
- data/spec/unit/formatter/total_spec.rb +0 -16
- data/spec/unit/frequency_spec.rb +0 -27
- data/spec/unit/head_spec.rb +0 -32
- data/spec/unit/hide_cursor_spec.rb +0 -27
- data/spec/unit/inspect_spec.rb +0 -11
- data/spec/unit/iterate_spec.rb +0 -79
- data/spec/unit/log_spec.rb +0 -29
- data/spec/unit/meter_spec.rb +0 -70
- data/spec/unit/multi/advance_spec.rb +0 -123
- data/spec/unit/multi/events_spec.rb +0 -115
- data/spec/unit/multi/finish_spec.rb +0 -41
- data/spec/unit/multi/line_inset_spec.rb +0 -65
- data/spec/unit/multi/register_spec.rb +0 -35
- data/spec/unit/multi/reset_spec.rb +0 -28
- data/spec/unit/multi/stop_spec.rb +0 -15
- data/spec/unit/multi/width_spec.rb +0 -118
- data/spec/unit/new_spec.rb +0 -76
- data/spec/unit/pipeline_spec.rb +0 -19
- data/spec/unit/ratio_spec.rb +0 -31
- data/spec/unit/render_spec.rb +0 -25
- data/spec/unit/reset_spec.rb +0 -31
- data/spec/unit/resize_spec.rb +0 -35
- data/spec/unit/set_current_spec.rb +0 -43
- data/spec/unit/start_spec.rb +0 -14
- data/spec/unit/stop_spec.rb +0 -19
- data/spec/unit/update_spec.rb +0 -22
- data/spec/unit/width_spec.rb +0 -86
- data/tasks/console.rake +0 -9
- data/tasks/coverage.rake +0 -9
- data/tasks/spec.rake +0 -27
- data/tty-progressbar.gemspec +0 -30
data/lib/tty-progressbar.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
require_relative
|
2
|
-
require_relative
|
1
|
+
require_relative "tty/progressbar"
|
2
|
+
require_relative "tty/progressbar/multi"
|
data/lib/tty/progressbar.rb
CHANGED
@@ -1,16 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
|
10
|
-
|
11
|
-
require_relative
|
12
|
-
require_relative
|
13
|
-
require_relative
|
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["
|
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, :
|
36
|
-
:
|
37
|
-
:
|
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
|
-
|
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
|
74
|
-
#
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
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
|
93
|
+
# the object that responds to print call, defaults to stderr
|
83
94
|
# @option options [Number] :frequency
|
84
|
-
# the frequency with which to display
|
95
|
+
# the frequency with which to display a progress bar per second
|
85
96
|
# @option options [Number] :interval
|
86
|
-
# the
|
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
|
-
@
|
100
|
-
@meter
|
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
|
-
@
|
121
|
+
@formatters.load(self)
|
104
122
|
reset
|
105
123
|
|
106
124
|
@first_render = true
|
107
|
-
@multibar
|
108
|
-
@row
|
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
|
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
|
-
@
|
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
|
-
@
|
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
|
-
@
|
166
|
-
@current
|
167
|
-
|
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 !
|
222
|
+
if !indeterminate? && @current >= total
|
171
223
|
finish && return
|
172
224
|
end
|
173
225
|
|
174
|
-
|
175
|
-
|
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
|
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 =
|
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
|
-
|
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 = @
|
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
|
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
|
-
|
421
|
+
|
422
|
+
@current = total unless indeterminate?
|
359
423
|
render
|
360
|
-
clear ? clear_line : write(
|
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(
|
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
|
485
|
+
output.print("#{ECMA_CSI}0m#{TTY::Cursor.clear_line}")
|
395
486
|
end
|
396
487
|
|
397
|
-
# Check if progress is
|
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
|
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
|
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
|
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 +=
|
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.
|
608
|
+
callback.(*args)
|
508
609
|
end
|
509
610
|
end
|
510
611
|
|