philiprehberger-progress
Terminal progress bars and spinners with ETA calculation and throughput display
Requirements
- Ruby >= 3.1
Installation
Add to your Gemfile:
gem "philiprehberger-progress"Or install directly:
gem install philiprehberger-progressUsage
Progress Bar
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/sBlock Usage
Philiprehberger::Progress.bar(total: 100) do |bar|
100.times { bar.advance }
end
# Auto-finishes when block completesSpinner
spinner = Philiprehberger::Progress::Spinner.new(message: 'Loading...')
10.times do
sleep(0.1)
spinner.spin
end
spinner.stop('done')Block Spinner
Philiprehberger::Progress.spin('Processing...') do |spinner|
10.times { spinner.spin; sleep(0.1) }
endAuto-spinning
Start a background thread that animates the spinner automatically:
Philiprehberger::Progress.spin("Deploying...") do |spinner|
spinner.auto_spin
deploy! # spinner animates while you work
endThe thread is joined automatically when stop is called (or when the block completes).
Pause and Resume
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.finishCustom Bar Characters
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/sDefault characters are fill: '=', empty: ' ', tip: '>'.
Data Export
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 }JSON Mode
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 barEnumerable Integration
items = (1..100).to_a
Philiprehberger::Progress.each(items) do |item|
sleep(0.01)
endMapping with Progress
Transform items while displaying progress:
results = Philiprehberger::Progress.map(urls) { |url| fetch(url) }
# results contains the return values from each block callMulti-bar
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? # => trueAPI
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 |
#to_h |
Hash with :percentage, :elapsed, :eta, :throughput, :current, :total
|
#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 |
Module Methods
| 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 |
Development
bundle install
bundle exec rspec
bundle exec rubocopSupport
If you find this project useful: