austb-tty-spinner 0.5.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.
@@ -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: