Class: Clack::Core::Prompt Abstract

Inherits:
Object
  • Object
show all
Includes:
Chrome
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
    "#{frame_header}#{gutter(active_bar)}#{something}\n#{frame_footer}"
  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, with_guide: nil, show_instructions: nil, instructions: 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, Regexp, Symbol, Array, Hash, nil, false) (defaults to: nil)

    validator, resolved by Validators.resolve; a proc returns an error string, Warning, or nil. nil or false disables validation

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

    transformer (symbol shortcut or proc); applied after validation

  • with_guide (Boolean, nil) (defaults to: nil)

    show the guide rail; nil uses Clack.settings

  • show_instructions (Boolean, nil) (defaults to: nil)

    show the keyboard hint footer; nil uses Clack.settings

  • instructions (String, Array<String>, nil) (defaults to: nil)

    replacement hint text, rendered verbatim on one line (newlines are not re-prefixed); nil uses the prompt's built-in hints

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

    input stream (default: $stdin)

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

    output stream (default: $stdout)

Raises:

  • (ArgumentError)

    if instructions is not nil, a String, or an Array of Strings



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/clack/core/prompt.rb', line 99

def initialize(message:, help: nil, validate: nil, transform: nil,
  with_guide: nil, show_instructions: nil, instructions: nil,
  input: $stdin, output: $stdout)
  @message = message
  @help = help
  setup_chrome(with_guide:, show_instructions:, instructions:)
  @validate = Validators.resolve(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.



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

def active_prompts
  @active_prompts
end

.resize_pendingObject

Returns the value of attribute resize_pending.



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

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



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

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)



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

def state
  @state
end

#valueObject (readonly)

Returns the current/final value.

Returns:

  • (Object)

    the current/final value



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

def value
  @value
end

#warning_messageString? (readonly)

Returns validation warning message, if any.

Returns:

  • (String, nil)

    validation warning message, if any



83
84
85
# File 'lib/clack/core/prompt.rb', line 83

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.



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

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



46
47
48
# File 'lib/clack/core/prompt.rb', line 46

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.



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

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.



51
52
53
# File 'lib/clack/core/prompt.rb', line 51

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.



120
121
122
# File 'lib/clack/core/prompt.rb', line 120

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

Raises:



131
132
133
134
135
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
162
163
164
# File 'lib/clack/core/prompt.rb', line 131

def run
  return run_ci_mode if CiMode.active?(@input)

  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
  rescue NotATerminalError
    # The first read failed, so the frame drawn above is orphaned: erase
    # it so the caller's error output is the only thing left on screen.
    finalize("")
    raise
  ensure
    Prompt.unregister(self)
    cleanup_terminal if terminal_setup
  end
end