Module: RubyTUI::App

Defined in:
lib/rubytui/app.rb

Overview

Optional Model-View-Update (Elm Architecture) framework. Provides a structured pattern for building TUI applications.

Examples:

Counter app

class MyApp
  include RubyTUI::App

  def init
    { counter: 0 }   # initial model
  end

  def update(model, msg)
    case msg
    when :increment then model.merge(counter: model[:counter] + 1)
    when :quit      then throw(:quit)
    else model
    end
  end

  def view(model, frame)
    frame.buffer.set_string(0, 0, "Count: #{model[:counter]}")
  end

  def handle_event(event)
    case event
    when KeyEvent
      case event.char
      when "+" then :increment
      when "q" then :quit
      end
    end
  end
end

MyApp.new.run!

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ void

This method returns an undefined value.

Extend the including class with ClassMethods.

Parameters:

  • base (Class) —

    the class that includes App



45
46
47
# File 'lib/rubytui/app.rb', line 45

def self.included(base)
  base.extend(ClassMethods)
end

Instance Method Details

#handle_event(event) ⇒ Object?

Override: convert an Event into a message (or nil to ignore)

Parameters:

Returns:

  • (Object, nil) —

    message passed to #update, or nil to ignore (default: nil)



94
95
96
# File 'lib/rubytui/app.rb', line 94

def handle_event(event)
  nil
end

#handle_tick(model) ⇒ Object?

Override: return a message on each tick, or nil

Called once per loop iteration after polling, whether or not an event arrived.

Parameters:

  • model (Object) —

    the current model

Returns:

  • (Object, nil) —

    message passed to #update, or nil (default: nil)



170
171
172
# File 'lib/rubytui/app.rb', line 170

def handle_tick(model)
  nil
end

#init ⇒ Object

Override: return the initial model (a Hash or any object)

Returns:

  • (Object) —

    the initial model (default: empty Hash)



65
66
67
# File 'lib/rubytui/app.rb', line 65

def init
  {}
end

#on_start(terminal, reader) ⇒ void

This method returns an undefined value.

Optional: called once before the main loop with [terminal, reader]

Called after #init, with the terminal already set up.

Parameters:



105
106
# File 'lib/rubytui/app.rb', line 105

def on_start(terminal, reader)
end

#on_stop(model) ⇒ void

This method returns an undefined value.

Optional: called once after the main loop exits

Called before the terminal is restored.

Parameters:

  • model (Object, nil) —

    the result of the catch(:quit) block: the value passed as throw(:quit, value), or nil for a bare throw(:quit)



116
117
# File 'lib/rubytui/app.rb', line 116

def on_stop(model)
end

#run!(mouse: false, tick_ms: 50, output: $stdout, input: $stdin) ⇒ void

This method returns an undefined value.

Run the application.

The main loop renders, polls for events, and dispatches messages. Call throw(:quit) from #update to exit the loop cleanly.

Wraps RubyTUI.run (raw mode, hidden cursor, alternate screen), so the terminal is restored when the loop exits or raises. Each iteration draws via #view, waits up to tick_ms for an event (passed through #handle_event), then calls #handle_tick; non-nil messages from either are passed to #update.

Parameters:

  • mouse (Boolean) (defaults to: false) —

    enable mouse event reporting (default: false)

  • tick_ms (Integer) (defaults to: 50) —

    event poll timeout per loop iteration, in milliseconds (default: 50)

  • output (IO) (defaults to: $stdout) —

    output stream (default: $stdout)

  • input (IO) (defaults to: $stdin) —

    input stream (default: $stdin)



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/rubytui/app.rb', line 136

def run!(mouse: false, tick_ms: 50, output: $stdout, input: $stdin)
  RubyTUI.run(output: output, input: input, mouse: mouse) do |terminal, reader|
    model = init
    on_start(terminal, reader)

    model = catch(:quit) do
      loop do
        terminal.draw do |frame|
          view(model, frame)
        end

        event = reader.poll(timeout_ms: tick_ms)

        if event
          msg = handle_event(event)
          model = update(model, msg) if msg
        end

        tick_msg = handle_tick(model)
        model = update(model, tick_msg) if tick_msg
      end
    end

    on_stop(model)
  end
end

#update(model, msg) ⇒ Object

Override: given the current model and a message, return the next model. Call throw(:quit) from update to exit the app loop.

Parameters:

Returns:

  • (Object) —

    the next model (default: model unchanged)



75
76
77
# File 'lib/rubytui/app.rb', line 75

def update(model, msg)
  model
end

#view(model, frame) ⇒ void

This method returns an undefined value.

Override: render the UI for the current model

Called inside Terminal#draw once per loop iteration.

Parameters:

  • model (Object) —

    the current model

  • frame (Frame) —

    the frame to render into



86
87
# File 'lib/rubytui/app.rb', line 86

def view(model, frame)
end