tty-spinner 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +120 -0
  3. data/README.md +28 -10
  4. data/Rakefile +8 -0
  5. data/examples/auto_spin.rb +8 -0
  6. data/examples/basic.rb +8 -0
  7. data/examples/clear.rb +9 -0
  8. data/examples/color.rb +12 -0
  9. data/examples/error.rb +9 -0
  10. data/examples/formats.rb +11 -0
  11. data/examples/hide_cursor.rb +12 -0
  12. data/examples/multi/basic.rb +13 -0
  13. data/examples/multi/basic_top_level.rb +13 -0
  14. data/examples/multi/custom_style.rb +26 -0
  15. data/examples/multi/files.rb +14 -0
  16. data/examples/multi/jobs.rb +10 -0
  17. data/examples/multi/multi.rb +17 -0
  18. data/examples/multi/multi_top_level.rb +18 -0
  19. data/examples/multi/pause.rb +26 -0
  20. data/examples/multi/threaded.rb +30 -0
  21. data/examples/pause.rb +19 -0
  22. data/examples/run.rb +18 -0
  23. data/examples/success.rb +9 -0
  24. data/examples/threaded.rb +11 -0
  25. data/examples/update.rb +11 -0
  26. data/lib/tty-spinner.rb +0 -2
  27. data/lib/tty/spinner.rb +40 -30
  28. data/lib/tty/spinner/formats.rb +3 -3
  29. data/lib/tty/spinner/multi.rb +31 -12
  30. data/lib/tty/spinner/version.rb +2 -2
  31. data/spec/spec_helper.rb +51 -0
  32. data/spec/unit/auto_spin_spec.rb +27 -0
  33. data/spec/unit/clear_spec.rb +18 -0
  34. data/spec/unit/error_spec.rb +46 -0
  35. data/spec/unit/events_spec.rb +37 -0
  36. data/spec/unit/formats_spec.rb +9 -0
  37. data/spec/unit/frames_spec.rb +33 -0
  38. data/spec/unit/hide_cursor_spec.rb +53 -0
  39. data/spec/unit/job_spec.rb +14 -0
  40. data/spec/unit/join_spec.rb +12 -0
  41. data/spec/unit/multi/auto_spin_spec.rb +34 -0
  42. data/spec/unit/multi/error_spec.rb +109 -0
  43. data/spec/unit/multi/line_inset_spec.rb +59 -0
  44. data/spec/unit/multi/on_spec.rb +13 -0
  45. data/spec/unit/multi/register_spec.rb +47 -0
  46. data/spec/unit/multi/spin_spec.rb +103 -0
  47. data/spec/unit/multi/stop_spec.rb +97 -0
  48. data/spec/unit/multi/success_spec.rb +110 -0
  49. data/spec/unit/new_spec.rb +26 -0
  50. data/spec/unit/pause_spec.rb +25 -0
  51. data/spec/unit/reset_spec.rb +21 -0
  52. data/spec/unit/run_spec.rb +32 -0
  53. data/spec/unit/spin_spec.rb +90 -0
  54. data/spec/unit/stop_spec.rb +64 -0
  55. data/spec/unit/success_spec.rb +46 -0
  56. data/spec/unit/update_spec.rb +87 -0
  57. data/tasks/console.rake +11 -0
  58. data/tasks/coverage.rake +11 -0
  59. data/tasks/spec.rake +29 -0
  60. data/tty-spinner.gemspec +27 -0
  61. 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)
@@ -0,0 +1,19 @@
1
+ require_relative '../lib/tty-spinner'
2
+
3
+ spinner = TTY::Spinner.new("[:spinner] Task name")
4
+
5
+ spinner.auto_spin
6
+
7
+ sleep(2)
8
+
9
+ spinner.pause
10
+
11
+ sleep(2)
12
+
13
+ spinner.resume
14
+
15
+ sleep(2)
16
+
17
+ spinner.stop
18
+
19
+ puts
@@ -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 }
@@ -0,0 +1,9 @@
1
+ require_relative '../lib/tty-spinner'
2
+
3
+ spinner = TTY::Spinner.new("[:spinner] Task name")
4
+ 20.times do
5
+ spinner.spin
6
+ sleep(0.1)
7
+ end
8
+
9
+ spinner.success('(successful)')
@@ -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
@@ -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) }
@@ -1,4 +1,2 @@
1
- # encoding: utf-8
2
-
3
1
  require_relative 'tty/spinner'
4
2
  require_relative 'tty/spinner/multi'
@@ -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[".freeze
20
+ ECMA_CSI = "\x1b["
22
21
 
23
22
  MATCHER = /:spinner/
24
- TICK = '✔'.freeze
25
- CROSS = '✖'.freeze
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
- @succeeded = false
116
- @first_run = true
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.arity.zero?
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
- CURSOR_LOCK.synchronize do
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(self) if @multispinner
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
@@ -1,4 +1,4 @@
1
- # coding: utf-8
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: '-◡⊙-◠'.freeze
167
+ frames: '-◡⊙-◠'
168
168
  },
169
169
  burger: {
170
170
  interval: 6,
@@ -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
- }.freeze
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] pattern
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(pattern, options = {}, &job)
82
+ def register(pattern_or_spinner, **options, &job)
83
83
  observable = options.delete(:observable) { true }
84
- spinner = TTY::Spinner.new(pattern, @options.merge(options))
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 [TTY::Spinner] spinner
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(spinner)
191
+ def line_inset(line_no)
172
192
  return '' if @top_spinner.nil?
173
193
 
174
- case spinner
175
- when @top_spinner
194
+ if line_no == 1
176
195
  @inset_opts[:top]
177
- when @spinners.last
196
+ elsif line_no == @spinners_count
178
197
  @inset_opts[:bottom]
179
198
  else
180
199
  @inset_opts[:middle]
@@ -1,7 +1,7 @@
1
- # coding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module TTY
4
4
  class Spinner
5
- VERSION = '0.8.0'.freeze
5
+ VERSION = '0.9.0'
6
6
  end # Spinner
7
7
  end # TTY
@@ -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