philiprehberger-progress 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -16
- data/README.md +65 -3
- data/lib/philiprehberger/progress/bar.rb +84 -4
- data/lib/philiprehberger/progress/spinner.rb +1 -1
- data/lib/philiprehberger/progress/version.rb +1 -1
- data/lib/philiprehberger/progress.rb +16 -2
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8cd9b24ec4af77caa0242168972123e46f8aefe0aa7d010f355f634462d4e7ad
|
|
4
|
+
data.tar.gz: 956912f23898fd45c2434b06030aea103af6d33f390f11fc432a7cf285983325
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 79fcad8d626d2869ac6c3bfa9d29ec243c134f7c140dfca896282c940bb82b3879d25378b229162441708d13fc204c6e912c5f802fef0b9331fc4bd9bb845889
|
|
7
|
+
data.tar.gz: d0074a78f1adfa22c1c749bf6367c468dfdfb1fa011d56ab88b83faedcc86625ada2f5982430cdc2f69013c66ead9bd030058d0b538959b3778cbfc94c86cb9c
|
data/CHANGELOG.md
CHANGED
|
@@ -2,11 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this gem will be documented in this file.
|
|
4
4
|
|
|
5
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.
|
|
6
|
-
and this
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.5.0] - 2026-04-14
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `Bar#pause` and `Bar#resume` to freeze and unfreeze elapsed time calculation
|
|
14
|
+
- `Bar#to_h` returns a hash with `:percentage`, `:elapsed`, `:eta`, `:throughput`, `:current`, `:total`
|
|
15
|
+
- Custom bar characters via `fill:`, `empty:`, `tip:` keyword arguments (defaults: `fill: '='`, `empty: ' '`, `tip: '>'`)
|
|
16
|
+
- `Progress.json_mode!` and `Progress.text_mode!` to toggle JSON line output for bar rendering
|
|
17
|
+
|
|
18
|
+
## [0.4.0] - 2026-04-09
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- `Bar#set(n)` to set absolute progress position
|
|
22
|
+
- `Bar#reset` to restart the bar from 0, preserving configuration
|
|
23
|
+
- `Spinner#message=` to update the spinner message dynamically
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- Standardize CHANGELOG header format to match template
|
|
27
|
+
|
|
10
28
|
## [0.3.0] - 2026-04-09
|
|
11
29
|
|
|
12
30
|
### Added
|
|
@@ -92,17 +110,3 @@ and this gem adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
|
92
110
|
- `Progress.spin` convenience method with block support
|
|
93
111
|
- `Progress.each` for iterating enumerables with progress display
|
|
94
112
|
- TTY detection to auto-disable rendering in non-terminal environments
|
|
95
|
-
|
|
96
|
-
[0.3.0]: https://github.com/philiprehberger/rb-progress/releases/tag/v0.3.0
|
|
97
|
-
[0.2.0]: https://github.com/philiprehberger/rb-progress/releases/tag/v0.2.0
|
|
98
|
-
[0.1.11]: https://github.com/philiprehberger/rb-progress/releases/tag/v0.1.11
|
|
99
|
-
[0.1.10]: https://github.com/philiprehberger/rb-progress/releases/tag/v0.1.10
|
|
100
|
-
[0.1.9]: https://github.com/philiprehberger/rb-progress/releases/tag/v0.1.9
|
|
101
|
-
[0.1.8]: https://github.com/philiprehberger/rb-progress/releases/tag/v0.1.8
|
|
102
|
-
[0.1.7]: https://github.com/philiprehberger/rb-progress/releases/tag/v0.1.7
|
|
103
|
-
[0.1.6]: https://github.com/philiprehberger/rb-progress/releases/tag/v0.1.6
|
|
104
|
-
[0.1.5]: https://github.com/philiprehberger/rb-progress/releases/tag/v0.1.5
|
|
105
|
-
[0.1.4]: https://github.com/philiprehberger/rb-progress/releases/tag/v0.1.4
|
|
106
|
-
[0.1.3]: https://github.com/philiprehberger/rb-progress/releases/tag/v0.1.3
|
|
107
|
-
[0.1.2]: https://github.com/philiprehberger/rb-progress/releases/tag/v0.1.2
|
|
108
|
-
[0.1.0]: https://github.com/philiprehberger/rb-progress/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -81,6 +81,58 @@ end
|
|
|
81
81
|
|
|
82
82
|
The thread is joined automatically when `stop` is called (or when the block completes).
|
|
83
83
|
|
|
84
|
+
### Pause and Resume
|
|
85
|
+
|
|
86
|
+
Pause the progress bar to freeze elapsed time calculation (e.g. while waiting for user input):
|
|
87
|
+
|
|
88
|
+
```ruby
|
|
89
|
+
bar = Philiprehberger::Progress::Bar.new(total: 100)
|
|
90
|
+
50.times { bar.advance }
|
|
91
|
+
bar.pause
|
|
92
|
+
# ... elapsed time is frozen ...
|
|
93
|
+
bar.resume
|
|
94
|
+
50.times { bar.advance }
|
|
95
|
+
bar.finish
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Custom Bar Characters
|
|
99
|
+
|
|
100
|
+
Customize the fill, empty, and tip characters:
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
bar = Philiprehberger::Progress::Bar.new(total: 100, fill: '#', empty: '.', tip: '>')
|
|
104
|
+
50.times { bar.advance }
|
|
105
|
+
bar.to_s
|
|
106
|
+
# [########################>.........................] 50.0% | 50/100 | ETA: 0s | 50.0/s
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Default characters are `fill: '='`, `empty: ' '`, `tip: '>'`.
|
|
110
|
+
|
|
111
|
+
### Data Export
|
|
112
|
+
|
|
113
|
+
Export the current state as a hash:
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
bar = Philiprehberger::Progress::Bar.new(total: 100)
|
|
117
|
+
50.times { bar.advance }
|
|
118
|
+
bar.to_h
|
|
119
|
+
# => { percentage: 50.0, elapsed: 1.2, eta: 1.2, throughput: 41.7, current: 50, total: 100 }
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### JSON Mode
|
|
123
|
+
|
|
124
|
+
Switch to JSON line output for machine-readable progress:
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
Philiprehberger::Progress.json_mode!
|
|
128
|
+
bar = Philiprehberger::Progress::Bar.new(total: 100)
|
|
129
|
+
50.times { bar.advance }
|
|
130
|
+
bar.to_s
|
|
131
|
+
# {"percentage":50.0,"elapsed":1.2,"eta":1.2,"throughput":41.7,"current":50,"total":100}
|
|
132
|
+
|
|
133
|
+
Philiprehberger::Progress.text_mode! # revert to ANSI bar
|
|
134
|
+
```
|
|
135
|
+
|
|
84
136
|
### Enumerable Integration
|
|
85
137
|
|
|
86
138
|
```ruby
|
|
@@ -121,21 +173,28 @@ multi.finished? # => true
|
|
|
121
173
|
|
|
122
174
|
| Method | Description |
|
|
123
175
|
|--------|-------------|
|
|
124
|
-
| `.new(total:, width: 30, output: $stderr)` | Create a progress bar |
|
|
176
|
+
| `.new(total:, width: 30, output: $stderr, fill: '=', empty: ' ', tip: '>')` | Create a progress bar |
|
|
125
177
|
| `#advance(n = 1)` | Advance by `n` items |
|
|
178
|
+
| `#set(n)` | Set absolute progress position (clamped to 0..total) |
|
|
179
|
+
| `#reset` | Reset to 0, clear finished state, restart timer |
|
|
180
|
+
| `#pause` | Pause the bar, freezing elapsed time |
|
|
181
|
+
| `#resume` | Resume after pause |
|
|
182
|
+
| `#paused?` | Whether the bar is paused |
|
|
126
183
|
| `#finish` | Mark as complete |
|
|
127
184
|
| `#finished?` | Whether the bar is finished |
|
|
128
185
|
| `#percentage` | Current percentage (0.0 to 100.0) |
|
|
129
|
-
| `#elapsed` | Elapsed time in seconds |
|
|
186
|
+
| `#elapsed` | Elapsed time in seconds (excludes paused time) |
|
|
130
187
|
| `#eta` | Estimated time remaining in seconds |
|
|
131
188
|
| `#throughput` | Items per second |
|
|
132
|
-
| `#
|
|
189
|
+
| `#to_h` | Hash with `:percentage`, `:elapsed`, `:eta`, `:throughput`, `:current`, `:total` |
|
|
190
|
+
| `#to_s` | Render the bar as a string (or JSON line in json_mode) |
|
|
133
191
|
|
|
134
192
|
### `Philiprehberger::Progress::Spinner`
|
|
135
193
|
|
|
136
194
|
| Method | Description |
|
|
137
195
|
|--------|-------------|
|
|
138
196
|
| `.new(message:, output: $stderr)` | Create a spinner |
|
|
197
|
+
| `#message=` | Update the spinner message dynamically |
|
|
139
198
|
| `#spin` | Advance to the next frame |
|
|
140
199
|
| `#auto_spin(interval: 0.1)` | Start background thread animation |
|
|
141
200
|
| `#stop(final_message = 'done')` | Stop with a message (joins background thread) |
|
|
@@ -164,6 +223,9 @@ multi.finished? # => true
|
|
|
164
223
|
| `Progress.multi(output: $stderr, &block)` | Create multi-bar tracker |
|
|
165
224
|
| `Progress.each(enumerable, label: nil) { \|item\| }` | Iterate with progress |
|
|
166
225
|
| `Progress.map(enumerable, label: nil) { \|item\| }` | Transform with progress, returns results |
|
|
226
|
+
| `Progress.json_mode!` | Switch bar rendering to JSON line output |
|
|
227
|
+
| `Progress.text_mode!` | Switch bar rendering back to ANSI text |
|
|
228
|
+
| `Progress.json_mode?` | Whether JSON mode is active |
|
|
167
229
|
|
|
168
230
|
## Development
|
|
169
231
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
3
5
|
module Philiprehberger
|
|
4
6
|
module Progress
|
|
5
7
|
class Bar
|
|
@@ -8,13 +10,19 @@ module Philiprehberger
|
|
|
8
10
|
|
|
9
11
|
attr_reader :current, :total
|
|
10
12
|
|
|
11
|
-
def initialize(total:, width: 30, output: $stderr)
|
|
13
|
+
def initialize(total:, width: 30, output: $stderr, fill: '=', empty: ' ', tip: '>')
|
|
12
14
|
@total = [total, 0].max
|
|
13
15
|
@width = width
|
|
14
16
|
@output = output
|
|
15
17
|
@current = 0
|
|
16
18
|
@start_time = now
|
|
17
19
|
@finished = false
|
|
20
|
+
@paused = false
|
|
21
|
+
@pause_elapsed = 0.0
|
|
22
|
+
@pause_start = nil
|
|
23
|
+
@fill = fill
|
|
24
|
+
@empty = empty
|
|
25
|
+
@tip = tip
|
|
18
26
|
end
|
|
19
27
|
|
|
20
28
|
def advance(n = 1)
|
|
@@ -25,6 +33,58 @@ module Philiprehberger
|
|
|
25
33
|
self
|
|
26
34
|
end
|
|
27
35
|
|
|
36
|
+
# Set absolute progress position.
|
|
37
|
+
#
|
|
38
|
+
# @param n [Integer] the new current value (clamped to 0..total)
|
|
39
|
+
# @return [self]
|
|
40
|
+
def set(n)
|
|
41
|
+
return self if @finished
|
|
42
|
+
|
|
43
|
+
@current = [[n, 0].max, @total].min
|
|
44
|
+
render_to_output
|
|
45
|
+
self
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Reset the bar to 0, preserving total and width. Restarts the timer.
|
|
49
|
+
#
|
|
50
|
+
# @return [self]
|
|
51
|
+
def reset
|
|
52
|
+
@current = 0
|
|
53
|
+
@finished = false
|
|
54
|
+
@paused = false
|
|
55
|
+
@pause_elapsed = 0.0
|
|
56
|
+
@pause_start = nil
|
|
57
|
+
@start_time = now
|
|
58
|
+
self
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Pause the progress bar, freezing elapsed time calculation.
|
|
62
|
+
#
|
|
63
|
+
# @return [self]
|
|
64
|
+
def pause
|
|
65
|
+
return self if @paused || @finished
|
|
66
|
+
|
|
67
|
+
@paused = true
|
|
68
|
+
@pause_start = now
|
|
69
|
+
self
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Resume after pause.
|
|
73
|
+
#
|
|
74
|
+
# @return [self]
|
|
75
|
+
def resume
|
|
76
|
+
return self unless @paused
|
|
77
|
+
|
|
78
|
+
@pause_elapsed += now - @pause_start
|
|
79
|
+
@pause_start = nil
|
|
80
|
+
@paused = false
|
|
81
|
+
self
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def paused?
|
|
85
|
+
@paused
|
|
86
|
+
end
|
|
87
|
+
|
|
28
88
|
def finish
|
|
29
89
|
@current = @total
|
|
30
90
|
@finished = true
|
|
@@ -44,7 +104,9 @@ module Philiprehberger
|
|
|
44
104
|
end
|
|
45
105
|
|
|
46
106
|
def elapsed
|
|
47
|
-
now - @start_time
|
|
107
|
+
raw = now - @start_time - @pause_elapsed
|
|
108
|
+
raw -= (now - @pause_start) if @paused
|
|
109
|
+
raw
|
|
48
110
|
end
|
|
49
111
|
|
|
50
112
|
def eta
|
|
@@ -63,7 +125,20 @@ module Philiprehberger
|
|
|
63
125
|
@current.to_f / elapsed_time
|
|
64
126
|
end
|
|
65
127
|
|
|
128
|
+
def to_h
|
|
129
|
+
{
|
|
130
|
+
percentage: percentage,
|
|
131
|
+
elapsed: elapsed,
|
|
132
|
+
eta: eta,
|
|
133
|
+
throughput: throughput,
|
|
134
|
+
current: @current,
|
|
135
|
+
total: @total
|
|
136
|
+
}
|
|
137
|
+
end
|
|
138
|
+
|
|
66
139
|
def to_s
|
|
140
|
+
return to_h.to_json if Philiprehberger::Progress.json_mode?
|
|
141
|
+
|
|
67
142
|
bar_str = render_bar
|
|
68
143
|
pct = format('%<p>5.1f%%', p: percentage)
|
|
69
144
|
eta_str = format_eta(eta)
|
|
@@ -87,11 +162,16 @@ module Philiprehberger
|
|
|
87
162
|
end
|
|
88
163
|
|
|
89
164
|
def render_bar
|
|
90
|
-
return
|
|
165
|
+
return @fill * @width if @total.zero?
|
|
91
166
|
|
|
92
167
|
filled = (@current.to_f / @total * @width).round
|
|
93
168
|
empty = @width - filled
|
|
94
|
-
|
|
169
|
+
|
|
170
|
+
if filled.positive? && filled < @width
|
|
171
|
+
(@fill * (filled - 1)) + @tip + (@empty * empty)
|
|
172
|
+
else
|
|
173
|
+
(@fill * filled) + (@empty * empty)
|
|
174
|
+
end
|
|
95
175
|
end
|
|
96
176
|
|
|
97
177
|
def format_eta(seconds)
|
|
@@ -7,8 +7,22 @@ require_relative 'progress/multi'
|
|
|
7
7
|
|
|
8
8
|
module Philiprehberger
|
|
9
9
|
module Progress
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
@json_mode = false
|
|
11
|
+
|
|
12
|
+
def self.json_mode!
|
|
13
|
+
@json_mode = true
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.text_mode!
|
|
17
|
+
@json_mode = false
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.json_mode?
|
|
21
|
+
@json_mode
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.bar(total:, width: 30, output: $stderr, fill: '=', empty: ' ', tip: '>')
|
|
25
|
+
progress_bar = Bar.new(total: total, width: width, output: output, fill: fill, empty: empty, tip: tip)
|
|
12
26
|
|
|
13
27
|
if block_given?
|
|
14
28
|
result = yield progress_bar
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: philiprehberger-progress
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Philip Rehberger
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-04-
|
|
11
|
+
date: 2026-04-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Display progress bars with percentage, ETA, and throughput, or spinners
|
|
14
14
|
for indeterminate tasks. Supports block-based usage, enumerable iteration with each/map,
|