Module: CommandKit::Interactive

Includes:
Stdio
Defined in:
lib/command_kit/interactive.rb

Overview

Provides methods for asking the user for input.

Examples

first_name = ask("First name")
last_name = ask("Last name")

Asking for secret input

password = ask_secret("Password")

Asking Y/N?

if ask_yes_or_no("Proceed anyways?")
  # ...
else
  stderr.puts "Aborting!"
end

Asking multi-choice questions

ask_multiple_choice("Select a flavor", %w[Apple Orange Lemon Lime])
#   1) Apple
#   2) Orange
#   3) Lemon
#   4) Lime
#   Select a flavor: 4
#
# => "Lime"

Prompt for multi-line input

ask_multiline('Comment')
# Comment (Press Ctrl^D to exit):
# foo bar
# baz qux
# Ctrl^D
# => "foo bar\nbaz qux\n"

Instance Method Summary collapse

Methods included from Stdio

#abort, #gets, #initialize, #print, #printf, #putc, #puts, #readline, #readlines, #stderr, #stdin, #stdout

Instance Method Details

#ask(prompt, default: nil, required: false) ⇒ String

Asks the user for input.

Examples:

first_name = ask("First name")
last_name = ask("Last name")

Default value:

ask("Country", default: "EU")
# Country [EU]: <enter>
# => "EU"

Required non-empty input:

ask("Email", required: true)
# Email: <enter>
# Email: [email protected]<enter>
# => "[email protected]"

Parameters:

  • prompt (String)

    The prompt that will be printed before reading input.

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

    The default value to return if no input is given.

  • required (Boolean) (defaults to: false)

    Requires non-empty input.

Returns:

  • (String)

    The user input.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/command_kit/interactive.rb', line 81

def ask(prompt, default: nil, required: false)
  prompt = prompt.chomp
  prompt << " [#{default}]" if default
  prompt << ": "

  loop do
    stdout.print(prompt)

    value = stdin.gets(chomp: true)
    value ||= '' # convert nil values (ctrl^D) to an empty String

    if value.empty?
      if required
        next
      else
        return (default || value)
      end
    else
      return value
    end
  end
end

#ask_multiline(prompt, help: nil, default: nil, required: false, terminator: :ctrl_d) ⇒ String

Asks the user for multi-line text input.

Examples:

ask_multiline('Comment')
# Comment (Press Ctrl^D to exit):
# foo bar
# baz qux
# Ctrl^D
# => "foo bar\nbaz qux\n"

Terminate input on a double newline:

ask_multiline('Comment', terminator: :double_newline)
# Comment (Enter two empty lines to exit):
# foo bar
# baz qux
#
# => "foo bar\nbaz qux\n"

Parameters:

  • prompt (String)

    The prompt that will be printed before reading input.

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

    Optional help instructions on how to exit from reading.

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

    The default value to return if no input is given.

  • required (Boolean) (defaults to: false)

    Requires non-empty input.

  • terminator (:double_newline, :ctrl_d) (defaults to: :ctrl_d)

    Indicates how the input should be terminated. Defaults to :ctrl_d which indicates Ctrl^D.

Returns:

  • (String)

    The user input.

Since:

  • 0.6.0



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/command_kit/interactive.rb', line 298

def ask_multiline(prompt, help:       nil,
                          default:    nil,
                          required:   false,
                          terminator: :ctrl_d)
  case terminator
  when :ctrl_d
    eos    = nil
    help ||= 'Press Ctrl^D to exit'
  when :double_newline
    eos    = "#{$/}#{$/}"
    help ||= 'Press Enter twice to exit'
  else
    raise(ArgumentError,"invalid terminator: #{terminator.inspect}")
  end

  prompt = "#{prompt.chomp} (#{help})"
  prompt << " [#{default}]" if default
  prompt << ": "

  loop do
    stdout.puts(prompt)

    value = stdin.gets(eos)
    value ||= '' # convert nil values (ctrl^D) to an empty String

    if value.empty?
      if required
        next
      else
        return (default || value)
      end
    else
      return value
    end
  end
end

#ask_multiple_choice(prompt, choices, **kwargs) ⇒ String

Asks the user to select a choice from a list of options.

Examples:

Array of choices:

ask_multiple_choice("Select a flavor", %w[Apple Orange Lemon Lime])
#   1) Apple
#   2) Orange
#   3) Lemon
#   4) Lime
#   Select a flavor: 4
#
# => "Lime"

Hash of choices:

ask_multiple_choice("Select an option", {'A' => 'Foo',
                                         'B' => 'Bar',
                                         'X' => 'All of the above'})
#   A) Foo
#   B) Bar
#   X) All of the above
#   Select an option: X
#
# => "All of the above"

Parameters:

  • prompt (String)

    The prompt that will be printed before reading input.

  • choices (Hash{String => String}, Array<String>)

    The choices to select from.

  • kwargs (Hash{Symbol => Object})

    Additional keyword arguments for #ask.

Options Hash (**kwargs):

  • default (String, nil)

    The default option to fallback to, if no input is given.

  • required (Boolean)

    Requires non-empty input.

Returns:

  • (String)

    The selected choice.



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/command_kit/interactive.rb', line 194

def ask_multiple_choice(prompt,choices,**kwargs)
  choices = case choices
            when Array
              Hash[choices.each_with_index.map { |value,i|
                [(i+1).to_s, value]
              }]
            when Hash
              choices
            else
              raise(TypeError,"unsupported choices class #{choices.class}: #{choices.inspect}")
            end

  prompt = "#{prompt} (#{choices.keys.join(', ')})"

  loop do
    # print the choices
    choices.each do |choice,value|
      stdout.puts "  #{choice}) #{value}"
    end
    stdout.puts

    # read the choice
    choice = ask(prompt,**kwargs)

    if choices.has_key?(choice)
      # if a valid choice is given, return the value
      return choices[choice]
    else
      stderr.puts "Invalid selection: #{choice}"
    end
  end
end

#ask_secret(prompt, required: true) ⇒ String

Asks the user for secret input.

Examples:

ask_secret("Password")
# Password: 
# => "s3cr3t"

Parameters:

  • prompt (String)

    The prompt that will be printed before reading input.

  • required (Boolean) (defaults to: true)

    Requires non-empty input.

Returns:

  • (String)

    The user input.



246
247
248
249
250
251
252
253
254
# File 'lib/command_kit/interactive.rb', line 246

def ask_secret(prompt, required: true)
  if stdin.respond_to?(:noecho)
    stdin.noecho do
      ask(prompt, required: required)
    end
  else
    ask(prompt, required: required)
  end
end

#ask_yes_or_no(prompt, default: nil, **kwargs) ⇒ Boolean

Asks the user a yes or no question.

Examples:

ask_yes_or_no("Proceed anyways?")
# Proceed anyways? (Y/N): Y
# => true

Default value:

ask_yes_or_no("Proceed anyways?", default: true)
# Proceed anyways? (Y/N) [Y]: <enter>
# => true

Parameters:

  • prompt (String)

    The prompt that will be printed before reading input.

  • default (true, false, nil) (defaults to: nil)

Returns:

  • (Boolean)

    Specifies whether the user entered Y/yes.



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/command_kit/interactive.rb', line 127

def ask_yes_or_no(prompt, default: nil, **kwargs)
  default = case default
            when true  then 'Y'
            when false then 'N'
            when nil  then nil
            else
              raise(ArgumentError,"invalid default: #{default.inspect}")
            end

  prompt = "#{prompt} (Y/N)"

  loop do
    answer = ask(prompt, **kwargs, default: default)

    case answer.downcase
    when 'y', 'yes'
      return true
    else
      return false
    end
  end
end