Suggest timepoints using OSM database of POIs
[bus.git] / maxious-canberra-transit-feed / highline.rb
blob:a/maxious-canberra-transit-feed/highline.rb -> blob:b/maxious-canberra-transit-feed/highline.rb
--- a/maxious-canberra-transit-feed/highline.rb
+++ b/maxious-canberra-transit-feed/highline.rb
@@ -1,1 +1,140 @@
+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
+