class TTY::Spinner::Multi
Used for managing multiple terminal spinners
@api public
Constants
- DEFAULT_INSET
Attributes
The current count of all rendered rows
@api public
Public Class Methods
Initialize a multispinner
@example
spinner = TTY::Spinner::Multi.new
@param [String] message
the optional message to print in front of the top level spinner
@param [Hash] options @option options [Hash] :style
keys :top :middle and :bottom can contain Strings that are used to indent the spinners. Ignored if message is blank
@option options [Object] :output
the object that responds to print call defaulting to stderr
@option options [Boolean] :hide_cursor
display or hide cursor
@option options [Boolean] :clear
clear ouptut when finished
@option options [Float] :interval
the interval for auto spinning
@api public
# File lib/tty/spinner/multi.rb, line 54 def initialize(*args) super() @options = args.last.is_a?(::Hash) ? args.pop : {} message = args.empty? ? nil : args.pop @inset_opts = @options.delete(:style) { DEFAULT_INSET } @rows = 0 @spinners = [] @spinners_count = 0 @top_spinner = nil @last_spin_at = nil unless message.nil? @top_spinner = register(message, observable: false, row: next_row) end @callbacks = { success: [], error: [], done: [], spin: [] } end
Public Instance Methods
Auto spin the top level spinner & all child spinners that have scheduled jobs
@api public
# File lib/tty/spinner/multi.rb, line 143 def auto_spin raise "No top level spinner" if @top_spinner.nil? jobs = [] @spinners.each do |spinner| if spinner.job? spinner.auto_spin jobs << Thread.new { spinner.execute_job } end end jobs.each(&:join) end
Create a spinner instance
@api private
# File lib/tty/spinner/multi.rb, line 104 def create_spinner(pattern_or_spinner, options) case pattern_or_spinner when ::String TTY::Spinner.new( pattern_or_spinner, @options.merge(options) ) when ::TTY::Spinner pattern_or_spinner else raise ArgumentError, "Expected a pattern or spinner, " \ "got: #{pattern_or_spinner.class}" end end
Check if all spinners are done
@return [Boolean]
@api public
# File lib/tty/spinner/multi.rb, line 208 def done? synchronize do (@spinners - [@top_spinner]).all?(&:done?) end end
Stop all spinners with error status
@api public
# File lib/tty/spinner/multi.rb, line 253 def error @spinners.dup.each(&:error) end
Check if any spinner errored
@return [Boolean]
@api public
# File lib/tty/spinner/multi.rb, line 230 def error? synchronize do (@spinners - [@top_spinner]).any?(&:error?) end end
Find the number of characters to move into the line before printing the spinner
@param [Integer] line_no
the current spinner line number for which line inset is calculated
@return [String]
the inset
@api public
# File lib/tty/spinner/multi.rb, line 191 def line_inset(line_no) return '' if @top_spinner.nil? if line_no == 1 @inset_opts[:top] elsif line_no == @spinners_count @inset_opts[:bottom] else @inset_opts[:middle] end end
Increase a row count
@api public
# File lib/tty/spinner/multi.rb, line 122 def next_row synchronize do @rows += 1 end end
Listen on event
@api public
# File lib/tty/spinner/multi.rb, line 260 def on(key, &callback) unless @callbacks.key?(key) raise ArgumentError, "The event #{key} does not exist. "\ ' Use :spin, :success, :error, or :done instead' end @callbacks[key] << callback self end
Pause all spinners
@api public
# File lib/tty/spinner/multi.rb, line 170 def pause @spinners.dup.each(&:pause) end
Register a new spinner
@param [String, TTY::Spinner] pattern_or_spinner
the pattern used for creating spinner, or a spinner instance
@api public
# File lib/tty/spinner/multi.rb, line 82 def register(pattern_or_spinner, **options, &job) observable = options.delete(:observable) { true } spinner = nil synchronize do spinner = create_spinner(pattern_or_spinner, options) spinner.attach_to(self) spinner.job(&job) if block_given? observe(spinner) if observable @spinners << spinner @spinners_count += 1 if @top_spinner @spinners.each { |sp| sp.redraw_indent if sp.spinning? || sp.done? } end end spinner end
Resume all spinners
@api public
# File lib/tty/spinner/multi.rb, line 177 def resume @spinners.dup.each(&:resume) end
Perform a single spin animation
@api public
# File lib/tty/spinner/multi.rb, line 159 def spin raise "No top level spinner" if @top_spinner.nil? synchronize do throttle { @top_spinner.spin } end end
Stop all spinners
@api public
# File lib/tty/spinner/multi.rb, line 239 def stop @spinners.dup.each(&:stop) end
Stop all spinners with success status
@api public
# File lib/tty/spinner/multi.rb, line 246 def success @spinners.dup.each(&:success) end
Check if all spinners succeeded
@return [Boolean]
@api public
# File lib/tty/spinner/multi.rb, line 219 def success? synchronize do (@spinners - [@top_spinner]).all?(&:success?) end end
Get the top level spinner if it exists
@return [TTY::Spinner] the top level spinner
@api public
# File lib/tty/spinner/multi.rb, line 133 def top_spinner raise "No top level spinner" if @top_spinner.nil? @top_spinner end
Private Instance Methods
Handle the done state
@api private
# File lib/tty/spinner/multi.rb, line 343 def done_handler proc do if done? @top_spinner.stop if @top_spinner && !error? && !success? emit(:done) end end end
Fire an event
@api private
# File lib/tty/spinner/multi.rb, line 287 def emit(key, *args) @callbacks[key].each do |block| block.call(*args) end end
Handle the error state
@api private
# File lib/tty/spinner/multi.rb, line 331 def error_handler proc do if error? @top_spinner.error if @top_spinner @fired ||= emit(:error) # fire once end end end
Observe spinner for events to notify top spinner of current state
@param [TTY::Spinner] spinner
the spinner to listen to for events
@api private
# File lib/tty/spinner/multi.rb, line 299 def observe(spinner) spinner.on(:spin, &spin_handler) .on(:success, &success_handler) .on(:error, &error_handler) .on(:done, &done_handler) end
Handle spin event
@api private
# File lib/tty/spinner/multi.rb, line 309 def spin_handler proc do spin if @top_spinner emit(:spin) end end
Handle the success state
@api private
# File lib/tty/spinner/multi.rb, line 319 def success_handler proc do if success? @top_spinner.success if @top_spinner emit(:success) end end end
Check if this spinner should revolve to keep constant speed matching top spinner interval
@api private
# File lib/tty/spinner/multi.rb, line 275 def throttle sleep_time = 1.0 / @top_spinner.interval if @last_spin_at && Time.now - @last_spin_at < sleep_time return end yield if block_given? @last_spin_at = Time.now end