austb-tty-spinner 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,127 @@
1
+ # coding: utf-8
2
+
3
+ module TTY
4
+ module Formats
5
+ FORMATS = {
6
+ classic: {
7
+ interval: 10,
8
+ frames: %w{| / - \\}
9
+ },
10
+ spin: {
11
+ interval: 10,
12
+ frames: %w{◴ ◷ ◶ ◵ }
13
+ },
14
+ spin_2: {
15
+ interval: 10,
16
+ frames: %w{◐ ◓ ◑ ◒ }
17
+ },
18
+ spin_3: {
19
+ interval: 10,
20
+ frames: %w{◰ ◳ ◲ ◱}
21
+ },
22
+ spin_4: {
23
+ inteval: 10,
24
+ frames: %w{╫ ╪'}
25
+ },
26
+ pulse: {
27
+ interval: 10,
28
+ frames: %w{⎺ ⎻ ⎼ ⎽ ⎼ ⎻}
29
+ },
30
+ pulse_2: {
31
+ interval: 15,
32
+ frames: %w{▁ ▃ ▅ ▆ ▇ █ ▇ ▆ ▅ ▃ }
33
+ },
34
+ pulse_3: {
35
+ interval: 20,
36
+ frames: '▉▊▋▌▍▎▏▎▍▌▋▊▉'
37
+ },
38
+ dots: {
39
+ interval: 10,
40
+ frames: [ "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" ]
41
+ },
42
+ arrow: {
43
+ interval: 10,
44
+ frames: %w{← ↖ ↑ ↗ → ↘ ↓ ↙ }
45
+ },
46
+ arrow_pulse: {
47
+ interval: 10,
48
+ frames: [
49
+ "▹▹▹▹▹",
50
+ "▸▹▹▹▹",
51
+ "▹▸▹▹▹",
52
+ "▹▹▸▹▹",
53
+ "▹▹▹▸▹",
54
+ "▹▹▹▹▸"
55
+ ]
56
+ },
57
+ triangle: {
58
+ interval: 10,
59
+ frames: %w{◢ ◣ ◤ ◥}
60
+ },
61
+ arc: {
62
+ interval: 10,
63
+ frames: %w{ ◜ ◠ ◝ ◞ ◡ ◟ }
64
+ },
65
+ pipe: {
66
+ interval: 10,
67
+ frames: %w{ ┤ ┘ ┴ └ ├ ┌ ┬ ┐ }
68
+ },
69
+ bouncing: {
70
+ interval: 10,
71
+ frames: [
72
+ "[ ]",
73
+ "[ =]",
74
+ "[ ==]",
75
+ "[ ===]",
76
+ "[====]",
77
+ "[=== ]",
78
+ "[== ]",
79
+ "[= ]"
80
+ ]
81
+ },
82
+ bouncing_ball: {
83
+ interval: 10,
84
+ frames: [
85
+ "( ● )",
86
+ "( ● )",
87
+ "( ● )",
88
+ "( ● )",
89
+ "( ●)",
90
+ "( ● )",
91
+ "( ● )",
92
+ "( ● )",
93
+ "( ● )",
94
+ "(● )"
95
+ ]
96
+ },
97
+ box_bounce: {
98
+ interval: 10,
99
+ frames: %w{ ▌ ▀ ▐ ▄ }
100
+ },
101
+ box_bounce_2: {
102
+ interval: 10,
103
+ frames: %w{ ▖ ▘ ▝ ▗ }
104
+ },
105
+ star: {
106
+ interval: 10,
107
+ frames: %w{ ✶ ✸ ✹ ✺ ✹ ✷ }
108
+ },
109
+ toggle: {
110
+ interval: 10,
111
+ frames: %w{ ■ □ ▪ ▫ }
112
+ },
113
+ balloon: {
114
+ interval: 10,
115
+ frames: %w{ . o O @ * }
116
+ },
117
+ balloon_2: {
118
+ interval: 10,
119
+ frames: %w{. o O ° O o . }
120
+ },
121
+ flip: {
122
+ interval: 10,
123
+ frames: '-◡⊙-◠'.freeze
124
+ }
125
+ }
126
+ end # Formats
127
+ end # TTY
@@ -0,0 +1,259 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require 'forwardable'
5
+
6
+ require_relative '../spinner'
7
+
8
+ module TTY
9
+ class Spinner
10
+ # Used for managing multiple terminal spinners
11
+ #
12
+ # @api public
13
+ class Multi
14
+ include Enumerable
15
+
16
+ extend Forwardable
17
+
18
+ def_delegators :@spinners, :each, :empty?, :length
19
+
20
+ DEFAULT_INSET = {
21
+ top: Gem.win_platform? ? '+ ' : "\u250c ",
22
+ middle: Gem.win_platform? ? '|-- ' : "\u251c\u2500\u2500",
23
+ bottom: Gem.win_platform? ? '|__ ' : "\u2514\u2500\u2500"
24
+ }.freeze
25
+
26
+ # Initialize a multispinner
27
+ #
28
+ # @example
29
+ # spinner = TTY::Spinner::Multi.new
30
+ #
31
+ # @param [String] message
32
+ # the optional message to print in front of the top level spinner
33
+ #
34
+ # @param [Hash] options
35
+ # @option options [Hash] :style
36
+ # keys :top :middle and :bottom can contain Strings that are used to
37
+ # indent the spinners. Ignored if message is blank
38
+ # @option options [Object] :output
39
+ # the object that responds to print call defaulting to stderr
40
+ # @option options [Boolean] :hide_cursor
41
+ # display or hide cursor
42
+ # @option options [Boolean] :clear
43
+ # clear ouptut when finished
44
+ # @option options [Float] :interval
45
+ # the interval for auto spinning
46
+ #
47
+ # @api public
48
+ def initialize(*args)
49
+ @options = args.last.is_a?(::Hash) ? args.pop : {}
50
+ message = args.empty? ? nil : args.pop
51
+ @inset_opts = @options.delete(:style) { DEFAULT_INSET }
52
+ @create_spinner_lock = Mutex.new
53
+ @spinners = []
54
+ @top_spinner = nil
55
+ @top_spinner = register(message) unless message.nil?
56
+
57
+ @callbacks = {
58
+ success: [],
59
+ error: [],
60
+ done: []
61
+ }
62
+ end
63
+
64
+ # Register a new spinner
65
+ #
66
+ # @param [String] pattern
67
+ # the pattern used for creating spinner
68
+ #
69
+ # @api public
70
+ def register(pattern, options = {}, &job)
71
+ spinner = TTY::Spinner.new(pattern, @options.merge(options))
72
+
73
+ @create_spinner_lock.synchronize do
74
+ spinner.add_multispinner(self, @spinners.length)
75
+ spinner.job(&job) if block_given?
76
+ observe_events(spinner) if @top_spinner
77
+ @spinners << spinner
78
+ if @top_spinner
79
+ @spinners.each { |sp| sp.redraw_indent if sp.spinning? || sp.done? }
80
+ end
81
+ end
82
+
83
+ spinner
84
+ end
85
+
86
+ # Observe all child events to notify top spinner of current state
87
+ #
88
+ # @param [TTY::Spinner] spinner
89
+ # the spinner to listen to for events
90
+ #
91
+ # @api private
92
+ def observe_events(spinner)
93
+ spinner.on(:success) { @top_spinner.success if success? }
94
+ .on(:error) { @top_spinner.error if error? }
95
+ .on(:done) { @top_spinner.stop if done? && !success? && !error? }
96
+ end
97
+
98
+ # Get the top level spinner if it exists
99
+ #
100
+ # @return [TTY::Spinner] the top level spinner
101
+ #
102
+ # @api public
103
+ def top_spinner
104
+ raise "No top level spinner" if @top_spinner.nil?
105
+
106
+ @top_spinner
107
+ end
108
+
109
+ # Auto spin the top level spinner & all child spinners
110
+ # that have scheduled jobs
111
+ #
112
+ # @api public
113
+ def auto_spin
114
+ raise "No top level spinner" if @top_spinner.nil?
115
+
116
+ @top_spinner.auto_spin
117
+ jobs = []
118
+ @spinners.each do |spinner|
119
+ if spinner.job?
120
+ spinner.auto_spin
121
+ jobs << Thread.new { spinner.instance_eval(&spinner.job) }
122
+ end
123
+ end
124
+ jobs.each(&:join)
125
+ end
126
+
127
+ # Pause all spinners
128
+ #
129
+ # @api public
130
+ def pause
131
+ @spinners.dup.each(&:pause)
132
+ end
133
+
134
+ # Resume all spinners
135
+ #
136
+ # @api public
137
+ def resume
138
+ @spinners.dup.each(&:resume)
139
+ end
140
+
141
+ # Find relative offset position to which to move the current cursor
142
+ #
143
+ # The position is found among the registered spinners given the current
144
+ # position the spinner is at provided its index
145
+ #
146
+ # @param [Integer] index
147
+ # the position to search from
148
+ #
149
+ # @return [Integer]
150
+ # the current position
151
+ #
152
+ # @api public
153
+ def count_line_offset(index)
154
+ Array(@spinners[index..-1]).reduce(0) do |acc, spinner|
155
+ if spinner.spinning? || spinner.done?
156
+ acc += 1
157
+ end
158
+ acc
159
+ end
160
+ end
161
+
162
+ # Find the number of characters to move into the line
163
+ # before printing the spinner
164
+ #
165
+ # @param [TTY::Spinner] spinner
166
+ # the spinner for which line inset is calculated
167
+ #
168
+ # @return [String]
169
+ # the inset
170
+ #
171
+ # @api public
172
+ def line_inset(spinner)
173
+ return '' if @top_spinner.nil?
174
+
175
+ case spinner
176
+ when @top_spinner
177
+ @inset_opts[:top]
178
+ when @spinners.last
179
+ @inset_opts[:bottom]
180
+ else
181
+ @inset_opts[:middle]
182
+ end
183
+ end
184
+
185
+ # Check if all spinners are done
186
+ #
187
+ # @return [Boolean]
188
+ #
189
+ # @api public
190
+ def done?
191
+ (@spinners - [@top_spinner]).all?(&:done?)
192
+ end
193
+
194
+ # Check if all spinners succeeded
195
+ #
196
+ # @return [Boolean]
197
+ #
198
+ # @api public
199
+ def success?
200
+ (@spinners - [@top_spinner]).all?(&:success?)
201
+ end
202
+
203
+ # Check if any spinner errored
204
+ #
205
+ # @return [Boolean]
206
+ #
207
+ # @api public
208
+ def error?
209
+ (@spinners - [@top_spinner]).any?(&:error?)
210
+ end
211
+
212
+ # Stop all spinners
213
+ #
214
+ # @api public
215
+ def stop
216
+ @spinners.dup.each(&:stop)
217
+ emit :done
218
+ end
219
+
220
+ # Stop all spinners with success status
221
+ #
222
+ # @api public
223
+ def success
224
+ @top_spinner.success if @top_spinner
225
+ @spinners.dup.each(&:success)
226
+ emit :success
227
+ end
228
+
229
+ # Stop all spinners with error status
230
+ #
231
+ # @api public
232
+ def error
233
+ @top_spinner.error if @top_spinner
234
+ @spinners.dup.each(&:error)
235
+ emit :error
236
+ end
237
+
238
+ # Listen on event
239
+ #
240
+ # @api public
241
+ def on(key, &callback)
242
+ unless @callbacks.key?(key)
243
+ raise ArgumentError, "The event #{key} does not exist. "\
244
+ " Use :success, :error, or :done instead"
245
+ end
246
+ @callbacks[key] << callback
247
+ self
248
+ end
249
+
250
+ private
251
+
252
+ def emit(key, *args)
253
+ @callbacks[key].each do |block|
254
+ block.call(*args)
255
+ end
256
+ end
257
+ end # MultiSpinner
258
+ end # Spinner
259
+ end # TTY
@@ -0,0 +1,7 @@
1
+ # coding: utf-8
2
+
3
+ module TTY
4
+ class Spinner
5
+ VERSION = "0.5.0"
6
+ end # Spinner
7
+ end # TTY
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: austb-tty-spinner
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: ruby
6
+ authors:
7
+ - Piotr Murach
8
+ - Austin Blatt
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2017-08-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: tty-cursor
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 0.5.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: 0.5.0
28
+ - !ruby/object:Gem::Dependency
29
+ name: bundler
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 1.5.0
35
+ - - "<"
36
+ - !ruby/object:Gem::Version
37
+ version: '2.0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: 1.5.0
45
+ - - "<"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ - !ruby/object:Gem::Dependency
49
+ name: rake
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: A terminal spinner for tasks that have non-deterministic time frame.
63
+ email:
64
+ - austinblatt@gmail.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - LICENSE.txt
70
+ - README.md
71
+ - lib/tty-spinner.rb
72
+ - lib/tty/spinner.rb
73
+ - lib/tty/spinner/formats.rb
74
+ - lib/tty/spinner/multi.rb
75
+ - lib/tty/spinner/version.rb
76
+ homepage: https://github.com/austb/tty-spinner
77
+ licenses:
78
+ - MIT
79
+ metadata: {}
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubyforge_project:
96
+ rubygems_version: 2.5.1
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: A terminal spinner for tasks that have non-deterministic time frame.
100
+ test_files: []
101
+ has_rdoc: