Class: TTY2::Prompt::Question

Inherits:
Object
  • Object
show all
Includes:
Checks
Defined in:
lib/tty2/prompt/question.rb,
lib/tty2/prompt/question/checks.rb,
lib/tty2/prompt/question/modifier.rb,
lib/tty2/prompt/question/validation.rb

Overview

A class responsible for gathering user input

Direct Known Subclasses

ConfirmQuestion, Keypress, MaskQuestion, Multiline

Defined Under Namespace

Modules: Checks Classes: Modifier, Validation

Constant Summary collapse

UndefinedSetting =
Class.new do
  def to_s
    "undefined"
  end
  alias_method :inspect, :to_s
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(prompt, **options) ⇒ Question

Initialize a Question



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/tty2/prompt/question.rb', line 37

def initialize(prompt, **options)
  # Option deprecation
  if options[:validation]
    warn "[DEPRECATION] The `:validation` option is deprecated. Use `:validate` instead."
    options[:validate] = options[:validation]
  end

  @prompt       = prompt
  @prefix       = options.fetch(:prefix) { @prompt.prefix }
  @default      = options.fetch(:default) { UndefinedSetting }
  @required     = options.fetch(:required) { false }
  @echo         = options.fetch(:echo) { true }
  @in           = options.fetch(:in) { UndefinedSetting }
  @modifier     = options.fetch(:modifier) { [] }
  @validation   = options.fetch(:validate) { UndefinedSetting }
  @convert      = options.fetch(:convert) { UndefinedSetting }
  @active_color = options.fetch(:active_color) { @prompt.active_color }
  @help_color   = options.fetch(:help_color) { @prompt.help_color }
  @error_color  = options.fetch(:error_color) { :red }
  @value        = options.fetch(:value) { UndefinedSetting }
  @quiet        = options.fetch(:quiet) { @prompt.quiet }
  @messages     = Utils.deep_copy(options.fetch(:messages) { {} })
  @done         = false
  @first_render = true
  @input        = nil

  @evaluator = Evaluator.new(self)

  @evaluator << CheckRequired
  @evaluator << CheckDefault
  @evaluator << CheckRange
  @evaluator << CheckValidation
  @evaluator << CheckModifier
  @evaluator << CheckConversion
end

Instance Attribute Details

#messageObject (readonly)

Store question message



28
29
30
# File 'lib/tty2/prompt/question.rb', line 28

def message
  @message
end

#messagesObject (readonly)

Stores all the error messages displayed to user The currently supported messages are:

* :range?
* :required?
* :valid?


78
79
80
# File 'lib/tty2/prompt/question.rb', line 78

def messages
  @messages
end

#modifierObject (readonly)



30
31
32
# File 'lib/tty2/prompt/question.rb', line 30

def modifier
  @modifier
end

#validationObject (readonly)



32
33
34
# File 'lib/tty2/prompt/question.rb', line 32

def validation
  @validation
end

Instance Method Details

#call(message = "", &block) ⇒ self

Call the question

Parameters:

  • message (String) (defaults to: "")

Returns:

  • (self)


107
108
109
110
111
112
113
# File 'lib/tty2/prompt/question.rb', line 107

def call(message = "", &block)
  @message = message
  block.call(self) if block
  @prompt.subscribe(self) do
    render
  end
end

#convert(value = (not_set = true), message = nil) ⇒ Object

Specify answer conversion



237
238
239
240
241
242
243
244
# File 'lib/tty2/prompt/question.rb', line 237

def convert(value = (not_set = true), message = nil)
  messages[:convert?] = message if message
  if not_set
    @convert
  else
    @convert = value
  end
end

#convert?Boolean

Check if conversion is set

Returns:

  • (Boolean)


251
252
253
# File 'lib/tty2/prompt/question.rb', line 251

def convert?
  @convert != UndefinedSetting
end

#convert_result(value) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Convert value to expected type

Parameters:

  • value (Object)


221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/tty2/prompt/question.rb', line 221

def convert_result(value)
  if convert? && !Utils.blank?(value)
    case @convert
    when Proc
      @convert.call(value)
    else
      Converters.convert(@convert, value)
    end
  else
    value
  end
end

#default(value = (not_set = true)) ⇒ Object

Set default value.



258
259
260
261
262
# File 'lib/tty2/prompt/question.rb', line 258

def default(value = (not_set = true))
  return @default if not_set

  @default = value
end

#default?Boolean

Check if default value is set

Returns:

  • (Boolean)


269
270
271
# File 'lib/tty2/prompt/question.rb', line 269

def default?
  @default != UndefinedSetting
end

#echo(value = nil) ⇒ Object Also known as: echo?

Turn terminal echo on or off. This is used to secure the display so that the entered characters are not echoed back to the screen.



331
332
333
334
335
# File 'lib/tty2/prompt/question.rb', line 331

def echo(value = nil)
  return @echo if value.nil?

  @echo = value
end

#in(value = (not_set = true), message = nil) ⇒ Object

Set expected range of values

Parameters:

  • value (String) (defaults to: (not_set = true))


353
354
355
356
357
358
359
360
361
# File 'lib/tty2/prompt/question.rb', line 353

def in(value = (not_set = true), message = nil)
  messages[:range?] = message if message
  if in? && !@in.is_a?(Range)
    @in = Converters.convert(:range, @in)
  end
  return @in if not_set

  @in = Converters.convert(:range, value)
end

#in?Boolean

Check if range is set

Returns:

  • (Boolean)


368
369
370
# File 'lib/tty2/prompt/question.rb', line 368

def in?
  @in != UndefinedSetting
end

#inspectObject

String representation of this question



386
387
388
# File 'lib/tty2/prompt/question.rb', line 386

def inspect
  "#<#{self.class.name} @message=#{message}, @input=#{@input}>"
end

#message_for(name, tokens = nil) ⇒ Array[String]

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Retrieve message based on the key

Parameters:

  • name (Symbol)

    the name of message key

  • tokens (Hash) (defaults to: nil)

    the tokens to evaluate

Returns:

  • (Array[String])


91
92
93
94
95
96
97
98
# File 'lib/tty2/prompt/question.rb', line 91

def message_for(name, tokens = nil)
  template = @messages[name]
  if template && !template.match(/\%\{/).nil?
    [template % tokens]
  else
    [template || ""]
  end
end

#modify(*rules) ⇒ Object

Modify string according to the rule given.

Parameters:

  • rule (Symbol)


323
324
325
# File 'lib/tty2/prompt/question.rb', line 323

def modify(*rules)
  @modifier = rules
end

#process_input(question) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Decide how to handle input from user



161
162
163
164
165
166
167
# File 'lib/tty2/prompt/question.rb', line 161

def process_input(question)
  @input = read_input(question)
  if Utils.blank?(@input)
    @input = default? ? default : nil
  end
  @evaluator.(@input)
end

#quiet(value) ⇒ Object

Set quiet mode.



375
376
377
# File 'lib/tty2/prompt/question.rb', line 375

def quiet(value)
  @quiet = value
end

#raw(value = nil) ⇒ Object Also known as: raw?

Turn raw mode on or off. This enables character-based input.



341
342
343
344
345
# File 'lib/tty2/prompt/question.rb', line 341

def raw(value = nil)
  return @raw if value.nil?

  @raw = value
end

#read_input(question) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Process input



172
173
174
175
176
177
178
179
# File 'lib/tty2/prompt/question.rb', line 172

def read_input(question)
  options = { echo: echo }
  if value? && @first_render
    options[:value] = @value
    @first_render = false
  end
  @prompt.read_line(question, **options).chomp
end

#refresh(lines, lines_to_clear) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Determine area of the screen to clear

Parameters:

  • lines (Integer)

    number of lines to clear

Returns:

  • (String)


201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/tty2/prompt/question.rb', line 201

def refresh(lines, lines_to_clear)
  output = []
  if @done
    if @errors.count.zero?
      output << @prompt.cursor.up(lines)
    else
      lines += @errors.count
      lines_to_clear += @errors.count
    end
  else
    output << @prompt.cursor.up(lines)
  end
  output.join + @prompt.clear_lines(lines_to_clear)
end

#renderObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Read answer and convert to type



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/tty2/prompt/question.rb', line 118

def render
  @errors = []
  until @done
    result = process_input(render_question)
    if result.failure?
      @errors = result.errors
      @prompt.print(render_error(result.errors))
    else
      @done = true
    end
    question    = render_question
    input_line  = question + result.value.to_s
    total_lines = @prompt.count_screen_lines(input_line)
    @prompt.print(refresh(question.lines.count, total_lines))
  end
  @prompt.print(render_question) unless @quiet
  result.value
end

#render_error(errors) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Handle error condition

Returns:

  • (String)


186
187
188
189
190
191
# File 'lib/tty2/prompt/question.rb', line 186

def render_error(errors)
  errors.reduce([]) do |acc, err|
    acc << @prompt.decorate(">>", :red) + " " + err
    acc
  end.join("\n")
end

#render_questionString

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Render question

Returns:

  • (String)


142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/tty2/prompt/question.rb', line 142

def render_question
  header = []
  if !Utils.blank?(@prefix) || !Utils.blank?(message)
    header << "#{@prefix}#{message} "
  end
  if !echo?
    header
  elsif @done
    header << @prompt.decorate(@input.to_s, @active_color)
  elsif default? && !Utils.blank?(@default)
    header << @prompt.decorate("(#{default})", @help_color) + " "
  end
  header << "\n" if @done
  header.join
end

#required(value = (not_set = true), message = nil) ⇒ Boolean Also known as: required?

Ensure that passed argument is present or not

Returns:

  • (Boolean)


278
279
280
281
282
283
# File 'lib/tty2/prompt/question.rb', line 278

def required(value = (not_set = true), message = nil)
  messages[:required?] = message if message
  return @required if not_set

  @required = value
end

#to_sObject



380
381
382
# File 'lib/tty2/prompt/question.rb', line 380

def to_s
  message.to_s
end

#validate(value = nil, message = nil, &block) ⇒ Question

Set validation rule for an argument

Parameters:

  • value (Object) (defaults to: nil)

Returns:



293
294
295
296
# File 'lib/tty2/prompt/question.rb', line 293

def validate(value = nil, message = nil, &block)
  messages[:valid?] = message if message
  @validation = (value || block)
end

#validation?Boolean

Returns:

  • (Boolean)


314
315
316
# File 'lib/tty2/prompt/question.rb', line 314

def validation?
  @validation != UndefinedSetting
end

#value(val) ⇒ Object

Prepopulate input with custom content



301
302
303
304
305
# File 'lib/tty2/prompt/question.rb', line 301

def value(val)
  return @value if val.nil?

  @value = val
end

#value?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Check if custom value is present

Returns:

  • (Boolean)


310
311
312
# File 'lib/tty2/prompt/question.rb', line 310

def value?
  @value != UndefinedSetting
end