Skip to content

Commit

Permalink
Merge pull request #162 from matugm/patch-2
Browse files Browse the repository at this point in the history
Extract QuestionAsker class
  • Loading branch information
abinoam committed Sep 27, 2015
2 parents 5d815a8 + df22332 commit 3900e35
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 170 deletions.
170 changes: 10 additions & 160 deletions lib/highline.rb
Expand Up @@ -15,6 +15,7 @@
require "stringio"
require "abbrev"
require "highline/terminal"
require "highline/custom_errors"
require "highline/question"
require "highline/menu"
require "highline/color_scheme"
Expand All @@ -35,23 +36,7 @@
#
class HighLine
include BuiltinStyles

# An internal HighLine error. User code does not need to trap this.
class QuestionError < StandardError
# do nothing, just creating a unique error type
end

class NotValidQuestionError < QuestionError
# do nothing, just creating a unique error type
end

class NotInRangeQuestionError < QuestionError
# do nothing, just creating a unique error type
end

class NoConfirmationQuestionError < QuestionError
# do nothing, just creating a unique error type
end
include CustomErrors

# The setting used to disable color output.
@use_color = true
Expand Down Expand Up @@ -129,7 +114,7 @@ def initialize( input = $stdin, output = $stdout,
@output = output

@multi_indent = true
@indent_size = indent_size
@indent_size = indent_size
@indent_level = indent_level

self.wrap_at = wrap_at
Expand All @@ -155,7 +140,7 @@ def initialize( input = $stdin, output = $stdout,

attr_reader :input, :output

attr_reader :key
attr_accessor :key

# System specific that responds to #initialize_system_extensions,
# #terminal_size, #raw_no_echo_mode, #restore_mode, #get_character.
Expand Down Expand Up @@ -194,7 +179,12 @@ def agree( yes_or_no_question, character = nil )
#
def ask(template_or_question, answer_type = nil, &details)
question = Question.build(template_or_question, answer_type, &details)
question.ask_at(self)

if question.gather
QuestionAsker.new(question, self).gather_answers
else
QuestionAsker.new(question, self).ask_once
end
end

#
Expand Down Expand Up @@ -416,150 +406,10 @@ def explain_error(error, question)
say(question.ask_on_error_msg)
end

#
# Gets one answer, as opposed to HighLine#gather
#
def ask_once(question)

# readline() needs to handle its own output, but readline only supports
# full line reading. Therefore if question.echo is anything but true,
# the prompt will not be issued. And we have to account for that now.
# Also, JRuby-1.7's ConsoleReader.readLine() needs to be passed the prompt
# to handle line editing properly.
say(question) unless ((question.readline) and (question.echo == true and !question.limit))

begin
question.get_response_or_default(self)
raise NotValidQuestionError unless question.valid_answer?

question.convert

if question.confirm
# need to add a layer of scope (new_scope) to ask a question inside a
# question, without destroying instance data

raise NoConfirmationQuestionError unless confirm(question)
end

rescue NoConfirmationQuestionError
explain_error(nil, question)
retry

rescue NotInRangeQuestionError
explain_error(:not_in_range, question)
retry

rescue NotValidQuestionError
explain_error(:not_valid, question)
retry

rescue QuestionError
retry

rescue ArgumentError => error
case error.message
when /ambiguous/
# the assumption here is that OptionParser::Completion#complete
# (used for ambiguity resolution) throws exceptions containing
# the word 'ambiguous' whenever resolution fails
explain_error(:ambiguous_completion, question)
retry
when /invalid value for/
explain_error(:invalid_type, question)
retry
else
raise
end

rescue Question::NoAutoCompleteMatch
explain_error(:no_completion, question)
retry
end
question.answer
end

def confirm(question)
new_scope.agree(question.confirm_question(self))
end


public :ask_once

#
# Collects an Array/Hash full of answers as described in
# HighLine::Question.gather().
#
# Raises EOFError if input is exhausted.
#
def gather(question)
original_question_template = question.template
verify_match = question.verify_match

begin # when verify_match is set this loop will repeat until unique_answers == 1
question.template = original_question_template

answers =
case question.gather
when Integer
gather_integer(question)
when ::String, Regexp
gather_regexp(question)
when Hash
gather_hash(question)
end

if verify_match && (unique_answers(answers).size > 1)
explain_error(:mismatch, question)
else
verify_match = false
end

end while verify_match

question.verify_match ? last_answer(answers) : answers
end

public :gather

def gather_integer(question)
answers = []

answers << ask_once(question)

question.template = ""

(question.gather-1).times do
answers << ask_once(question)
end

answers
end

def gather_regexp(question)
answers = []

answers << ask_once(question)

question.template = ""
until (question.gather.is_a?(::String) and answers.last.to_s == question.gather) or
(question.gather.is_a?(Regexp) and answers.last.to_s =~ question.gather)
answers << ask_once(question)
end

answers.pop
answers
end

def gather_hash(question)
answers = {}

question.gather.keys.sort.each do |key|
@key = key
answers[key] = ask_once(question)
end
answers
end

#
# A helper method used by HighLine::Question.verify_match
# for finding whether a list of answers match or differ
Expand Down
19 changes: 19 additions & 0 deletions lib/highline/custom_errors.rb
@@ -0,0 +1,19 @@
class HighLine
module CustomErrors
# Internal HighLine errors.
class QuestionError < StandardError
end

class NotValidQuestionError < QuestionError
end

class NotInRangeQuestionError < QuestionError
end

class NoConfirmationQuestionError < QuestionError
end

class NoAutoCompleteMatch < StandardError
end
end
end
12 changes: 2 additions & 10 deletions lib/highline/question.rb
Expand Up @@ -21,10 +21,7 @@ class HighLine
# process is handled according to the users wishes.
#
class Question
# An internal HighLine error. User code does not need to trap this.
class NoAutoCompleteMatch < StandardError
# do nothing, just creating a unique error type
end
include CustomErrors

#
# If _template_or_question_ is already a Question object just return it.
Expand All @@ -49,7 +46,7 @@ def initialize(template, answer_type)
# initialize instance data
@template = template.dup
@answer_type = answer_type
@completion = @answer_type
@completion = @answer_type

@character = nil
@limit = nil
Expand Down Expand Up @@ -484,11 +481,6 @@ def get_response_or_default(highline)
self.answer = answer_or_default(get_response(highline))
end

def ask_at(highline)
return highline.gather(self) if gather
return highline.ask_once(self)
end

def confirm_question(highline)
if confirm == true
"Are you sure? "
Expand Down

0 comments on commit 3900e35

Please sign in to comment.