Terminal progress bars and spinners with ETA calculation and throughput display
gem install philiprehberger-progressTerminal progress bars and spinners with ETA calculation and throughput display
Add to your Gemfile:
gem "philiprehberger-progress"
Or install directly:
gem install philiprehberger-progress
require "philiprehberger/progress"
bar = Philiprehberger::Progress::Bar.new(total: 100)
100.times do
sleep(0.01)
bar.advance
end
bar.finish
# [████████████████████████████████] 100.0% | 100/100 | ETA: 0s | 100.0/s
Philiprehberger::Progress.bar(total: 100) do |bar|
100.times { bar.advance }
end
# Auto-finishes when block completes
spinner = Philiprehberger::Progress::Spinner.new(message: 'Loading...')
10.times do
sleep(0.1)
spinner.spin
end
spinner.stop('done')
Philiprehberger::Progress.spin('Processing...') do |spinner|
10.times { spinner.spin; sleep(0.1) }
end
Start a background thread that animates the spinner automatically:
Philiprehberger::Progress.spin("Deploying...") do |spinner|
spinner.auto_spin
deploy! # spinner animates while you work
end
The thread is joined automatically when stop is called (or when the block completes).
Pause the progress bar to freeze elapsed time calculation (e.g. while waiting for user input):
bar = Philiprehberger::Progress::Bar.new(total: 100)
50.times { bar.advance }
bar.pause
# ... elapsed time is frozen ...
bar.resume
50.times { bar.advance }
bar.finish
Customize the fill, empty, and tip characters:
bar = Philiprehberger::Progress::Bar.new(total: 100, fill: '#', empty: '.', tip: '>')
50.times { bar.advance }
bar.to_s
# [########################>.........................] 50.0% | 50/100 | ETA: 0s | 50.0/s
Default characters are fill: '=', empty: ' ', tip: '>'.
Export the current state as a hash:
bar = Philiprehberger::Progress::Bar.new(total: 100)
50.times { bar.advance }
bar.to_h
# => { percentage: 50.0, elapsed: 1.2, eta: 1.2, throughput: 41.7, current: 50, total: 100, remaining: 50 }
bar.remaining # => 50
Switch to JSON line output for machine-readable progress:
Philiprehberger::Progress.json_mode!
bar = Philiprehberger::Progress::Bar.new(total: 100)
50.times { bar.advance }
bar.to_s
# {"percentage":50.0,"elapsed":1.2,"eta":1.2,"throughput":41.7,"current":50,"total":100}
Philiprehberger::Progress.text_mode! # revert to ANSI bar
items = (1..100).to_a
Philiprehberger::Progress.each(items) do |item|
sleep(0.01)
end
Transform items while displaying progress:
results = Philiprehberger::Progress.map(urls) { |url| fetch(url) }
# results contains the return values from each block call
require "philiprehberger/progress"
multi = Philiprehberger::Progress.multi do |m|
downloads = m.add("Downloads", total: 100)
uploads = m.add("Uploads", total: 50)
100.times { downloads.advance }
50.times { uploads.advance }
end
multi.finished? # => true
Philiprehberger::Progress::Bar| Method | Description |
|---|---|
.new(total:, width: 30, output: $stderr, fill: '=', empty: ' ', tip: '>') | Create a progress bar |
#advance(n = 1) | Advance by n items |
#set(n) | Set absolute progress position (clamped to 0..total) |
#reset | Reset to 0, clear finished state, restart timer |
#pause | Pause the bar, freezing elapsed time |
#resume | Resume after pause |
#paused? | Whether the bar is paused |
#finish | Mark as complete |
#finished? | Whether the bar is finished |
#percentage | Current percentage (0.0 to 100.0) |
#elapsed | Elapsed time in seconds (excludes paused time) |
#eta | Estimated time remaining in seconds |
#throughput | Items per second |
#remaining | Number of steps remaining (clamped to non-negative) |
#to_h | Hash with :percentage, :elapsed, :eta, :throughput, :current, :total, :remaining |
#to_s | Render the bar as a string (or JSON line in json_mode) |
Philiprehberger::Progress::Spinner| Method | Description |
|---|---|
.new(message:, output: $stderr) | Create a spinner |
#message= | Update the spinner message dynamically |
#spin | Advance to the next frame |
#auto_spin(interval: 0.1) | Start background thread animation |
#stop(final_message = 'done') | Stop with a message (joins background thread) |
#stopped? | Whether the spinner is stopped |
#to_s | Render the current frame with message |
Philiprehberger::Progress::Multi| Method | Description |
|---|---|
Multi.new(output: $stderr) | Create multi-bar tracker |
Multi#add(label, total:, width: 30) | Add a named progress bar |
Multi#[](label) | Retrieve a bar by label |
Multi#labels | List of bar labels in order |
Multi#bars | Hash of label to bar |
Multi#finished? | True when all bars are finished |
Multi#render | Render all bars to output |
Multi#reset | Clear all bars |
| Method | Description |
|---|---|
Progress.bar(total:, &block) | Create bar, auto-finish after block |
Progress.spin(message, &block) | Create spinner, auto-stop after block |
Progress.multi(output: $stderr, &block) | Create multi-bar tracker |
Progress.each(enumerable, label: nil) { |item| } | Iterate with progress |
Progress.map(enumerable, label: nil) { |item| } | Transform with progress, returns results |
Progress.json_mode! | Switch bar rendering to JSON line output |
Progress.text_mode! | Switch bar rendering back to ANSI text |
Progress.json_mode? | Whether JSON mode is active |
bundle install
bundle exec rspec
bundle exec rubocop
If you find this project useful: