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)
-
(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)
-
(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
-
(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)
-
(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)
-
(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
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)
-
(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)
-
(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
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
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
def first_answer @first_answer ensure @first_answer = nil end
def first_answer?
def first_answer? true if @first_answer end
def format_answer(answer_string)
-
(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)
-
(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)
-
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)
-
(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)
-
(void)
-
Parameters:
-
highline
(HighLine
) -- context
def show_question(highline) highline.say(self) end
def to_s
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