Class: Hawktui::StreamingTable
- Inherits:
-
Object
- Object
- Hawktui::StreamingTable
- Defined in:
- lib/hawktui/streaming_table.rb,
lib/hawktui/streaming_table/cell.rb,
lib/hawktui/streaming_table/column.rb,
lib/hawktui/streaming_table/layout.rb
Overview
Public: Streams a dynamic table to the terminal, handling user input such as pausing/unpausing and quitting. Continually appends new rows at the top of the table and enforces a maximum row limit.
Examples
columns = [
{ name: :timestamp, width: 20 },
{ name: :message, width: 50 },
]
table = Hawktui::StreamingTable.new(columns: columns, max_rows: 1000)
table.start
# In a separate thread or async process:
table.add_row(timestamp: Time.now.to_s, message: "Hello, world!")
WARNING: The StreamingTable must run in the main thread and table.add_row should be called from a separate thread or async process. This is because the table uses curses, which is not thread-safe and does not respond to user input in a separate thread.
Defined Under Namespace
Instance Attribute Summary collapse
-
#layout ⇒ Object
Public accessors.
-
#max_rows ⇒ Object
readonly
Public accessors.
-
#paused ⇒ Object
readonly
Public accessors.
-
#rows ⇒ Object
readonly
Public accessors.
-
#should_exit ⇒ Object
readonly
Public accessors.
-
#win ⇒ Object
Returns the value of attribute win.
Instance Method Summary collapse
-
#add_row(row_data) ⇒ Object
Public: Add a new row of data to the top of the table.
-
#draw ⇒ Object
Internal: Draw the entire table (header, rows, status line).
-
#draw_body ⇒ Object
Internal: Draw the body of the table (rows of data).
-
#draw_footer ⇒ Object
Internal: Draw the status line at the bottom of the screen.
-
#draw_header ⇒ Object
Internal: Draw the header row of the table.
-
#draw_row(y_pos, formatted_cells) ⇒ Object
Internal: Draw a single row of the table, given already-formatted cells.
-
#draw_with_color(str_value, color) ⇒ Object
Internal: Add a string to the screen, optionally with a color attribute.
-
#handle_input ⇒ Object
Internal: Handle a single character of user input, toggling pause or stopping the table as appropriate.
-
#initialize(columns: {}, max_rows: 100_000) ⇒ StreamingTable
constructor
Public: Create a new StreamingTable.
-
#setup ⇒ Object
Internal: Set up curses, initialize colors, etc.
-
#start ⇒ Object
Public: Start the table UI.
-
#start_input_handling ⇒ Object
Internal: Start a separate thread to handle user input (non-blocking).
-
#stop ⇒ Object
Public: Stop the table UI.
-
#toggle_pause ⇒ Object
Internal: Toggle whether the table is paused.
Constructor Details
#initialize(columns: {}, max_rows: 100_000) ⇒ StreamingTable
Public: Create a new StreamingTable.
columns - An Array of Hashes or Hawktui::StreamingTable::Column objects that define the table’s
columns. Each element should at least contain `:name` and `:width`.
max_rows - The maximum number of rows to keep in the table. Defaults to 100000.
Examples
table = Hawktui::StreamingTable.new(columns: [{ name: :time, width: 10 }], max_rows: 500)
Returns a new StreamingTable instance.
43 44 45 46 47 48 49 50 |
# File 'lib/hawktui/streaming_table.rb', line 43 def initialize(columns: {}, max_rows: 100_000) @layout = Layout.new(columns: columns) @max_rows = max_rows @rows = [] # Store rows newest-first @paused = false @input_thread = nil @should_exit = false end |
Instance Attribute Details
#layout ⇒ Object
Public accessors
53 54 55 |
# File 'lib/hawktui/streaming_table.rb', line 53 def layout @layout end |
#max_rows ⇒ Object (readonly)
Public accessors
53 54 55 |
# File 'lib/hawktui/streaming_table.rb', line 53 def max_rows @max_rows end |
#paused ⇒ Object (readonly)
Public accessors
53 54 55 |
# File 'lib/hawktui/streaming_table.rb', line 53 def paused @paused end |
#rows ⇒ Object (readonly)
Public accessors
53 54 55 |
# File 'lib/hawktui/streaming_table.rb', line 53 def rows @rows end |
#should_exit ⇒ Object (readonly)
Public accessors
53 54 55 |
# File 'lib/hawktui/streaming_table.rb', line 53 def should_exit @should_exit end |
#win ⇒ Object
Returns the value of attribute win.
54 55 56 |
# File 'lib/hawktui/streaming_table.rb', line 54 def win @win end |
Instance Method Details
#add_row(row_data) ⇒ Object
Public: Add a new row of data to the top of the table. If the table has reached its maximum row limit, the oldest row is dropped.
row_data - A Hash of row data where keys match column names. Values can be
raw (String, Integer, etc.) or a Hash with :value and :color.
Examples
table.add_row(timestamp: "2025-01-01 12:00", message: { value: "New Year!", color: :red })
Returns nothing.
111 112 113 114 115 116 117 |
# File 'lib/hawktui/streaming_table.rb', line 111 def add_row(row_data) return unless win rows.unshift(row_data) rows.pop if rows.size > max_rows draw unless paused end |
#draw ⇒ Object
Internal: Draw the entire table (header, rows, status line).
Returns nothing.
178 179 180 181 182 183 184 185 186 |
# File 'lib/hawktui/streaming_table.rb', line 178 def draw return unless win win.clear draw_header draw_body win.refresh end |
#draw_body ⇒ Object
Internal: Draw the body of the table (rows of data).
Returns nothing.
202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/hawktui/streaming_table.rb', line 202 def draw_body max_display_rows = Curses.lines - 2 display_rows = rows.first(max_display_rows) display_rows.each_with_index do |row_data, idx| cells = layout.build_cells_for_row(row_data) formatted_cells = cells.zip(layout.columns).map do |cell, column| column.format_cell(cell) end draw_row(idx + 1, formatted_cells) end end |
#draw_footer ⇒ Object
Internal: Draw the status line at the bottom of the screen.
Returns nothing.
218 219 220 221 222 223 224 225 226 |
# File 'lib/hawktui/streaming_table.rb', line 218 def return unless win win.setpos(Curses.lines - 1, 0) status = paused ? "PAUSED" : "RUNNING" help_text = " | Press 'p' to pause/unpause, 'q' to quit" win.addstr("Status: #{status}#{help_text}".ljust(Curses.cols)) win.refresh end |
#draw_header ⇒ Object
Internal: Draw the header row of the table.
Returns nothing.
191 192 193 194 195 196 197 |
# File 'lib/hawktui/streaming_table.rb', line 191 def draw_header header_cells = layout.build_header_row formatted_header_cells = header_cells.zip(layout.columns).map do |cell, column| column.format_cell(cell) end draw_row(0, formatted_header_cells) end |
#draw_row(y_pos, formatted_cells) ⇒ Object
Internal: Draw a single row of the table, given already-formatted cells.
y_pos - The Integer row position (0-based) on the screen to draw. formatted_cells - An Array of [string_value, color], as returned by column formatting.
Returns nothing.
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/hawktui/streaming_table.rb', line 234 def draw_row(y_pos, formatted_cells) x_pos = 0 formatted_cells.each do |str_value, color| win.setpos(y_pos, x_pos) if y_pos == 0 # Bold the header row win.attron(Curses::A_BOLD) { draw_with_color(str_value, color) } else draw_with_color(str_value, color) end x_pos += str_value.length + 1 end end |
#draw_with_color(str_value, color) ⇒ Object
Internal: Add a string to the screen, optionally with a color attribute.
str_value - The String text to draw. color - An Integer color index or Symbol referencing a base color in Colors.
Returns nothing.
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
# File 'lib/hawktui/streaming_table.rb', line 256 def draw_with_color(str_value, color) if color color_index = case color when Integer color when Symbol, String Utils::Colors::BASE_COLORS[color.to_sym]&.first end if color_index win.attron(Curses.color_pair(color_index + 1)) { win.addstr(str_value) } else win.addstr(str_value) end else win.addstr(str_value) end end |
#handle_input ⇒ Object
Internal: Handle a single character of user input, toggling pause or stopping the table as appropriate.
Returns nothing.
156 157 158 159 160 161 162 163 164 |
# File 'lib/hawktui/streaming_table.rb', line 156 def handle_input case Curses.getch when "p" toggle_pause when "q" @should_exit = true stop end end |
#setup ⇒ Object
Internal: Set up curses, initialize colors, etc. Called by #start.
Returns nothing.
122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/hawktui/streaming_table.rb', line 122 def setup Curses.init_screen Utils::Colors.setup_colors Curses.start_color Curses.noecho Curses.curs_set(0) Curses.stdscr.keypad(true) Curses.timeout = 0 self.win = Curses.stdscr win.clear end |
#start ⇒ Object
Public: Start the table UI. Initializes curses, sets up input handling, and draws the initial screen.
Examples
table.start
Returns nothing.
79 80 81 82 83 |
# File 'lib/hawktui/streaming_table.rb', line 79 def start setup start_input_handling draw end |
#start_input_handling ⇒ Object
Internal: Start a separate thread to handle user input (non-blocking).
Returns nothing.
138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/hawktui/streaming_table.rb', line 138 def start_input_handling @input_thread = Thread.new do loop do handle_input sleep 0.1 break if should_exit end rescue => e win.setpos(0, 0) win.addstr("Error in input thread: #{e.}") win.refresh end end |
#stop ⇒ Object
Public: Stop the table UI. Stops the input thread, closes the curses screen, and exits the process.
Examples
table.stop
Returns nothing. Exits the process.
93 94 95 96 97 98 |
# File 'lib/hawktui/streaming_table.rb', line 93 def stop @input_thread&.exit Curses.close_screen if win self.win = nil Process.exit(0) end |
#toggle_pause ⇒ Object
Internal: Toggle whether the table is paused. When paused, new rows are still collected but not rendered until unpaused.
Returns nothing.
170 171 172 173 |
# File 'lib/hawktui/streaming_table.rb', line 170 def toggle_pause @paused = !paused end |