Class: Clack::Core::Prompt Abstract

Inherits:
Object
  • Object
show all
Defined in:
lib/clack/core/prompt.rb

Overview

This class is abstract.

Subclass and override #build_frame to implement a prompt.

Base class for all interactive prompts.

Implements a state machine with states: :initial, :active, :error, :warning, :submit, :cancel. Subclasses override #handle_input, #build_frame, and #build_final_frame to customize behavior and rendering.

The prompt loop:

  1. Renders the initial frame

  2. Reads keyboard input via KeyReader

  3. Handles input and transitions state

  4. Re-renders the frame

  5. Repeats until a terminal state (:submit or :cancel)

Examples:

Creating a custom prompt

class MyPrompt < Clack::Core::Prompt
  def build_frame
    "#{bar}\n#{symbol_for_state}  #{@message}\n"
  end
end

Constant Summary collapse

MIN_TERMINAL_WIDTH =

Minimum terminal width for clean rendering. Prompts warn (non-blocking) if the terminal is narrower.

40

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(message:, help: nil, validate: nil, transform: nil, input: $stdin, output: $stdout) ⇒ Prompt

Returns a new instance of Prompt.

Parameters:

  • message (String)

    the prompt message to display

  • help (String, nil) (defaults to: nil)

    optional help text shown below the message

  • validate (Proc, nil) (defaults to: nil)

    optional validation proc; returns error string, Warning, or nil

  • transform (Symbol, Proc, nil) (defaults to: nil)

    transformer (symbol shortcut or proc); applied after validation

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

    input stream (default: $stdin)

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

    output stream (default: $stdout)



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/clack/core/prompt.rb', line 89

def initialize(message:, help: nil, validate: nil, transform: nil, input: $stdin, output: $stdout)
  @message = message
  @help = help
  @validate = validate
  @transform = Transformers.resolve(transform)
  @input = input
  @output = output
  @state = :initial
  @value = nil
  @error_message = nil
  @warning_message = nil
  @warning_confirmed = false
  @prev_frame = nil
  @needs_redraw = false
end

Class Attribute Details

.active_promptsObject (readonly)

Returns the value of attribute active_prompts.



40
41
42
# File 'lib/clack/core/prompt.rb', line 40

def active_prompts
  @active_prompts
end

.resize_pendingObject

Returns the value of attribute resize_pending.



41
42
43
# File 'lib/clack/core/prompt.rb', line 41

def resize_pending
  @resize_pending
end

Instance Attribute Details

#error_messageString? (readonly)

Returns validation error message, if any.

Returns:

  • (String, nil)

    validation error message, if any



79
80
81
# File 'lib/clack/core/prompt.rb', line 79

def error_message
  @error_message
end

#stateSymbol (readonly)

Returns current state (:initial, :active, :error, :warning, :submit, :cancel).

Returns:

  • (Symbol)

    current state (:initial, :active, :error, :warning, :submit, :cancel)



75
76
77
# File 'lib/clack/core/prompt.rb', line 75

def state
  @state
end

#valueObject (readonly)

Returns the current/final value.

Returns:

  • (Object)

    the current/final value



77
78
79
# File 'lib/clack/core/prompt.rb', line 77

def value
  @value
end

#warning_messageString? (readonly)

Returns validation warning message, if any.

Returns:

  • (String, nil)

    validation warning message, if any



81
82
83
# File 'lib/clack/core/prompt.rb', line 81

def warning_message
  @warning_message
end

Class Method Details

.flush_resizeObject

Notify all active prompts of a pending resize. Called from the render loop, not from the signal handler.



55
56
57
58
59
60
# File 'lib/clack/core/prompt.rb', line 55

def flush_resize
  return unless @resize_pending

  @resize_pending = false
  @active_prompts.each(&:request_redraw)
end

.register(prompt) ⇒ Object

Register a prompt instance for resize notifications



44
45
46
# File 'lib/clack/core/prompt.rb', line 44

def register(prompt)
  @active_prompts << prompt
end

.setup_signal_handlerObject

Set up SIGWINCH handler (called once on load). Signal handler only sets a flag – no allocation or iteration.



64
65
66
67
68
69
70
71
# File 'lib/clack/core/prompt.rb', line 64

def setup_signal_handler
  return if Clack::Environment.windows?
  return unless Signal.list.key?("WINCH")

  Signal.trap("WINCH") do
    @resize_pending = true
  end
end

.unregister(prompt) ⇒ Object

Unregister a prompt instance from resize notifications.



49
50
51
# File 'lib/clack/core/prompt.rb', line 49

def unregister(prompt)
  @active_prompts.delete(prompt)
end

Instance Method Details

#request_redrawObject

Request a full redraw on next render cycle. Called by SIGWINCH handler when terminal is resized.



107
108
109
# File 'lib/clack/core/prompt.rb', line 107

def request_redraw
  @needs_redraw = true
end

#runObject, Clack::CANCEL

Run the prompt interaction loop.

Sets up the terminal, renders frames, and processes input until the user submits or cancels. Returns the final value or Clack::CANCEL.

Returns:

  • (Object, Clack::CANCEL)

    the submitted value or CANCEL sentinel



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/clack/core/prompt.rb', line 117

def run
  return run_ci_mode if CiMode.active?

  Prompt.register(self)
  warn_narrow_terminal
  terminal_setup = false

  begin
    setup_terminal
    terminal_setup = true
    render
    @state = :active

    loop do
      Prompt.flush_resize
      key = KeyReader.read(@input)
      dispatch_key(key)
      render

      break if terminal_state?
    end

    finalize
    (terminal_state? && @state == :cancel) ? CANCEL : @value
  ensure
    Prompt.unregister(self)
    cleanup_terminal if terminal_setup
  end
end