Class: Ruborg::Progress

Inherits:
Object
  • Object
show all
Defined in:
lib/ruborg/progress.rb

Overview

Terminal progress display: named stages, inline progress bar, and spinner. Writes to $stderr so stdout remains clean for –json or piped output. Degrades to plain text lines when output is not a TTY (piped / redirected).

Constant Summary collapse

SPINNER_FRAMES =
%w[         ].freeze
BAR_WIDTH =
28
LINE_WIDTH =
80

Instance Method Summary collapse

Constructor Details

#initialize(output: $stderr) ⇒ Progress

Returns a new instance of Progress.



12
13
14
15
16
# File 'lib/ruborg/progress.rb', line 12

def initialize(output: $stderr)
  @output = output
  @tty = output.respond_to?(:isatty) && output.isatty
  @spinner_thread = nil
end

Instance Method Details

#bar(current, total, label = "") ⇒ Object

Redraw an inline progress bar. Call once per item in a loop. label is truncated to fit the terminal line.



53
54
55
56
57
58
59
60
61
62
# File 'lib/ruborg/progress.rb', line 53

def bar(current, total, label = "")
  return unless @tty

  pct = total.positive? ? (current.to_f / total) : 0
  filled = (BAR_WIDTH * pct).round
  bar_str = filled.positive? ? "#{"=" * (filled - 1)}>" : ""
  bar_str = bar_str.ljust(BAR_WIDTH)
  short_label = truncate_left(label.to_s, 28)
  @output.print "\r  [#{bar_str}]  #{current}/#{total}  #{short_label.ljust(28)}"
end

#done(label = nil) ⇒ Object

Halt any in-progress display and print a completion line.



65
66
67
68
69
# File 'lib/ruborg/progress.rb', line 65

def done(label = nil)
  stop_spin
  clear_line if @tty
  @output.puts "#{label}" if label
end

#spin(label) ⇒ Object

Start a spinner on the current line for an indeterminate operation. Call stop_spin (or done) to halt it.



27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/ruborg/progress.rb', line 27

def spin(label)
  stop_spin
  return unless @tty

  frame = 0
  @spinner_thread = Thread.new do
    loop do
      @output.print "\r  #{SPINNER_FRAMES[frame % SPINNER_FRAMES.size]}  #{label}"
      frame += 1
      sleep 0.1
    end
  end
end

#stage(index, total, label) ⇒ Object

Print a numbered stage header: “[2/3] Label”



19
20
21
22
23
# File 'lib/ruborg/progress.rb', line 19

def stage(index, total, label)
  stop_spin
  clear_line if @tty
  @output.puts "[#{index}/#{total}] #{label}"
end

#stop_spinObject

Stop the spinner and erase its line.



42
43
44
45
46
47
48
49
# File 'lib/ruborg/progress.rb', line 42

def stop_spin
  return unless @spinner_thread

  @spinner_thread.kill
  @spinner_thread.join(0.2)
  @spinner_thread = nil
  clear_line if @tty
end