[SOLUTION] Highline (#29)

M

mark sparshatt

--------------020708000909060406020505
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

This is my attempt at a solution. It's implemented as a module
containing two public methods.

ask(prompt, type, options)

ask_if(prompt)

For ask type can either be a symbol (one of :string, :integer, :float or
:bool) or it can be a Proc object which is called in order to do the
conversion.

options is a hash which can have the following keys,
:validate - this can be either a regular expression or a Proc object
:within - compares the value to the specified range
:above
:below
:default
:base - when converting to an integer this specifies the base of the
number. it can be one of b,o,d or x

ask_if is just an alias for ask(prompt, :bool)

--
Mark Sparshatt

--------------020708000909060406020505
Content-Type: text/plain;
name="highline.rb"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="highline.rb"

module HighLine
#prompt = text to display
#type can be one of :string, :integer, :float, :bool or a proc
#if it's a proc then it is called with the entered string. If the input cannot be converted then it should throw an exception
#if type == :bool then y,yes are converted to true. n,no are converted to false. All other values are rejected.
#
#options should be a hash of validation options
#:validate => regular expresion or proc
#if validate is a regular expression then the input is matched against it
#if it's a proc then the proc is called and the input is accepted if it returns true
#:between => range
#the input is checked if it lies within the range
#:above => value
#the input is checked if it is above the value
#:below => value
#the input is checked if it is less than the value
#:default => string
#if the user doesn't enter a value then the default value is returned
#:base => [b, o, d, x]
#when asking for integers this will take a number in binary, octal, decimal or hexadecimal
def ask(prompt, type, options=nil)
begin
valid = true

default = option(options, :default)
if default
defaultstr = " |#{default}|"
else
defaultstr = ""
end

base = option(options, :base)

print prompt, "#{defaultstr} "
$stdout.flush
input = gets.chomp

if default && input == ""
input = default
end

#comvert the input to the correct type
input = case type
when :string: input
when :integer: convert(input, base) rescue valid = false
when :float: Float(input) rescue valid = false
when :bool
valid = input =~ /^(y|n|yes|no)$/
input[0] == ?y
when Proc: input = type.call(input) rescue valid = false
end

#validate the input
valid &&= validate(options, :validate) do |test|
case test
when Regexp: input =~ test
when Proc: test.call(input)
end
end
valid &&= validate(options, :within) { |range| range === input}
valid &&= validate(options, :above) { |value| input > value}
valid &&= validate(options, :below) { |value| input < value}

puts "Not a valid value" unless valid
end until valid

return input
end

#asks a yes/no question
def ask_if(prompt)
ask(prompt, :bool)
end

private

#extracts a key from the options hash
def option(options, key)
result = nil
if options && options.key?(key)
result = options[key]
end
result
end

#helper function for validation
def validate(options, key)
result = true
if options && options.key?(key)
result = yield options[key]
end
result
end

#converts a string to an integer
#input = the value to convert
#base = the numeric base of the value b,o,d,x
def convert(input, base)
if base
if ["b", "o", "d", "x"].include?(base)
input = "0#{base}#{input}"
value = Integer(input)
else
value = Integer(input)
end
else
value = Integer(input)
end

value
end
end



if __FILE__ == $0
include HighLine
#string input using a regexp to validate, returns test as the default value
p ask("enter a string, (all lower case)", :string, :validate => /^[a-z]*$/, :default => "test")
#string input using a proc to validate
p ask("enter a string, (between 3 and 6 characters)", :string, :validate => proc { |input| (3..6) === input.length})

#integer intput using :within
p ask("enter an integer, (0-10)", :integer, :within => 0..10)
#float input using :above
p ask("enter a float, (> 6)", :float, :above => 6)

#getting a binary value
p ask("enter a binary number", :integer, :base => "b")

#using a proc to convert the a comma seperated list into an array
p ask("enter a comma seperated list", proc { |x| x.split(/,/)})

p ask_if("do you want to continue?")
end

--------------020708000909060406020505--
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top