tty-spinner 0.8.0 → 0.9.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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +120 -0
- data/README.md +28 -10
- data/Rakefile +8 -0
- data/examples/auto_spin.rb +8 -0
- data/examples/basic.rb +8 -0
- data/examples/clear.rb +9 -0
- data/examples/color.rb +12 -0
- data/examples/error.rb +9 -0
- data/examples/formats.rb +11 -0
- data/examples/hide_cursor.rb +12 -0
- data/examples/multi/basic.rb +13 -0
- data/examples/multi/basic_top_level.rb +13 -0
- data/examples/multi/custom_style.rb +26 -0
- data/examples/multi/files.rb +14 -0
- data/examples/multi/jobs.rb +10 -0
- data/examples/multi/multi.rb +17 -0
- data/examples/multi/multi_top_level.rb +18 -0
- data/examples/multi/pause.rb +26 -0
- data/examples/multi/threaded.rb +30 -0
- data/examples/pause.rb +19 -0
- data/examples/run.rb +18 -0
- data/examples/success.rb +9 -0
- data/examples/threaded.rb +11 -0
- data/examples/update.rb +11 -0
- data/lib/tty-spinner.rb +0 -2
- data/lib/tty/spinner.rb +40 -30
- data/lib/tty/spinner/formats.rb +3 -3
- data/lib/tty/spinner/multi.rb +31 -12
- data/lib/tty/spinner/version.rb +2 -2
- data/spec/spec_helper.rb +51 -0
- data/spec/unit/auto_spin_spec.rb +27 -0
- data/spec/unit/clear_spec.rb +18 -0
- data/spec/unit/error_spec.rb +46 -0
- data/spec/unit/events_spec.rb +37 -0
- data/spec/unit/formats_spec.rb +9 -0
- data/spec/unit/frames_spec.rb +33 -0
- data/spec/unit/hide_cursor_spec.rb +53 -0
- data/spec/unit/job_spec.rb +14 -0
- data/spec/unit/join_spec.rb +12 -0
- data/spec/unit/multi/auto_spin_spec.rb +34 -0
- data/spec/unit/multi/error_spec.rb +109 -0
- data/spec/unit/multi/line_inset_spec.rb +59 -0
- data/spec/unit/multi/on_spec.rb +13 -0
- data/spec/unit/multi/register_spec.rb +47 -0
- data/spec/unit/multi/spin_spec.rb +103 -0
- data/spec/unit/multi/stop_spec.rb +97 -0
- data/spec/unit/multi/success_spec.rb +110 -0
- data/spec/unit/new_spec.rb +26 -0
- data/spec/unit/pause_spec.rb +25 -0
- data/spec/unit/reset_spec.rb +21 -0
- data/spec/unit/run_spec.rb +32 -0
- data/spec/unit/spin_spec.rb +90 -0
- data/spec/unit/stop_spec.rb +64 -0
- data/spec/unit/success_spec.rb +46 -0
- data/spec/unit/update_spec.rb +87 -0
- data/tasks/console.rake +11 -0
- data/tasks/coverage.rake +11 -0
- data/tasks/spec.rake +29 -0
- data/tty-spinner.gemspec +27 -0
- metadata +62 -9
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative '../../lib/tty-spinner'
|
2
|
+
|
3
|
+
spinners = TTY::Spinner::Multi.new("[:spinner]")
|
4
|
+
|
5
|
+
sp1 = spinners.register("[:spinner] one")
|
6
|
+
sp2 = spinners.register("[:spinner] two")
|
7
|
+
sp3 = spinners.register("[:spinner] three")
|
8
|
+
|
9
|
+
sp1.auto_spin
|
10
|
+
sp2.auto_spin
|
11
|
+
sp3.auto_spin
|
12
|
+
|
13
|
+
sleep(1)
|
14
|
+
|
15
|
+
spinners.pause
|
16
|
+
|
17
|
+
sleep(1)
|
18
|
+
|
19
|
+
spinners.resume
|
20
|
+
|
21
|
+
sleep(1)
|
22
|
+
|
23
|
+
sp1.stop
|
24
|
+
sp2.stop
|
25
|
+
sp3.stop
|
26
|
+
spinners.stop
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../lib/tty-spinner'
|
4
|
+
|
5
|
+
def spinner_options
|
6
|
+
[
|
7
|
+
":spinner \e[1mNo\e[0m :number Row :line",
|
8
|
+
format: :dots,
|
9
|
+
error_mark: '✖',
|
10
|
+
success_mark: "\e[1m\e[32m✓\e[0m\e[0m"
|
11
|
+
]
|
12
|
+
end
|
13
|
+
|
14
|
+
spinners = TTY::Spinner::Multi.new(*spinner_options)
|
15
|
+
threads = []
|
16
|
+
|
17
|
+
20.times do |i|
|
18
|
+
threads << Thread.new do
|
19
|
+
spinner = spinners.register(*spinner_options)
|
20
|
+
sleep Random.rand(0.1..0.3)
|
21
|
+
|
22
|
+
10.times do
|
23
|
+
sleep Random.rand(0.1..0.3)
|
24
|
+
spinner.update(number: "(#{i})", line: spinner.row)
|
25
|
+
spinner.spin
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
threads.each(&:join)
|
data/examples/pause.rb
ADDED
data/examples/run.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative '../lib/tty-spinner'
|
2
|
+
|
3
|
+
# without block
|
4
|
+
spinner = TTY::Spinner.new(":title :spinner ...", format: :pulse_3)
|
5
|
+
|
6
|
+
def long_task
|
7
|
+
10000000.times do |n|
|
8
|
+
n * n
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
spinner.update(title: 'Task 1')
|
13
|
+
spinner.run 'done' do
|
14
|
+
long_task
|
15
|
+
end
|
16
|
+
|
17
|
+
spinner.update(title: 'Task 2')
|
18
|
+
spinner.run('done') { long_task }
|
data/examples/success.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require_relative '../lib/tty-spinner'
|
2
|
+
|
3
|
+
spinner = TTY::Spinner.new("[:spinner]")
|
4
|
+
|
5
|
+
th1 = Thread.new { 10.times { spinner.spin; sleep(0.1) } }
|
6
|
+
th2 = Thread.new { 10.times { spinner.spin; sleep(0.1) } }
|
7
|
+
th3 = Thread.new { 10.times { spinner.spin; sleep(0.1) } }
|
8
|
+
|
9
|
+
[th1, th2, th3].each(&:join)
|
10
|
+
|
11
|
+
spinner.success
|
data/examples/update.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require_relative '../lib/tty-spinner'
|
2
|
+
|
3
|
+
spinner = TTY::Spinner.new(":spinner :title", format: :pulse_3)
|
4
|
+
|
5
|
+
spinner.update(title: 'task aaaaa')
|
6
|
+
|
7
|
+
20.times { spinner.spin; sleep(0.1) }
|
8
|
+
|
9
|
+
spinner.update(title: 'task b')
|
10
|
+
|
11
|
+
20.times { spinner.spin; sleep(0.1) }
|
data/lib/tty-spinner.rb
CHANGED
data/lib/tty/spinner.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
3
|
require 'monitor'
|
@@ -18,11 +17,11 @@ module TTY
|
|
18
17
|
# @raised when attempting to join dead thread
|
19
18
|
NotSpinningError = Class.new(StandardError)
|
20
19
|
|
21
|
-
ECMA_CSI = "\x1b["
|
20
|
+
ECMA_CSI = "\x1b["
|
22
21
|
|
23
22
|
MATCHER = /:spinner/
|
24
|
-
TICK = '✔'
|
25
|
-
CROSS = '✖'
|
23
|
+
TICK = '✔'
|
24
|
+
CROSS = '✖'
|
26
25
|
|
27
26
|
CURSOR_LOCK = Monitor.new
|
28
27
|
|
@@ -61,8 +60,16 @@ module TTY
|
|
61
60
|
# @api public
|
62
61
|
attr_reader :tokens
|
63
62
|
|
63
|
+
# The amount of time between frames in auto spinning
|
64
|
+
#
|
65
|
+
# @api public
|
64
66
|
attr_reader :interval
|
65
67
|
|
68
|
+
# The current row inside the multi spinner
|
69
|
+
#
|
70
|
+
# @api public
|
71
|
+
attr_reader :row
|
72
|
+
|
66
73
|
# Initialize a spinner
|
67
74
|
#
|
68
75
|
# @example
|
@@ -106,14 +113,23 @@ module TTY
|
|
106
113
|
|
107
114
|
@callbacks = Hash.new { |h, k| h[k] = [] }
|
108
115
|
@length = @frames.length
|
109
|
-
@current = 0
|
110
|
-
@done = false
|
111
|
-
@state = :stopped
|
112
116
|
@thread = nil
|
113
117
|
@job = nil
|
114
118
|
@multispinner= nil
|
115
|
-
|
116
|
-
|
119
|
+
reset
|
120
|
+
end
|
121
|
+
|
122
|
+
# Reset the spinner to initial frame
|
123
|
+
#
|
124
|
+
# @api public
|
125
|
+
def reset
|
126
|
+
synchronize do
|
127
|
+
@current = 0
|
128
|
+
@done = false
|
129
|
+
@state = :stopped
|
130
|
+
@succeeded = false
|
131
|
+
@first_run = true
|
132
|
+
end
|
117
133
|
end
|
118
134
|
|
119
135
|
# Notifies the TTY::Spinner that it is running under a multispinner
|
@@ -202,13 +218,11 @@ module TTY
|
|
202
218
|
|
203
219
|
# Execute this spinner job
|
204
220
|
#
|
221
|
+
# @yield [TTY::Spinner]
|
222
|
+
#
|
205
223
|
# @api public
|
206
224
|
def execute_job
|
207
|
-
if job
|
208
|
-
instance_eval(&job)
|
209
|
-
else
|
210
|
-
job.(self)
|
211
|
-
end
|
225
|
+
job.(self) if job?
|
212
226
|
end
|
213
227
|
|
214
228
|
# Check if this spinner has a scheduled job
|
@@ -241,6 +255,10 @@ module TTY
|
|
241
255
|
end
|
242
256
|
end
|
243
257
|
end
|
258
|
+
ensure
|
259
|
+
if @hide_cursor
|
260
|
+
write(TTY::Cursor.show, false)
|
261
|
+
end
|
244
262
|
end
|
245
263
|
|
246
264
|
# Checked if current spinner is paused
|
@@ -370,9 +388,6 @@ module TTY
|
|
370
388
|
mon_enter
|
371
389
|
return if done?
|
372
390
|
|
373
|
-
if @hide_cursor
|
374
|
-
write(TTY::Cursor.show, false)
|
375
|
-
end
|
376
391
|
clear_line
|
377
392
|
return if @clear
|
378
393
|
|
@@ -388,6 +403,11 @@ module TTY
|
|
388
403
|
@state = :stopped
|
389
404
|
@done = true
|
390
405
|
@started_at = nil
|
406
|
+
|
407
|
+
if @hide_cursor
|
408
|
+
write(TTY::Cursor.show, false)
|
409
|
+
end
|
410
|
+
|
391
411
|
emit(:done)
|
392
412
|
kill
|
393
413
|
mon_exit
|
@@ -454,16 +474,6 @@ module TTY
|
|
454
474
|
end
|
455
475
|
end
|
456
476
|
|
457
|
-
# Reset the spinner to initial frame
|
458
|
-
#
|
459
|
-
# @api public
|
460
|
-
def reset
|
461
|
-
synchronize do
|
462
|
-
@current = 0
|
463
|
-
@first_run = true
|
464
|
-
end
|
465
|
-
end
|
466
|
-
|
467
477
|
private
|
468
478
|
|
469
479
|
# Execute a block on the proper terminal line if the spinner is running
|
@@ -472,7 +482,7 @@ module TTY
|
|
472
482
|
# @api private
|
473
483
|
def execute_on_line
|
474
484
|
if @multispinner
|
475
|
-
|
485
|
+
@multispinner.synchronize do
|
476
486
|
if @first_run
|
477
487
|
@row ||= @multispinner.next_row
|
478
488
|
yield if block_given?
|
@@ -502,7 +512,7 @@ module TTY
|
|
502
512
|
execute_on_line do
|
503
513
|
output.print(TTY::Cursor.column(1)) if clear_first
|
504
514
|
# If there's a top level spinner, print with inset
|
505
|
-
characters_in = @multispinner.line_inset(
|
515
|
+
characters_in = @multispinner.line_inset(@row) if @multispinner
|
506
516
|
output.print("#{characters_in}#{data}")
|
507
517
|
output.flush
|
508
518
|
end
|
@@ -553,7 +563,7 @@ module TTY
|
|
553
563
|
def replace_tokens(string)
|
554
564
|
data = string.dup
|
555
565
|
@tokens.each do |name, val|
|
556
|
-
data.gsub!(/\:#{name}/, val)
|
566
|
+
data.gsub!(/\:#{name}/, val.to_s)
|
557
567
|
end
|
558
568
|
data
|
559
569
|
end
|
data/lib/tty/spinner/formats.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module TTY
|
4
4
|
module Formats
|
@@ -21,7 +21,7 @@ module TTY
|
|
21
21
|
},
|
22
22
|
spin_4: {
|
23
23
|
interval: 10,
|
24
|
-
frames: %w{╫ ╪
|
24
|
+
frames: %w{╫ ╪}
|
25
25
|
},
|
26
26
|
pulse: {
|
27
27
|
interval: 10,
|
@@ -164,7 +164,7 @@ module TTY
|
|
164
164
|
},
|
165
165
|
flip: {
|
166
166
|
interval: 10,
|
167
|
-
frames: '-◡⊙-◠'
|
167
|
+
frames: '-◡⊙-◠'
|
168
168
|
},
|
169
169
|
burger: {
|
170
170
|
interval: 6,
|
data/lib/tty/spinner/multi.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
3
|
require 'monitor'
|
@@ -23,7 +22,7 @@ module TTY
|
|
23
22
|
top: Gem.win_platform? ? '+ ' : "\u250c ",
|
24
23
|
middle: Gem.win_platform? ? '|-- ' : "\u251c\u2500\u2500 ",
|
25
24
|
bottom: Gem.win_platform? ? '|__ ' : "\u2514\u2500\u2500 "
|
26
|
-
}
|
25
|
+
}
|
27
26
|
|
28
27
|
# The current count of all rendered rows
|
29
28
|
#
|
@@ -59,6 +58,7 @@ module TTY
|
|
59
58
|
@inset_opts = @options.delete(:style) { DEFAULT_INSET }
|
60
59
|
@rows = 0
|
61
60
|
@spinners = []
|
61
|
+
@spinners_count = 0
|
62
62
|
@top_spinner = nil
|
63
63
|
@last_spin_at = nil
|
64
64
|
unless message.nil?
|
@@ -75,19 +75,21 @@ module TTY
|
|
75
75
|
|
76
76
|
# Register a new spinner
|
77
77
|
#
|
78
|
-
# @param [String]
|
79
|
-
# the pattern used for creating spinner
|
78
|
+
# @param [String, TTY::Spinner] pattern_or_spinner
|
79
|
+
# the pattern used for creating spinner, or a spinner instance
|
80
80
|
#
|
81
81
|
# @api public
|
82
|
-
def register(
|
82
|
+
def register(pattern_or_spinner, **options, &job)
|
83
83
|
observable = options.delete(:observable) { true }
|
84
|
-
spinner =
|
84
|
+
spinner = nil
|
85
85
|
|
86
86
|
synchronize do
|
87
|
+
spinner = create_spinner(pattern_or_spinner, options)
|
87
88
|
spinner.attach_to(self)
|
88
89
|
spinner.job(&job) if block_given?
|
89
90
|
observe(spinner) if observable
|
90
91
|
@spinners << spinner
|
92
|
+
@spinners_count += 1
|
91
93
|
if @top_spinner
|
92
94
|
@spinners.each { |sp| sp.redraw_indent if sp.spinning? || sp.done? }
|
93
95
|
end
|
@@ -96,6 +98,24 @@ module TTY
|
|
96
98
|
spinner
|
97
99
|
end
|
98
100
|
|
101
|
+
# Create a spinner instance
|
102
|
+
#
|
103
|
+
# @api private
|
104
|
+
def create_spinner(pattern_or_spinner, options)
|
105
|
+
case pattern_or_spinner
|
106
|
+
when ::String
|
107
|
+
TTY::Spinner.new(
|
108
|
+
pattern_or_spinner,
|
109
|
+
@options.merge(options)
|
110
|
+
)
|
111
|
+
when ::TTY::Spinner
|
112
|
+
pattern_or_spinner
|
113
|
+
else
|
114
|
+
raise ArgumentError, "Expected a pattern or spinner, " \
|
115
|
+
"got: #{pattern_or_spinner.class}"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
99
119
|
# Increase a row count
|
100
120
|
#
|
101
121
|
# @api public
|
@@ -161,20 +181,19 @@ module TTY
|
|
161
181
|
# Find the number of characters to move into the line
|
162
182
|
# before printing the spinner
|
163
183
|
#
|
164
|
-
# @param [
|
165
|
-
# the spinner for which line inset is calculated
|
184
|
+
# @param [Integer] line_no
|
185
|
+
# the current spinner line number for which line inset is calculated
|
166
186
|
#
|
167
187
|
# @return [String]
|
168
188
|
# the inset
|
169
189
|
#
|
170
190
|
# @api public
|
171
|
-
def line_inset(
|
191
|
+
def line_inset(line_no)
|
172
192
|
return '' if @top_spinner.nil?
|
173
193
|
|
174
|
-
|
175
|
-
when @top_spinner
|
194
|
+
if line_no == 1
|
176
195
|
@inset_opts[:top]
|
177
|
-
|
196
|
+
elsif line_no == @spinners_count
|
178
197
|
@inset_opts[:bottom]
|
179
198
|
else
|
180
199
|
@inset_opts[:middle]
|
data/lib/tty/spinner/version.rb
CHANGED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if ENV['COVERAGE'] || ENV['TRAVIS']
|
4
|
+
require 'simplecov'
|
5
|
+
require 'coveralls'
|
6
|
+
|
7
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
8
|
+
SimpleCov::Formatter::HTMLFormatter,
|
9
|
+
Coveralls::SimpleCov::Formatter
|
10
|
+
]
|
11
|
+
|
12
|
+
SimpleCov.start do
|
13
|
+
command_name 'spec'
|
14
|
+
add_filter 'spec'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
require 'tty-spinner'
|
19
|
+
|
20
|
+
class StringIO
|
21
|
+
def tty?
|
22
|
+
true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
RSpec.configure do |config|
|
27
|
+
config.expect_with :rspec do |expectations|
|
28
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
29
|
+
end
|
30
|
+
|
31
|
+
config.mock_with :rspec do |mocks|
|
32
|
+
mocks.verify_partial_doubles = true
|
33
|
+
end
|
34
|
+
|
35
|
+
# Limits the available syntax to the non-monkey patched syntax that is recommended.
|
36
|
+
config.disable_monkey_patching!
|
37
|
+
|
38
|
+
# This setting enables warnings. It's recommended, but in some cases may
|
39
|
+
# be too noisy due to issues in dependencies.
|
40
|
+
config.warnings = true
|
41
|
+
|
42
|
+
if config.files_to_run.one?
|
43
|
+
config.default_formatter = 'doc'
|
44
|
+
end
|
45
|
+
|
46
|
+
config.profile_examples = 2
|
47
|
+
|
48
|
+
config.order = :random
|
49
|
+
|
50
|
+
Kernel.srand config.seed
|
51
|
+
end
|