--------------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--