module CommandKit::Interactive

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"

Public Instance Methods

ask(prompt, default: nil, required: false) click to toggle source

Asks the user for input.

@param [String] prompt

The prompt that will be printed before reading input.

@param [String, nil] default

The default value to return if no input is given.

@param [Boolean] required

Requires non-empty input.

@return [String]

The user input.

@example

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

@example Default value:

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

@example Required non-empty input:

ask("Email", required: true)
# Email: <enter>
# Email: bob@example.com<enter>
# => "bob@example.com"

@api public

# File lib/command_kit/interactive.rb, line 72
def ask(prompt, default: nil, required: false)
  prompt = prompt.chomp
  prompt << " [#{default}]" if default
  prompt << ": "

  stdout.print(prompt)

  loop do
    value = stdin.gets
    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) click to toggle source

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

@param [String] prompt

The prompt that will be printed before reading input.

@param [Hash{String => String}, Array<String>] choices

The choices to select from.

@param [Hash{Symbol => Object}] kwargs

Additional keyword arguments for {#ask}.

@option kwargs [String, nil] default

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

@option kwargs [Boolean] required

Requires non-empty input.

@return [String]

The selected choice.

@example 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"

@example 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"

@api public

# File lib/command_kit/interactive.rb, line 185
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) click to toggle source

Asks the user for secret input.

@example

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

@api public

# File lib/command_kit/interactive.rb, line 228
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) click to toggle source

Asks the user a yes or no question.

@param [String] prompt

The prompt that will be printed before reading input.

@param [true, false, nil] default

@return [Boolean]

Specifies whether the user entered Y/yes.

@example

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

@example Default value:

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

@api public

# File lib/command_kit/interactive.rb, line 118
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