Am I being obtuse, or is there a usage gap?
Current Problem
Given an external "black box" library call that follows the pattern of calling an optional block to report progress
result = lib_obj.do_big_slow_thing(params) { |progress| do_something_with_progress(progress) }
and feeds in a float between 0 and 1, representing percentage done, is there a way of making ruby-progressbar track this progress while still keeping ETA math relatively sane?
What I ended up doing was writing a wrapper around the bar, and incrementing the bar when we were on a new "integer percentage", like thus:
class ProgressWrapper
def initialize(format: "%a |%b>>%i| %p%% %t %E")
@bar = ProgressBar.create(format: format)
@showing = 0
@start = Time.now
end
# @param prog [Float] current progress between `0` and `1`
def feed(prog)
candidate = prog_to_perc(prog)
if candidate > @showing
@showing = candidate
@bar.increment
end
@bar.refresh
end
def finish
@bar.finish
@finish = Time.now
end
def duration
return unless @finish
@finish - @start
end
private
def prog_to_perc(prog)
(prog * 100).round
end
end
But.. my own bad code aside, it feels kludgy. Is there already a way to do this that I simply missed, or might it be an idea to provide a method to service this use-case, along the lines of bar.percentage_complete = progress?
Thanks for both your attention to this, and the awesome gem!
Sorry for the delay! Yeah! I'm honestly not entirely sure what you're trying to do, but I have some things that might be able to help.
First, you can explicitly set progress with progress= so if you did:
def feed(prog)
@bar.progress = prog * 100
end
That should do everything you need (including the refresh). The bar, by default has a total of 100. This was picked specifically for the default use case to be handy for percentages.
Additionally, the bar will autocomplete once it gets to the total so unless you want it to finish early, you don't have to call finish. Once the bar is completed (eg it gets to the total), you can check @bar.finished? so you don't have to store another variable there. Additionally you can get a large amount of bar data by calling @bar.to_h which includes the elapsed duration of the progress run.
Lastly, depending on how you're doing your looping, you may be interested in the enumerable refinement which can allow you to iterate over an enumerable and the bar will keep track of progress along the way for each loop.
Hope this helps!
Thanks for your reply, and my own apologies for the delay in getting back to you.
The above was an attempt at working around the caveat about #progress= messing up the estimated time - it keeps a parallel state and increments the bar's state only when a call to #increment is justified, rather than setting the progress to whatever it is, thereby messing up the ETA.
Or at least... that's the intent. Ultimately, any solution that accommodates the fact that progress will be an arbitrary (if monotonic) number, and doesn't mess up the ETA will be just peachy.
@manuelgomes I now have a better sense of what your issue is, but it's still a bit too abstract for me. Is there any way you can get me a repro case of what the library currently does compared to what you'd like it to do? Examples that include sleep statements to repro long running tasks is perfectly fine with me.
@manuelgomes I really thought I was keeping on top of things but apparently I was not. Please read this comment about new functionality I've added.
I think that if you can submit a PR that handles the use case you're trying to achieve (with a test), I'd be happy to work with you on trying to accomplish what you're trying to accomplish. I'm going to close this issue for now, but feel free to reference it if you open a PR. Thanks!
Actually I went even further with this. Check out this PR. It should now be extremely extensible for someone to submit a PR to be able to include a different projector to estimate the remaining time.