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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9d1ecb051d55213a818343c3133c067129bf55e65b6677a2efb9be49c249ab5b
4
- data.tar.gz: '0039452f607ef4f9fe1f1a1098b31039c6ea7ecbc9c0618c8f8fba78b5464763'
3
+ metadata.gz: fb855052746be6b7d6a120d7ad8b84ce16c8ccce47534463d04b698c680ea0fa
4
+ data.tar.gz: 2bf7bbafcf7feb5985c8baacc4a5eb10cbab3b5ee75f25d10a0512704230212a
5
5
  SHA512:
6
- metadata.gz: da6edc69bcc8dc77d091ba848dae89fb8f8e5054ceb1f9c6991537e85554b8a3bbde4cef23b94089d0c6194827ae6c13e3a7cb02f875d9d7ca9e3960519d7023
7
- data.tar.gz: 5692a7b42fb906e3234117a3a96ca0ad2c1af88d558bc41e7a787cd8c1cc954a5c317613b91ff8d913b27b87263ea279d8bf497b19f17886cf57c8141578b6f8
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
- | `#to_s` | Render the bar as a string |
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 FILL_CHAR * @width if @total.zero?
173
+ return @fill * @width if @total.zero?
113
174
 
114
175
  filled = (@current.to_f / @total * @width).round
115
176
  empty = @width - filled
116
- (FILL_CHAR * filled) + (EMPTY_CHAR * empty)
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)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Philiprehberger
4
4
  module Progress
5
- VERSION = '0.4.0'
5
+ VERSION = '0.6.0'
6
6
  end
7
7
  end
@@ -7,8 +7,22 @@ require_relative 'progress/multi'
7
7
 
8
8
  module Philiprehberger
9
9
  module Progress
10
- def self.bar(total:, width: 30, output: $stderr)
11
- progress_bar = Bar.new(total: total, width: width, output: output)
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.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-04-10 00:00:00.000000000 Z
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,