In Progress
Unit 1, Lesson 21
In Progress

Progress Bar

Video transcript & code

In the last episode we benchmarked the accuracy of the #sleep method. When we started using very short timeouts, we started to see significant "drift" in the length of sleeps. As a result, the benchmark took a lot longer to run than we expected.

require "ruby-progressbar"
require "thread"
require "hitimes"

duration = ARGV[0].to_f
seconds  = ARGV[1].to_f

repeat = (seconds / duration).to_i
samples = []
total_duration = 0.0
repeat.times do
  actual_duration = Hitimes::Interval.measure do
    sleep duration
  end
  samples << actual_duration
  total_duration += actual_duration
end

min = samples.min
max = samples.max
sum = samples.reduce(:+)
avg = sum / samples.size
sorted = samples.sort
half_size = sorted.size / 2
q, r = sorted.size.divmod(2)
middle = sorted[q - 1 + r, 2 - r]
med = middle.reduce(:+) / middle.size
std = Math.sqrt(samples.map{|n| (n - avg) ** 2}.reduce(:+) / samples.size)

puts "Sleep duration: #{duration}"
puts "Repeat: #{repeat}"
puts "Ideal length: #{duration * repeat} seconds"
puts "Actual length: #{total_duration} seconds"
puts "Samples: #{samples.size}"
puts "Min: #{samples.min}"
puts "Max: #{samples.max}"
puts "Avg: #{avg}"
puts "Median: #{med}"
puts "Std dev: #{std}"

When we run a script like this, one that works silently and might take some time to complete, it can be nice to have some reassurance that something is actually happening and that the program isn't simply hung up. Enter the ruby-progressbar gem.

This gem does exactly what it sounds like: it gives us an animated progressbar to showing live feedback on the state of a long process. And it does this with an incredibly easy-to-use API. First, we create a progressbar, with an option specifying what number of increments will indicate that the process has finished. If we didn't specify this, it would default to 100.

Then we add a line inside the repeat loop, which increments the progress bar on each repetition.

require "ruby-progressbar"
require "thread"
require "hitimes"
require "ruby-progressbar"

duration = ARGV[0].to_f
seconds  = ARGV[1].to_f

repeat = (seconds / duration).to_i
samples = []
total_duration = 0.0
progress = ProgressBar.create(total: repeat)
repeat.times do
  actual_duration = Hitimes::Interval.measure do
    sleep duration
  end
  samples << actual_duration
  total_duration += actual_duration
  progress.increment
end

min = samples.min
max = samples.max
sum = samples.reduce(:+)
avg = sum / samples.size
sorted = samples.sort
half_size = sorted.size / 2
q, r = sorted.size.divmod(2)
middle = sorted[q - 1 + r, 2 - r]
med = middle.reduce(:+) / middle.size
std = Math.sqrt(samples.map{|n| (n - avg) ** 2}.reduce(:+) / samples.size)

puts "Sleep duration: #{duration}"
puts "Repeat: #{repeat}"
puts "Ideal length: #{duration * repeat} seconds"
puts "Actual length: #{total_duration} seconds"
puts "Samples: #{samples.size}"
puts "Min: #{samples.min}"
puts "Max: #{samples.max}"
puts "Avg: #{avg}"
puts "Median: #{med}"
puts "Std dev: #{std}"

And that's it—just two lines of code. When we run this code, we see a progress bar steadily fill before being presented with the results.

$ ruby test-sleep-progress.rb 0.0001 1
Progress: |=========================================================================================================================================|
Sleep duration: 0.0001
Repeat: 10000
Ideal length: 1.0 seconds
Actual length: 1.6204931379999945 seconds
Samples: 10000
Min: 0.000110278
Max: 0.002985862
Avg: 0.00016204931379999946
Median: 0.000159311
Std dev: 3.8195301729821856e-05

ruby-progressbar can do a lot more than this. Check out the documentation for the many possibilities, including the ability to stop and pause the bar before it reaches the end, the ability to show a "please wait" animation when the ending time can't be predicted, and much more.

ruby-progressbar does one thing and it does it well. It's one of the gems that I think every Ruby programmer should have installed in their global gemset, because with just a couple of lines of code it can dramatically improve the user experience of command-line scripts.

And that's it for today. Happy hacking!

Responses