class HighLine::Question


process is handled according to the users wishes.
HighLine.ask() and then queried to make sure each step of the input
HighLine.ask(). The object is initialized by the parameters passed to
Question objects contain all the details of a single invocation of

def self.build(template_or_question, answer_type = nil, &details)

Returns:
  • (Question) -

Parameters:
  • details () -- to be passed to Question.new
  • answer_type (Class) -- to what class to convert the answer
  • template_or_question (String, Question) -- what to ask
def self.build(template_or_question, answer_type = nil, &details)
  if template_or_question.is_a? Question
    template_or_question
  else
    Question.new(template_or_question, answer_type, &details)
  end
end

def answer_or_default(answer_string)

Returns:
  • (String) - the answer itself or a default message.

Parameters:
  • answer_string (String) --
def answer_or_default(answer_string)
  return default if answer_string.empty? && default
  answer_string
end

def append_default_to_template


not affected.
Trailing whitespace is preserved so the function of HighLine.say() is
Adds the default choice to the end of question between |...|.
def append_default_to_template
  return unless default.respond_to? :to_s
  default_str = default.to_s
  return if default_str.empty?
  if template =~ /([\t ]+)\Z/
    template << "|#{default_str}|#{Regexp.last_match(1)}"
  elsif template == ""
    template << "|#{default_str}|  "
  elsif template[-1, 1] == "\n"
    template[-2, 0] = "  |#{default_str}|"
  else
    template << "  |#{default_str}|"
  end
end

def ask_on_error_msg

Returns:
  • (String) - if :ask_on_error on responses Hash is set to
  • (self) - if :ask_on_error on responses Hash is set to :question
def ask_on_error_msg
  if final_responses[:ask_on_error] == :question
    self
  elsif final_responses[:ask_on_error]
    final_responses[:ask_on_error]
  end
end

def build_responses(message_source = answer_type)

def build_responses(message_source = answer_type)
  append_default_to_template if default_hint_show
  new_hash = build_responses_new_hash(message_source)
  # Update our internal responses with the new hash
  # generated from the message source
  @internal_responses = @internal_responses.merge(new_hash)
end

def build_responses_new_hash(message_source)

Returns:
  • (Hash) - responses hash

Parameters:
  • message_source () --
def build_responses_new_hash(message_source)
  { ambiguous_completion: "Ambiguous choice.  Please choose one of " +
    choice_error_str(message_source) + ".",
    invalid_type: "You must enter a valid #{message_source}.",
    no_completion: "You must choose one of " +
      choice_error_str(message_source) + ".",
    not_in_range: "Your answer isn't within the expected range " \
      "(#{expected_range}).",
    not_valid: "Your answer isn't valid (must match " \
      "#{validate.inspect})." }
end

def change_case(answer_string)

Returns:
  • (String) - upcased, downcased, capitalized

Parameters:
  • answer_string (String) --
def change_case(answer_string)
  if [:up, :upcase].include?(@case)
    answer_string.upcase
  elsif [:down, :downcase].include?(@case)
    answer_string.downcase
  elsif @case == :capitalize
    answer_string.capitalize
  else
    answer_string
  end
end

def check_range

Run {#in_range?} and raise an error if not succesful
def check_range
  raise NotInRangeQuestionError unless in_range?
end

def choice_error_str(message_source)

def choice_error_str(message_source)
  if message_source.is_a? Array
    "[" + message_source.join(", ") + "]"
  else
    message_source.inspect
  end
end

def choices_complete(answer_string)

Returns:
  • (String) -

Parameters:
  • answer_string (String) --
def choices_complete(answer_string)
  # cheating, using OptionParser's Completion module
  choices = selection
  choices.extend(OptionParser::Completion)
  answer = choices.complete(answer_string)
  raise NoAutoCompleteMatch unless answer
  answer
end

def confirm_question(highline)

Returns:
  • (String) - {#confirm} rendered as a template if it is a String
  • (String) - default "Are you sure?" if {#confirm} is +true+

Parameters:
  • highline (HighLine) -- context
def confirm_question(highline)
  if confirm == true
    "Are you sure?  "
  elsif confirm.is_a?(Proc)
    confirm.call(answer)
  else
    # evaluate ERb under initial scope, so it will have
    # access to question and answer
    template = if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
      ERB.new(confirm, trim_mode: "%")
    else
      ERB.new(confirm, nil, "%")
    end
    template_renderer = TemplateRenderer.new(template, self, highline)
    template_renderer.render
  end
end

def convert


completed for any reason.
This method throws ArgumentError, if the conversion cannot be

Class.parse().
any other Class:: The answer is passed on to
the result returned.
Symbol:: The method to_sym() is called on answer and
Regexp:: Answer is fed to Regexp.new().
HighLine::String:: Answer is converted with HighLine::String()
String:: Answer is converted with Kernel.String().
returned.
Pathname:: Same as File, save that a Pathname object is
+nil+:: Answer is left in String format. (Default.)
Integer:: Answer is converted with Kernel.Integer().
Float:: Answer is converted with Kernel.Float().
returned.
terms of _directory_ + _glob_, opened, and
File:: The entered file name is auto-completed in
DateTime:: DateTime.parse() is called with answer.
Date:: Date.parse() is called with answer.
lambda {...}:: Answer is passed to lambda for conversion.
answers.
Auto-completion is used to expand partial
[...]:: Answer must be a member of the passed Array.

Question. Currently supported conversions are:
Transforms the given _answer_string_ into the expected type for this
def convert
  AnswerConverter.new(self).convert
end

def default_responses_hash

def default_responses_hash
  {
    ask_on_error: "?  ",
    mismatch: "Your entries didn't match."
  }
end

def expected_range

Returns an English explanation of the current range settings.
def expected_range
  expected = []
  expected << "above #{above}" if above
  expected << "below #{below}" if below
  expected << "included in #{@in.inspect}" if @in
  case expected.size
  when 0 then ""
  when 1 then expected.first
  when 2 then expected.join(" and ")
  else        expected[0..-2].join(", ") + ", and #{expected.last}"
  end
end

def final_response(error)

def final_response(error)
  response = final_responses[error]
  if response.respond_to?(:call)
    response.call(answer)
  else
    response
  end
end

def final_responses

generated internally via build_response
Notice that we give @user_responses precedence over the responses
This is the actual responses hash that gets used in determining output
def final_responses
  @internal_responses.merge(@user_responses)
end

def first_answer

Returns _first_answer_, which will be unset following this call.
def first_answer
  @first_answer
ensure
  @first_answer = nil
end

def first_answer?

Returns true if _first_answer_ is set.
def first_answer?
  true if @first_answer
end

def format_answer(answer_string)

Returns:
  • (String) - converted String

Parameters:
  • answer_string (String) --
def format_answer(answer_string)
  answer_string = String(answer_string)
  answer_string = remove_whitespace(answer_string)
  change_case(answer_string)
end

def get_echo_for_response(response)

Returns:
  • (String) - empty string if {#echo} is falsy.
  • (String) - echo character if {#echo} is truethy. Mainly a String.
  • (String) - the response itself if {#echo} is +true+.

Parameters:
  • response (String) --
def get_echo_for_response(response)
  # actually true, not only truethy value
  if echo == true
    response
  # any truethy value, probably a String
  elsif echo
    echo
  # any falsy value, false or nil
  else
    ""
  end
end

def get_response(highline)

def get_response(highline)
  return first_answer if first_answer?
  case character
  when :getc
    highline.get_response_getc_mode(self)
  when true
    highline.get_response_character_mode(self)
  else
    highline.get_response_line_mode(self)
  end
end

def get_response_or_default(highline)

def get_response_or_default(highline)
  self.answer = answer_or_default(get_response(highline))
end

def in_range?


are not checked.
_in_ attribute. Otherwise, +false+ is returned. Any +nil+ attributes
attribute, less than the _below_ attribute and include?()ed in the
Returns +true+ if the _answer_object_ is greater than the _above_
def in_range?
  (!above || answer > above) &&
    (!below || answer < below) &&
    (!@in || @in.include?(answer))
end

def initialize(template, answer_type)

Parameters:
  • answer_type (Class) -- the type the answer will be converted to it.
  • template (String) -- any String
def initialize(template, answer_type)
  # initialize instance data
  @template    = String(template).dup
  @answer_type = answer_type
  @completion  = @answer_type
  @echo         = true
  @default_hint_show = true
  @whitespace   = :strip
  @case         = nil
  @in           = nil
  @first_answer = nil
  @glob         = "*"
  @overwrite    = false
  @user_responses = {}
  @internal_responses = default_responses_hash
  @directory = Pathname.new(File.expand_path(File.dirname($PROGRAM_NAME)))
  # allow block to override settings
  yield self if block_given?
  # finalize responses based on settings
  build_responses
end

def remove_whitespace(answer_string)

Returns:
  • (String) - answer string with whitespaces removed

Parameters:
  • answer_string (String) --
def remove_whitespace(answer_string)
  if !whitespace
    answer_string
  elsif [:strip, :chomp].include?(whitespace)
    answer_string.send(whitespace)
  elsif whitespace == :collapse
    answer_string.gsub(/\s+/, " ")
  elsif [:strip_and_collapse, :chomp_and_collapse].include?(whitespace)
    result = answer_string.send(whitespace.to_s[/^[a-z]+/])
    result.gsub(/\s+/, " ")
  elsif whitespace == :remove
    answer_string.gsub(/\s+/, "")
  else
    answer_string
  end
end

def responses


validation checks fail.
:not_valid:: The error message shown when
the range requirement tests.
provided answer did not satisfy
:not_in_range:: Used to notify the user that a
auto-completion match.
selection does not have a valid
:no_completion:: Used to notify the user that their
conversion fails.
:invalid_type:: The error message shown when a type
original question.
:question to repeat the
of an error. Can be set to
redisplayed to the user in the event
:ask_on_error:: This is the question that will be
system cannot resolve.
ambiguous answer the auto-completion
:ambiguous_completion:: Used to notify the user of an

follows:
the user. The currently used responses and their purpose are as
A Hash that stores the various responses used by HighLine to notify
def responses
  @user_responses
end

def selection


Pathname. Any other time, this method will return an empty Array.
only known when _answer_type_ is set to an Array of choices, File, or
Returns an Array of valid answers to this question. These answers are
def selection
  if completion.is_a?(Array)
    completion
  elsif [File, Pathname].include?(completion)
    Dir[File.join(directory.to_s, glob)].map do |file|
      File.basename(file)
    end
  else
    []
  end
end

def show_question(highline)

Returns:
  • (void) -

Parameters:
  • highline (HighLine) -- context
def show_question(highline)
  highline.say(self)
end

def to_s

Stringifies the template to be asked.
def to_s
  template
end

def valid_answer?


and case handling.
It's important to realize that an answer is validated after whitespace

_validate_ attribute or +false+ if it's not.
Returns +true+ if the provided _answer_string_ is accepted by the
def valid_answer?
  !validate ||
    (validate.is_a?(Regexp) && answer =~ validate) ||
    (validate.is_a?(Proc)   && validate[answer]) ||
    (validate.respond_to?(:valid?) && validate.valid?(answer))
end