Skip to content

Commit

Permalink
Merge pull request #22 from austb/api_maint
Browse files Browse the repository at this point in the history
Make Multi#initialize take 2 args, add indent opts
  • Loading branch information
piotrmurach committed Aug 5, 2017
2 parents bd05ab3 + 8459a47 commit 2db5ba0
Show file tree
Hide file tree
Showing 8 changed files with 263 additions and 19 deletions.
54 changes: 53 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ _ Loading ... Done!

For more usage examples please see [examples directory](https://github.com/piotrmurach/tty-spinner/tree/master/examples)

## 2. API
## 2. TTY::Spinner API

### 2.1 spin

Expand Down Expand Up @@ -340,6 +340,58 @@ This event is fired when `error` completion is called. In order to respond to th
spinner.on(:error) { ... }
```

## 5. TTY::Spinner::Multi API

### 5.1 register

Create and register a `TTY::Spinner` under the multispinner

```ruby
new_spinner = multi_spinner.register("[:spinner] Task 1 name", options)
```

If no options are given it will use the options given to the multi_spinner when it was initialized to create the new spinner.
If options are passed, they will override any options given to the multi spinner.

### 5.2 auto_spin

The multispinner has to have been given a message on initialization.
To perform automatic spinning animation use `auto_spin` method like so:

```ruby
multi_spinner = TTY::Spinner::Multi.new("[:spinner] Top level spinner")
multi_spinner.auto_spin
```

The speed with which the spinning happens is determined by the `:interval` parameter. All the spinner formats have their default intervals specified ([see](https://github.com/piotrmurach/tty-spinner/blob/master/lib/tty/spinner/formats.rb)).

### 5.3 stop

In order to stop the multi spinner call `stop`. This will stop the top level spinner, if it exists, and any sub-spinners still spinning.

```ruby
multi_spinner.stop
```

#### 5.3.1 success

Use `success` call to stop the spinning animation and replace the spinning symbol with checkmark character to indicate successful completion.
This will also call `#success` on any sub-spinners that are still spinning.

```ruby
multi_spinner.success
```


#### 5.3.2 error

Use `error` call to stop the spining animation and replace the spinning symbol with cross character to indicate error completion.
This will also call `#error` on any sub-spinners that are still spinning.

```ruby
multi_spinner.error
```

## Contributing

1. Fork it ( https://github.com/piotrmurach/tty-spinner/fork )
Expand Down
34 changes: 34 additions & 0 deletions examples/multi/custom_indent_opts.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# encoding: utf-8

require 'tty-spinner'

opts = {
indent: 4,
style: {
top: "\u250c ",
middle: "\u251c\u2500\u2500",
bottom: "\u2514\u2500\u2500",
}
}
spinners = TTY::Spinner::Multi.new("[:spinner] Top level spinner", opts)

sp1 = spinners.register "[:spinner] one"
sp2 = spinners.register "[:spinner] two"
sp3 = spinners.register "[:spinner] three"

spinners.auto_spin
sp1.auto_spin
sp2.auto_spin
sp3.auto_spin

sleep(2)

sp1.success
sleep 1
sp2.success

sleep 1

sp3.error

spinners.error
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

require 'tty-spinner'

spinners = TTY::Spinner::Multi.new(message: "[:spinner] Top level spinner")
spinners = TTY::Spinner::Multi.new("[:spinner] Top level spinner")

sp1 = spinners.register "[:spinner] one"
sp2 = spinners.register "[:spinner] two"
Expand Down
61 changes: 57 additions & 4 deletions lib/tty/spinner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,35 +115,73 @@ def initialize(*args)
@first_run = true
end

# Notifies the TTY::Spinner that it is running under a multispinner
#
# @param [TTY::Spinner::Multi] the multispinner that it is running under
# @param [Integer] the index of this spinner in the multispinner
#
# @api private
def add_multispinner(multispinner, index)
@multispinner = multispinner
@index = index
end

# Whether the spinner has succeeded
#
# @return [Boolean] whether or not the spinner succeeded
#
# @api public
def succeeded?
done? && @succeeded
end

# Whether the spinner has errored
#
# @return [Boolean] whether or not the spinner errored
#
# @api public
def errored?
done? && !@succeeded
end

# Whether the spinner has completed spinning
#
# @return [Boolean] whether or not the spinner has finished
#
# @api public
def done?
@done
end

# Whether the spinner is spinner
#
# @return [Boolean] whether or not the spinner is spinning
#
# @api public
def spinning?
@state == :spinning
end

# Whether the spinner is in the success state. This is only true
# temporarily while it is being marked with a success mark.
#
# @return [Boolean] whether or not the spinner is succeeding
#
# @api private
def success?
@state == :success
end

# Whether the spinner is in the error state. This is only true
# temporarily while it is being marked with a failure mark.
#
# @return [Boolean] whether or not the spinner is erroring
#
# @api private
def error?
@state == :error
end

def done?
@done
end

# Register callback
#
# @api public
Expand Down Expand Up @@ -251,6 +289,17 @@ def spin
data
end

# Redraw the indent for this spinner, if it exists
#
# @api private
def redraw_indent
if @hide_cursor && !spinning?
write(ECMA_CSI + DEC_TCEM + DEC_RST)
end

write("", false)
end

# Finish spining
#
# @param [String] stop_message
Expand Down Expand Up @@ -343,6 +392,10 @@ def reset

private

# Execute a block on the proper terminal line if the spinner is running
# under a multispinner. Otherwise, execute the block on the current line.
#
# @api private
def execute_on_line
if @multispinner
CURSOR_USAGE_LOCK.synchronize do
Expand Down
70 changes: 65 additions & 5 deletions lib/tty/spinner/multi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,53 @@ class Multi

def_delegators :@spinners, :each, :empty?, :length

def initialize(options = {})
message = options.delete(:message)
@options = options
# Initialize the indentation options and remove them from the default
# spinner options
#
# @api private
def init_indent_opts
@indentation_opts = {}
@indentation_opts[:indent] = @options.delete(:indent) { 2 }
@indentation_opts[:style] = @options.delete(:style) {
{
top: '',
middle: '',
bottom: '',
}
}
end

# 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 [Integer] :indent
# the minimum number of characters to indent sub-spinners by.
# Ignored if message is blank
# @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
def initialize(*args)
@options = args.last.is_a?(::Hash) ? args.pop : {}
message = args.empty? ? nil : args.pop

init_indent_opts

@create_spinner_lock = Mutex.new
@spinners = []
@top_level_spinner = nil
Expand All @@ -46,17 +90,26 @@ def register(pattern, options = {})
@create_spinner_lock.synchronize do
spinner.add_multispinner(self, @spinners.length)
@spinners << spinner
@spinners.each { |sp| sp.redraw_indent if sp.spinning? || sp.done? } unless @top_level_spinner.nil?
end

spinner
end

# Get the top level spinner if it exists
#
# @return [TTY::Spinner] the top level spinner
#
# @api public
def top_level_spinner
raise "No top level spinner" if @top_level_spinner.nil?

@top_level_spinner
end

# Auto spin the top level spinner
#
# @api public
def auto_spin
raise "No top level spinner" if @top_level_spinner.nil?

Expand Down Expand Up @@ -90,9 +143,16 @@ def count_line_offset(index)
#
# @api public
def line_inset(spinner)
return " " if @top_level_spinner && spinner != @top_level_spinner
return '' if @top_level_spinner.nil?

return @indentation_opts[:style][:top] if spinner == @top_level_spinner

min_indent = @indentation_opts[:indent]
if spinner == @spinners.last
return @indentation_opts[:style][:bottom].ljust(min_indent)
end

""
@indentation_opts[:style][:middle].ljust(min_indent)
end

# Check if all spinners are done
Expand Down
35 changes: 30 additions & 5 deletions spec/unit/multi/top_level_spinner_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
end

it "returns the empty string for the top level spinner" do
spinners = TTY::Spinner::Multi.new(output: output, message: "Top level spinner")
spinners = TTY::Spinner::Multi.new("Top level spinner", output: output)
allow_any_instance_of(TTY::Spinner).to receive(:add_multispinner)

spinners.register ""
Expand All @@ -23,12 +23,37 @@
end

it "returns four spaces when there is a top level spinner" do
spinners = TTY::Spinner::Multi.new(output: output, message: "Top level spinner")
spinners = TTY::Spinner::Multi.new("Top level spinner", output: output)
allow_any_instance_of(TTY::Spinner).to receive(:add_multispinner)

spinner = spinners.register ""

expect(spinners.line_inset(spinner)).to eq(' ')
expect(spinners.line_inset(spinner)).to eq(" ")
end

it "defaults to the empty string for the top level spinner" do
spinners = TTY::Spinner::Multi.new("Top level spinner")

expect(spinners.line_inset(spinners.top_level_spinner)).to eq('')
end

it "allows customization" do
opts = {
output: output,
indent: 4,
style: {
top: ". ",
middle: "--",
bottom: "---",
}
}
spinners = TTY::Spinner::Multi.new("Top level spinner", opts)
middle_spinner = spinners.register ""
bottom_spinner = spinners.register ""

expect(spinners.line_inset(spinners.top_level_spinner)).to eq(". ")
expect(spinners.line_inset(middle_spinner)).to eq("-- ")
expect(spinners.line_inset(bottom_spinner)).to eq("--- ")
end
end

Expand All @@ -41,11 +66,11 @@

spinners.register ""

expect { spinners.auto_spin }.to raise_exception
expect { spinners.auto_spin }.to raise_exception(RuntimeError, /No top level spinner/)
end

it "doesn't raise exception" do
spinners = TTY::Spinner::Multi.new(output: output, message: "Top level spinner")
spinners = TTY::Spinner::Multi.new("Top level spinner", output: output)
allow_any_instance_of(TTY::Spinner).to receive(:add_multispinner)
allow_any_instance_of(TTY::Spinner).to receive(:auto_spin)

Expand Down

0 comments on commit 2db5ba0

Please sign in to comment.