Skip to content

Commit

Permalink
Merge branch 'allow-configurable-projectors'
Browse files Browse the repository at this point in the history
  • Loading branch information
jfelchner committed Mar 4, 2023
2 parents d7a2a51 + c914747 commit 2bc31d0
Show file tree
Hide file tree
Showing 16 changed files with 680 additions and 313 deletions.
43 changes: 41 additions & 2 deletions lib/ruby-progressbar/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,29 @@
require 'ruby-progressbar/outputs/non_tty'
require 'ruby-progressbar/outputs/tty'
require 'ruby-progressbar/progress'
require 'ruby-progressbar/projector'
require 'ruby-progressbar/timer'

class ProgressBar
class Base
extend Forwardable

# rubocop:disable Layout/HeredocIndentation
SMOOTHING_DEPRECATION_WARNING = <<-HEREDOC.tr("\n", ' ')
WARNING: Passing the 'smoothing' option is deprecated and will be removed
in version 2.0. Please pass { projector: { type: 'smoothing', strength: 0.x }}.
For more information on why this change is happening, visit
https://github.com/jfelchner/ruby-progressbar/wiki/Upgrading
HEREDOC

RUNNING_AVERAGE_RATE_DEPRECATION_WARNING = <<-HEREDOC.tr("\n", ' ')
WARNING: Passing the 'running_average_rate' option is deprecated and will be removed
in version 2.0. Please pass { projector: { type: 'smoothing', strength: 0.x }}.
For more information on why this change is happening, visit
https://github.com/jfelchner/ruby-progressbar/wiki/Upgrading
HEREDOC
# rubocop:enable Layout/HeredocIndentation

def_delegators :output,
:clear,
:log,
Expand All @@ -26,15 +43,34 @@ class Base
:total

def initialize(options = {}) # rubocop:disable Metrics/AbcSize
options[:projector] ||= {}

self.autostart = options.fetch(:autostart, true)
self.autofinish = options.fetch(:autofinish, true)
self.finished = false

self.timer = Timer.new(options)
projector_opts = if options[:projector].any?
options[:projector]
elsif options[:smoothing]
warn SMOOTHING_DEPRECATION_WARNING

{ :strength => options[:smoothing] }
elsif options[:running_average_rate]
warn RUNNING_AVERAGE_RATE_DEPRECATION_WARNING

{ :strength => options[:smoothing] }
else
{}
end
self.projector = Projector.
from_type(options[:projector][:type]).
new(projector_opts)
self.progressable = Progress.new(options)

options = options.merge(:progress => progressable,
:timer => timer)
options = options.merge(:progress => progressable,
:projector => projector,
:timer => timer)

self.title_component = Components::Title.new(options)
self.bar_component = Components::Bar.new(options)
Expand Down Expand Up @@ -79,6 +115,7 @@ def reset
output.with_refresh do
self.finished = false
progressable.reset
projector.reset
timer.reset
end
end
Expand Down Expand Up @@ -174,6 +211,7 @@ def format=(other)
protected

attr_accessor :output,
:projector,
:timer,
:progressable,
:title_component,
Expand All @@ -188,6 +226,7 @@ def format=(other)
def update_progress(*args)
output.with_refresh do
progressable.__send__(*args)
projector.__send__(*args)
timer.stop if finished?
end
end
Expand Down
9 changes: 0 additions & 9 deletions lib/ruby-progressbar/calculators/smoothed_average.rb

This file was deleted.

12 changes: 7 additions & 5 deletions lib/ruby-progressbar/components/time.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ class Time
}.freeze

def initialize(options = {})
self.timer = options[:timer]
self.progress = options[:progress]
self.timer = options[:timer]
self.progress = options[:progress]
self.projector = options[:projector]
end

def estimated_with_label(out_of_bounds_time_format = nil)
Expand Down Expand Up @@ -57,7 +58,8 @@ def estimated_wall_clock
protected

attr_accessor :timer,
:progress
:progress,
:projector

private

Expand Down Expand Up @@ -90,9 +92,9 @@ def estimated_with_elapsed_fallback(out_of_bounds_time_format)
end

def estimated_seconds_remaining
return if progress.unknown? || progress.none? || timer.stopped? || timer.reset?
return if progress.unknown? || projector.none? || progress.none? || timer.stopped? || timer.reset?

(timer.elapsed_seconds * ((progress.total / progress.running_average) - 1)).round
(timer.elapsed_seconds * ((progress.total / projector.projection) - 1)).round
end
end
end
Expand Down
40 changes: 9 additions & 31 deletions lib/ruby-progressbar/progress.rb
Original file line number Diff line number Diff line change
@@ -1,40 +1,22 @@
require 'ruby-progressbar/errors/invalid_progress_error'
require 'ruby-progressbar/calculators/smoothed_average'

class ProgressBar
class Progress
DEFAULT_TOTAL = 100
DEFAULT_BEGINNING_POSITION = 0
DEFAULT_RUNNING_AVERAGE_RATE = 0.1
DEFAULT_RUNNING_AVERAGE_CALCULATOR = ProgressBar::Calculators::SmoothedAverage

RUNNING_AVERAGE_CALCULATOR_MAP = {
'smoothing' => ProgressBar::Calculators::SmoothedAverage
}.freeze
DEFAULT_TOTAL = 100
DEFAULT_BEGINNING_POSITION = 0

attr_reader :total,
:progress

attr_accessor :starting_position,
:running_average,
:running_average_calculator,
:running_average_rate
attr_accessor :starting_position

def initialize(options = {})
self.total = options.fetch(:total, DEFAULT_TOTAL)
self.running_average_rate = options[:smoothing] ||
options[:running_average_rate] ||
DEFAULT_RUNNING_AVERAGE_RATE
self.running_average_calculator = RUNNING_AVERAGE_CALCULATOR_MAP.
fetch(options[:running_average_calculator],
DEFAULT_RUNNING_AVERAGE_CALCULATOR)

start :at => DEFAULT_BEGINNING_POSITION
self.total = options.fetch(:total, DEFAULT_TOTAL)

start(:at => DEFAULT_BEGINNING_POSITION)
end

def start(options = {})
self.running_average = 0
self.progress = \
self.progress = \
self.starting_position = options[:at] || progress
end

Expand Down Expand Up @@ -67,7 +49,7 @@ def decrement
end

def reset
start :at => starting_position
start(:at => starting_position)
end

def progress=(new_progress)
Expand All @@ -77,10 +59,6 @@ def progress=(new_progress)
end

@progress = new_progress

self.running_average = running_average_calculator.calculate(running_average,
absolute,
running_average_rate)
end

def total=(new_total)
Expand All @@ -105,7 +83,7 @@ def percentage_completed
end

def none?
running_average.zero? || progress.zero?
progress.zero?
end

def unknown?
Expand Down
14 changes: 14 additions & 0 deletions lib/ruby-progressbar/projector.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require 'ruby-progressbar/projectors/smoothed_average'

class ProgressBar
class Projector
DEFAULT_PROJECTOR = ProgressBar::Projectors::SmoothedAverage
NAME_TO_PROJECTOR_MAP = {
'smoothed' => ProgressBar::Projectors::SmoothedAverage
}.freeze

def self.from_type(name)
NAME_TO_PROJECTOR_MAP.fetch(name, DEFAULT_PROJECTOR)
end
end
end
71 changes: 71 additions & 0 deletions lib/ruby-progressbar/projectors/smoothed_average.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
class ProgressBar
module Projectors
class SmoothedAverage
DEFAULT_STRENGTH = 0.1
DEFAULT_BEGINNING_POSITION = 0

attr_accessor :samples,
:strength
attr_reader :projection

def initialize(options = {})
self.samples = []
self.projection = 0.0
self.strength = options[:strength] || DEFAULT_STRENGTH

start(:at => DEFAULT_BEGINNING_POSITION)
end

def start(options = {})
self.projection = 0.0
self.progress = samples[0] = (options[:at] || progress)
end

def decrement
self.progress -= 1
end

def increment
self.progress += 1
end

def progress
samples[1]
end

def total=(_new_total); end

def reset
start(:at => samples[0])
end

def progress=(new_progress)
samples[1] = new_progress
self.projection = \
self.class.calculate(
@projection,
absolute,
strength
)
end

def none?
projection.zero?
end

def self.calculate(current_projection, new_value, rate)
(new_value * (1.0 - rate)) + (current_projection * rate)
end

protected

attr_writer :projection

private

def absolute
samples[1] - samples[0]
end
end
end
end
30 changes: 28 additions & 2 deletions spec/lib/ruby-progressbar/base_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ class ProgressBar
Timecop.freeze(::Time.utc(2020, 1, 1, 0, 0, 0))

progressbar = ProgressBar::Base.new(:output => output,
:smoothing => 0.0,
:projector => { :strength => 0.0 },
:total => 100,
:format => '%l')

Expand All @@ -251,7 +251,7 @@ class ProgressBar
Timecop.freeze(::Time.utc(2020, 1, 1, 0, 0, 0))

progressbar = ProgressBar::Base.new(:output => output,
:smoothing => 0.0,
:projector => { :strength => 0.0 },
:total => 100,
:format => '%l')

Expand All @@ -275,6 +275,32 @@ class ProgressBar
not_to raise_error
end

# rubocop:disable RSpec/AnyInstance
it 'displays a warning if the user passes the deprecated "smoothing" option but ' \
'still processes it' do
expect_any_instance_of(ProgressBar::Base).
to receive(:warn).
with(include("WARNING: Passing the 'smoothing' option is deprecated"))

Timecop.freeze(::Time.utc(2020, 1, 1, 0, 0, 0))

progressbar = ProgressBar::Base.new(:output => output,
:smoothing => 0.0,
:total => 100,
:format => '%l')

progressbar.progress += 50

Timecop.freeze(::Time.utc(2020, 1, 1, 0, 30, 0))

progressbar.finish

Timecop.return

expect(progressbar.to_s).to eql '00:30:00'
end
# rubocop:enable RSpec/AnyInstance

it 'appends proper ending to string for non-TTY devices' do
progressbar = ProgressBar::Base.new(:output => non_tty_output)

Expand Down
19 changes: 0 additions & 19 deletions spec/lib/ruby-progressbar/calculators/smoothed_average_spec.rb

This file was deleted.

Loading

0 comments on commit 2bc31d0

Please sign in to comment.