Class: TTY::Spinner

Inherits:
Object
  • Object
show all
Includes:
Formats
Defined in:
lib/tty/spinner.rb,
lib/tty/spinner/multi.rb,
lib/tty/spinner/version.rb

Overview

Used for creating terminal spinner

Defined Under Namespace

Classes: Multi

Constant Summary collapse

NotSpinningError =
Class.new(StandardError)
ECMA_CSI =
"\x1b[".freeze
MATCHER =
/:spinner/
TICK =
''.freeze
CROSS =
''.freeze
CURSOR_USAGE_LOCK =
Monitor.new
VERSION =
"0.5.0"

Constants included from Formats

Formats::FORMATS

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ Spinner

Initialize a spinner

Examples:

spinner = TTY::Spinner.new

Parameters:

  • message (String)

    the message to print in front of the spinner

  • options (Hash)


83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/tty/spinner.rb', line 83

def initialize(*args)
  options  = args.last.is_a?(::Hash) ? args.pop : {}
  @message = args.empty? ? ':spinner' : args.pop
  @tokens  = {}

  @format      = options.fetch(:format) { :classic }
  @output      = options.fetch(:output) { $stderr }
  @hide_cursor = options.fetch(:hide_cursor) { false }
  @frames      = options.fetch(:frames) do
                   fetch_format(@format.to_sym, :frames)
                 end
  @clear       = options.fetch(:clear) { false }
  @success_mark= options.fetch(:success_mark) { TICK }
  @error_mark  = options.fetch(:error_mark) { CROSS }
  @interval    = options.fetch(:interval) do
                   fetch_format(@format.to_sym, :interval)
                 end

  @callbacks   = Hash.new { |h, k| h[k] = [] }
  @length      = @frames.length
  @current     = 0
  @done        = false
  @state       = :stopped
  @thread      = nil
  @job         = nil
  @multispinner= nil
  @index       = nil
  @succeeded   = false
  @first_run  = true
end

Instance Attribute Details

#formatString (readonly)

The current format type

Returns:

  • (String)


37
38
39
# File 'lib/tty/spinner.rb', line 37

def format
  @format
end

#hide_cursorBoolean (readonly)

Whether to show or hide cursor

Returns:

  • (Boolean)


44
45
46
# File 'lib/tty/spinner.rb', line 44

def hide_cursor
  @hide_cursor
end

#messageString (readonly)

The message to print before the spinner

Returns:

  • (String)

    the current message



52
53
54
# File 'lib/tty/spinner.rb', line 52

def message
  @message
end

#outputObject (readonly)

The object that responds to print call defaulting to stderr



30
31
32
# File 'lib/tty/spinner.rb', line 30

def output
  @output
end

#tokensHash[Symbol, Object] (readonly)

Tokens for the message

Returns:

  • (Hash[Symbol, Object])

    the current tokens



60
61
62
# File 'lib/tty/spinner.rb', line 60

def tokens
  @tokens
end

Instance Method Details

#add_multispinner(multispinner, index) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Notifies the TTY::Spinner that it is running under a multispinner

Parameters:

  • the (TTY::Spinner::Multi)

    multispinner that it is running under

  • the (Integer)

    index of this spinner in the multispinner



120
121
122
123
# File 'lib/tty/spinner.rb', line 120

def add_multispinner(multispinner, index)
  @multispinner = multispinner
  @index = index
end

#auto_spinObject

Start automatic spinning animation



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/tty/spinner.rb', line 203

def auto_spin
  CURSOR_USAGE_LOCK.synchronize do
    start
    sleep_time = 1.0 / @interval

    spin
    @thread = Thread.new do
      sleep(sleep_time)
      while @started_at
        if Thread.current['pause']
          Thread.stop
          Thread.current['pause'] = false
        end
        spin
        sleep(sleep_time)
      end
    end
  end
end

#clear_lineObject

Clear current line



404
405
406
# File 'lib/tty/spinner.rb', line 404

def clear_line
  write(ECMA_CSI + '0m' + TTY::Cursor.clear_line)
end

#done?Boolean

Whether the spinner has completed spinning

Returns:

  • (Boolean)

    whether or not the spinner has finished



130
131
132
# File 'lib/tty/spinner.rb', line 130

def done?
  @done
end

#durationNumeric

Duration of the spinning animation

Returns:

  • (Numeric)


277
278
279
# File 'lib/tty/spinner.rb', line 277

def duration
  @started_at ? Time.now - @started_at : nil
end

#error(stop_message = '') ⇒ Object

Finish spinning and set state to :error



393
394
395
396
397
398
399
# File 'lib/tty/spinner.rb', line 393

def error(stop_message = '')
  return if done?

  @succeeded = :error
  stop(stop_message)
  emit(:error)
end

#error?Boolean

Whether the spinner is in the error state. This is only true temporarily while it is being marked with a failure mark.

Returns:

  • (Boolean)

    whether or not the spinner is erroring



159
160
161
# File 'lib/tty/spinner.rb', line 159

def error?
  @succeeded == :error
end

#job(&work) ⇒ Object

Add job to this spinner



183
184
185
186
187
188
189
# File 'lib/tty/spinner.rb', line 183

def job(&work)
  if block_given?
    @job = work
  else
    @job
  end
end

#job?Boolean

Check if this spinner has a scheduled job

Returns:

  • (Boolean)


196
197
198
# File 'lib/tty/spinner.rb', line 196

def job?
  !@job.nil?
end

#join(timeout = nil) ⇒ Object

Join running spinner

Parameters:

  • timeout (Float) (defaults to: nil)

    the timeout for join



287
288
289
290
291
292
293
# File 'lib/tty/spinner.rb', line 287

def join(timeout = nil)
  unless @thread
    raise(NotSpinningError, 'Cannot join spinner that is not running')
  end

  timeout ? @thread.join(timeout) : @thread.join
end

#killObject

Kill running spinner



298
299
300
# File 'lib/tty/spinner.rb', line 298

def kill
  @thread.kill if @thread
end

#next_charString

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Retrieve next character

Returns:

  • (String)


369
370
371
372
373
374
375
376
377
# File 'lib/tty/spinner.rb', line 369

def next_char
  if success?
    @success_mark
  elsif error?
    @error_mark
  else
    @frames[@current - 1]
  end
end

#on(name, &block) ⇒ Object

Register callback



166
167
168
169
# File 'lib/tty/spinner.rb', line 166

def on(name, &block)
  @callbacks[name] << block
  self
end

#pauseObject

Pause spinner automatic animation



235
236
237
238
239
# File 'lib/tty/spinner.rb', line 235

def pause
  return if paused?

  @thread['pause'] = true if @thread
end

#paused?Boolean

Checked if current spinner is paused

Returns:

  • (Boolean)


228
229
230
# File 'lib/tty/spinner.rb', line 228

def paused?
  !!(@thread && @thread['pause'])
end

#redraw_indentObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Redraw the indent for this spinner, if it exists



326
327
328
329
330
331
332
# File 'lib/tty/spinner.rb', line 326

def redraw_indent
  if @hide_cursor && !spinning?
    write(ECMA_CSI + DEC_TCEM + DEC_RST)
  end

  write("", false)
end

#resetObject

Reset the spinner to initial frame



422
423
424
425
# File 'lib/tty/spinner.rb', line 422

def reset
  @current = 0
  @first_run = true
end

#resumeObject

Resume spinner automatic animation



244
245
246
247
248
# File 'lib/tty/spinner.rb', line 244

def resume
  return unless paused?

  @thread.wakeup if @thread
end

#run(stop_message = '') { ... } ⇒ Object

Run spinner while executing job

Examples:

spinner.run('Migrated DB') { ... }

Parameters:

  • stop_message (String) (defaults to: '')

    the message displayed when block is finished

Yields:

  • automatically animate and finish spinner



261
262
263
264
265
266
267
268
269
270
# File 'lib/tty/spinner.rb', line 261

def run(stop_message = '', &block)
  auto_spin

  @work = Thread.new {
    instance_eval(&block)
  }
  @work.join
ensure
  stop(stop_message)
end

#spinString

Perform a spin

Returns:

  • (String)

    the printed data



308
309
310
311
312
313
314
315
316
317
318
319
320
321
# File 'lib/tty/spinner.rb', line 308

def spin
  return if @done

  if @hide_cursor && !spinning?
    write(TTY::Cursor.hide)
  end

  data = message.gsub(MATCHER, @frames[@current])
  data = replace_tokens(data)
  write(data, true)
  @current = (@current + 1) % @length
  @state = :spinning
  data
end

#spinning?Boolean

Whether the spinner is spinning

Returns:

  • (Boolean)

    whether or not the spinner is spinning



139
140
141
# File 'lib/tty/spinner.rb', line 139

def spinning?
  @state == :spinning
end

#startObject

Start timer and unlock spinner



174
175
176
177
178
# File 'lib/tty/spinner.rb', line 174

def start
  @started_at = Time.now
  @done = false
  reset
end

#stop(stop_message = '') ⇒ Object

Finish spining

Parameters:

  • stop_message (String) (defaults to: '')

    the stop message to print



340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/tty/spinner.rb', line 340

def stop(stop_message = '')
  return if done?

  if @hide_cursor
    write(TTY::Cursor.show, false)
  end
  return clear_line if @clear

  data = message.gsub(MATCHER, next_char)
  data = replace_tokens(data)
  if !stop_message.empty?
    data << ' ' + stop_message
  end

  write(data, true)
  write("\n", false) unless @clear || @multispinner
ensure
  @state      = :stopped
  @done       = true
  @started_at = nil
  emit(:done)
  kill
end

#success(stop_message = '') ⇒ Object

Finish spinning and set state to :success



382
383
384
385
386
387
388
# File 'lib/tty/spinner.rb', line 382

def success(stop_message = '')
  return if done?

  @succeeded = :success
  stop(stop_message)
  emit(:success)
end

#success?Boolean

Whether the spinner is in the success state. When true the spinner is marked with a success mark.

Returns:

  • (Boolean)

    whether or not the spinner succeeded



149
150
151
# File 'lib/tty/spinner.rb', line 149

def success?
  @succeeded == :success
end

#update(tokens) ⇒ Object

Update string formatting tokens

Parameters:

  • tokens (Hash[Symbol])

    the tokens used in formatting string



414
415
416
417
# File 'lib/tty/spinner.rb', line 414

def update(tokens)
  clear_line if spinning?
  @tokens.merge!(tokens)
end