Rust Backend Design (ratatui_ruby extension)

This document describes the internal architecture of the ratatui_ruby Rust extension.

Architecture Guidelines

The project follows a Structured Design approach, separating concerns into modules to improve cohesiveness and testability.

Core Principles

  1. Single Generic Renderer: The backend implements a single generic renderer that accepts a Ruby Value representing the root of the view tree.

  2. No Custom Rust Structs for UI: Do not define custom Rust structs that mirror Ruby UI components. Instead, extract data directly from Ruby objects using funcall.

  3. Dynamic Dispatch: Use value.class().name() (e.g., "RatatuiRuby::Paragraph") to dynamically dispatch rendering logic to the appropriate widget module.

  4. Immediate Mode: The renderer traverses the Ruby object tree every frame and rebuilds the Ratatui widget tree on the fly.

Module Structure

The Rust extension is located in ext/ratatui_ruby/src/ and is organized as follows:

Adding a New Widget

To add a new widget:

  1. Create a new file src/widgets/my_widget.rs.

  2. Implement a public render function:

    /// Renders the widget to the given area.
    ///
    /// # Arguments
    ///
    /// * `frame` - The Ratatui frame to render to.
    /// * `area` - The rectangular area within the frame to draw the widget.
    /// * `node` - The Ruby object (Value) containing the widget's properties.
    pub fn render(frame: &mut Frame, area: Rect, node: Value) -> Result<(), Error>
  3. Inside render:

    • Extract properties from the node (Ruby value) using .funcall("method_name", ())?.

    • Construct the Ratatui widget.

    • Render it using frame.render_widget.

  4. Register the module in src/widgets/mod.rs.

  5. Add a dispatch arm in src/rendering.rs matching the Ruby class name (e.g., RatatuiRuby::MyWidget).

Testing Strategy