Class: Tapsoob::Progress::MultiBar

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

Overview

MultiBar manages multiple progress bars in parallel with a clean interface:

  • N progress bar lines (constantly updating)

  • 1 separator line

  • 1 info message line (shows latest INFO, gets replaced)

Instance Method Summary collapse

Constructor Details

#initialize(max_bars = 4) ⇒ MultiBar

Returns a new instance of MultiBar.



10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/tapsoob/progress/multi_bar.rb', line 10

def initialize(max_bars = 4)
  @max_bars = max_bars
  @bars = []
  @mutex = Mutex.new
  @active = true
  @out = STDOUT
  @last_update = Time.now
  @max_title_width = 14  # Minimum width, will grow with longer titles
  @initialized = false
  @total_lines = 0  # Total lines: max_bars + separator + info line
  @info_message = ""  # Current info message to display
  @start_time = Time.now  # Track total elapsed time
  @terminal_width = get_terminal_width
end

Instance Method Details

#create_bar(title, total) ⇒ Object

Create a new progress bar and return it



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/tapsoob/progress/multi_bar.rb', line 34

def create_bar(title, total)
  @mutex.synchronize do
    # Initialize display area on first bar creation
    unless @initialized
      @total_lines = @max_bars + 2  # bars + separator + info line
      @total_lines.times { @out.print "\n" }
      @out.flush
      @initialized = true
    end

    # Remove any existing bar with the same title to prevent duplicates
    @bars.reject! { |b| b.title == title }

    # Update max title width to accommodate longer titles
    @max_title_width = [@max_title_width, title.length].max

    bar = ThreadSafeBar.new(title, total, self)
    @bars << bar
    bar
  end
end

#finish_bar(bar) ⇒ Object

Finish a specific bar - mark it as completed



83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/tapsoob/progress/multi_bar.rb', line 83

def finish_bar(bar)
  @mutex.synchronize do
    return unless @active

    bar.mark_finished

    # Respect throttle when finishing to avoid spamming redraws
    if should_redraw?
      @last_update = Time.now
      redraw_all
    end
    # If throttled, the next regular update will show the finished state
  end
end

#get_terminal_widthObject

Get terminal width, default to 160 if can’t detect



26
27
28
29
30
31
# File 'lib/tapsoob/progress/multi_bar.rb', line 26

def get_terminal_width
  require 'io/console'
  IO.console&.winsize&.[](1) || 160
rescue
  160
end

#max_title_widthObject

Get the current maximum title width for alignment Note: Always called from within synchronized methods, so no mutex needed



67
68
69
# File 'lib/tapsoob/progress/multi_bar.rb', line 67

def max_title_width
  @max_title_width
end

#set_info(message) ⇒ Object

Update the info message line (called from outside for INFO logs)



57
58
59
60
61
62
63
# File 'lib/tapsoob/progress/multi_bar.rb', line 57

def set_info(message)
  @mutex.synchronize do
    return unless @active
    @info_message = message
    redraw_all if @initialized
  end
end

#stopObject

Stop all progress bars and clear them from display



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/tapsoob/progress/multi_bar.rb', line 99

def stop
  @mutex.synchronize do
    return unless @active  # Already stopped
    @active = false

    # Clear all lines (progress bars + separator + info line)
    if @total_lines > 0 && @initialized
      # Move cursor up to first line
      @out.print "\e[#{@total_lines}A"

      # Clear each line
      @total_lines.times do
        @out.print "\r\e[2K\n"
      end

      # Move cursor back to start
      @out.print "\e[#{@total_lines}A\r"
    end

    @out.flush
  end
end

#updateObject

Called by individual bars when they update



72
73
74
75
76
77
78
79
80
# File 'lib/tapsoob/progress/multi_bar.rb', line 72

def update
  @mutex.synchronize do
    return unless @active
    return unless should_redraw?

    @last_update = Time.now
    redraw_all
  end
end