diff --git a/lib/tty/spinner/multi.rb b/lib/tty/spinner/multi.rb index 50458b5..fa0d87c 100644 --- a/lib/tty/spinner/multi.rb +++ b/lib/tty/spinner/multi.rb @@ -68,7 +68,8 @@ def initialize(*args) @callbacks = { success: [], error: [], - done: [] + done: [], + spin: [] } end @@ -122,7 +123,6 @@ def top_spinner def auto_spin raise "No top level spinner" if @top_spinner.nil? - @top_spinner.auto_spin jobs = [] @spinners.each do |spinner| if spinner.job? @@ -139,7 +139,9 @@ def auto_spin def spin raise "No top level spinner" if @top_spinner.nil? - @top_spinner.spin + synchronize do + throttle { @top_spinner.spin } + end end # Pause all spinners @@ -239,7 +241,7 @@ def error def on(key, &callback) unless @callbacks.key?(key) raise ArgumentError, "The event #{key} does not exist. "\ - " Use :success, :error, or :done instead" + ' Use :spin, :success, :error, or :done instead' end @callbacks[key] << callback self @@ -247,6 +249,22 @@ def on(key, &callback) private + # Check if this spinner should revolve to keep constant speed + # matching top spinner interval + # + # @api private + 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 + + # Fire an event + # + # @api private def emit(key, *args) @callbacks[key].each do |block| block.call(*args) @@ -260,11 +278,22 @@ def emit(key, *args) # # @api private def observe(spinner) - spinner.on(:success, &success_handler) + spinner.on(:spin, &spin_handler) + .on(:success, &success_handler) .on(:error, &error_handler) .on(:done, &done_handler) end + # Handle spin event + # + # @api private + def spin_handler + proc do + spin if @top_spinner + emit(:spin) + end + end + # Handle the success state # # @api private @@ -295,7 +324,7 @@ def error_handler def done_handler proc do if done? - @top_spinner.stop if @top_spinner + @top_spinner.stop if @top_spinner && !error? && !success? emit(:done) end end diff --git a/spec/unit/multi/auto_spin_spec.rb b/spec/unit/multi/auto_spin_spec.rb index 0e3cffa..54a598b 100644 --- a/spec/unit/multi/auto_spin_spec.rb +++ b/spec/unit/multi/auto_spin_spec.rb @@ -3,13 +3,13 @@ RSpec.describe TTY::Spinner::Multi, '#auto_spin' do let(:output) { StringIO.new('', 'w+') } - it "auto spins top level spinner" do + it "doesn't auto spin top level spinner" do spinners = TTY::Spinner::Multi.new("Top level spinner", output: output) - allow_any_instance_of(TTY::Spinner).to receive(:auto_spin) + allow(spinners.top_spinner).to receive(:auto_spin) spinners.auto_spin - expect(spinners.top_spinner).to have_received(:auto_spin).once + expect(spinners.top_spinner).to_not have_received(:auto_spin) end it "raises an exception when called without a top spinner" do