--------------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;
 nameighline.rb"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filenameighline.rb"

module HighLine
  #prompt  ext 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, optionsl)
    begin
      valid  rue

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

      base  ption(options, :base)

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

      if default && input ""
        input  efault
      end

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

      #validate the input
      valid && alidate(options, :validate) do |test|
        case test
        when Regexp: input test
        when Proc: test.call(input)
        end
      end
      valid && alidate(options, :within) { |range| range  input}
      valid && alidate(options, :above) { |value| input > value}
      valid && alidate(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  il
    if options && options.key?(key)
      result  ptions[key]
    end
    result
  end

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

  #converts a string to an integer
  #input  he value to convert
  #base  he 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  nteger(input)
      else
        value  nteger(input)
      end
    else
      value  nteger(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--