philiprehberger-progress 0.4.0 → 0.6.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 +13 -0
- data/README.md +64 -3
- data/lib/philiprehberger/progress/bar.rb +70 -4
- 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: fb855052746be6b7d6a120d7ad8b84ce16c8ccce47534463d04b698c680ea0fa
|
|
4
|
+
data.tar.gz: 2bf7bbafcf7feb5985c8baacc4a5eb10cbab3b5ee75f25d10a0512704230212a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0b5e4769b17f4a50df8fba852ad7a9eddd1821207051bc1cd3011a2a1ef71afd2369ea6d984be789cb95be6a3e2271caf4872b32180d96ed63ddd0538956c9c1
|
|
7
|
+
data.tar.gz: c71135352b91900baa613bd2b2c87edd51d1992bd57ef586734b3328fd9fb44c330c2a1af8ecb2827f0b29ba7180860e453ffbe1abfc4e7e1a25142c811d5dc4
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.6.0] - 2026-05-07
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `Bar#remaining` — number of steps remaining (clamped to non-negative). Also added to `Bar#to_h`.
|
|
14
|
+
|
|
15
|
+
## [0.5.0] - 2026-04-14
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- `Bar#pause` and `Bar#resume` to freeze and unfreeze elapsed time calculation
|
|
19
|
+
- `Bar#to_h` returns a hash with `:percentage`, `:elapsed`, `:eta`, `:throughput`, `:current`, `:total`
|
|
20
|
+
- Custom bar characters via `fill:`, `empty:`, `tip:` keyword arguments (defaults: `fill: '='`, `empty: ' '`, `tip: '>'`)
|
|
21
|
+
- `Progress.json_mode!` and `Progress.text_mode!` to toggle JSON line output for bar rendering
|
|
22
|
+
|
|
10
23
|
## [0.4.0] - 2026-04-09
|
|
11
24
|
|
|
12
25
|
### Added
|
data/README.md
CHANGED
|
@@ -81,6 +81,59 @@ 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, remaining: 50 }
|
|
120
|
+
bar.remaining # => 50
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### JSON Mode
|
|
124
|
+
|
|
125
|
+
Switch to JSON line output for machine-readable progress:
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
Philiprehberger::Progress.json_mode!
|
|
129
|
+
bar = Philiprehberger::Progress::Bar.new(total: 100)
|
|
130
|
+
50.times { bar.advance }
|
|
131
|
+
bar.to_s
|
|
132
|
+
# {"percentage":50.0,"elapsed":1.2,"eta":1.2,"throughput":41.7,"current":50,"total":100}
|
|
133
|
+
|
|
134
|
+
Philiprehberger::Progress.text_mode! # revert to ANSI bar
|
|
135
|
+
```
|
|
136
|
+
|
|
84
137
|
### Enumerable Integration
|
|
85
138
|
|
|
86
139
|
```ruby
|
|
@@ -121,17 +174,22 @@ multi.finished? # => true
|
|
|
121
174
|
|
|
122
175
|
| Method | Description |
|
|
123
176
|
|--------|-------------|
|
|
124
|
-
| `.new(total:, width: 30, output: $stderr)` | Create a progress bar |
|
|
177
|
+
| `.new(total:, width: 30, output: $stderr, fill: '=', empty: ' ', tip: '>')` | Create a progress bar |
|
|
125
178
|
| `#advance(n = 1)` | Advance by `n` items |
|
|
126
179
|
| `#set(n)` | Set absolute progress position (clamped to 0..total) |
|
|
127
180
|
| `#reset` | Reset to 0, clear finished state, restart timer |
|
|
181
|
+
| `#pause` | Pause the bar, freezing elapsed time |
|
|
182
|
+
| `#resume` | Resume after pause |
|
|
183
|
+
| `#paused?` | Whether the bar is paused |
|
|
128
184
|
| `#finish` | Mark as complete |
|
|
129
185
|
| `#finished?` | Whether the bar is finished |
|
|
130
186
|
| `#percentage` | Current percentage (0.0 to 100.0) |
|
|
131
|
-
| `#elapsed` | Elapsed time in seconds |
|
|
187
|
+
| `#elapsed` | Elapsed time in seconds (excludes paused time) |
|
|
132
188
|
| `#eta` | Estimated time remaining in seconds |
|
|
133
189
|
| `#throughput` | Items per second |
|
|
134
|
-
| `#
|
|
190
|
+
| `#remaining` | Number of steps remaining (clamped to non-negative) |
|
|
191
|
+
| `#to_h` | Hash with `:percentage`, `:elapsed`, `:eta`, `:throughput`, `:current`, `:total`, `:remaining` |
|
|
192
|
+
| `#to_s` | Render the bar as a string (or JSON line in json_mode) |
|
|
135
193
|
|
|
136
194
|
### `Philiprehberger::Progress::Spinner`
|
|
137
195
|
|
|
@@ -167,6 +225,9 @@ multi.finished? # => true
|
|
|
167
225
|
| `Progress.multi(output: $stderr, &block)` | Create multi-bar tracker |
|
|
168
226
|
| `Progress.each(enumerable, label: nil) { \|item\| }` | Iterate with progress |
|
|
169
227
|
| `Progress.map(enumerable, label: nil) { \|item\| }` | Transform with progress, returns results |
|
|
228
|
+
| `Progress.json_mode!` | Switch bar rendering to JSON line output |
|
|
229
|
+
| `Progress.text_mode!` | Switch bar rendering back to ANSI text |
|
|
230
|
+
| `Progress.json_mode?` | Whether JSON mode is active |
|
|
170
231
|
|
|
171
232
|
## Development
|
|
172
233
|
|
|
@@ -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)
|
|
@@ -43,10 +51,40 @@ module Philiprehberger
|
|
|
43
51
|
def reset
|
|
44
52
|
@current = 0
|
|
45
53
|
@finished = false
|
|
54
|
+
@paused = false
|
|
55
|
+
@pause_elapsed = 0.0
|
|
56
|
+
@pause_start = nil
|
|
46
57
|
@start_time = now
|
|
47
58
|
self
|
|
48
59
|
end
|
|
49
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
|
+
|
|
50
88
|
def finish
|
|
51
89
|
@current = @total
|
|
52
90
|
@finished = true
|
|
@@ -65,8 +103,17 @@ module Philiprehberger
|
|
|
65
103
|
(@current.to_f / @total * 100).round(1)
|
|
66
104
|
end
|
|
67
105
|
|
|
106
|
+
# Number of steps remaining (clamped to non-negative).
|
|
107
|
+
#
|
|
108
|
+
# @return [Integer]
|
|
109
|
+
def remaining
|
|
110
|
+
[@total - @current, 0].max
|
|
111
|
+
end
|
|
112
|
+
|
|
68
113
|
def elapsed
|
|
69
|
-
now - @start_time
|
|
114
|
+
raw = now - @start_time - @pause_elapsed
|
|
115
|
+
raw -= (now - @pause_start) if @paused
|
|
116
|
+
raw
|
|
70
117
|
end
|
|
71
118
|
|
|
72
119
|
def eta
|
|
@@ -85,7 +132,21 @@ module Philiprehberger
|
|
|
85
132
|
@current.to_f / elapsed_time
|
|
86
133
|
end
|
|
87
134
|
|
|
135
|
+
def to_h
|
|
136
|
+
{
|
|
137
|
+
percentage: percentage,
|
|
138
|
+
elapsed: elapsed,
|
|
139
|
+
eta: eta,
|
|
140
|
+
throughput: throughput,
|
|
141
|
+
current: @current,
|
|
142
|
+
total: @total,
|
|
143
|
+
remaining: remaining
|
|
144
|
+
}
|
|
145
|
+
end
|
|
146
|
+
|
|
88
147
|
def to_s
|
|
148
|
+
return to_h.to_json if Philiprehberger::Progress.json_mode?
|
|
149
|
+
|
|
89
150
|
bar_str = render_bar
|
|
90
151
|
pct = format('%<p>5.1f%%', p: percentage)
|
|
91
152
|
eta_str = format_eta(eta)
|
|
@@ -109,11 +170,16 @@ module Philiprehberger
|
|
|
109
170
|
end
|
|
110
171
|
|
|
111
172
|
def render_bar
|
|
112
|
-
return
|
|
173
|
+
return @fill * @width if @total.zero?
|
|
113
174
|
|
|
114
175
|
filled = (@current.to_f / @total * @width).round
|
|
115
176
|
empty = @width - filled
|
|
116
|
-
|
|
177
|
+
|
|
178
|
+
if filled.positive? && filled < @width
|
|
179
|
+
(@fill * (filled - 1)) + @tip + (@empty * empty)
|
|
180
|
+
else
|
|
181
|
+
(@fill * filled) + (@empty * empty)
|
|
182
|
+
end
|
|
117
183
|
end
|
|
118
184
|
|
|
119
185
|
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.6.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-
|
|
11
|
+
date: 2026-05-07 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,
|