mose/cliprompt

View on GitHub
lib/cliprompt/optionset.rb

Summary

Maintainability
A
2 hrs
Test Coverage
module Cliprompt

  class OptionException < StandardError
  end

  class Optionset

    attr_reader :choices, :default, :boolean, :envdefault, :aslist

    def initialize(options = nil)
      @choices = []
      @default = nil
      @boolean = false
      @envdefault = nil
      @aslist = false
      @type = options.class.name.downcase
      meth = "parse_#{@type}".to_sym
      if respond_to? meth
        send(meth, options)
      else
        fail OptionException, "Undefined parser ::#{meth}"
      end
    end

    # placeholder for nil type objects
    def parse_nilclass(args)
    end

    def parse_hash(args)
      @choices = args[:choices] || args['choices'] || []
      parse_array @choices
      if args[:default] == false || args['default'] == false
        @default ||= false
      else
        @default ||= args[:default] || args['default']
      end
      if args[:aslist] == false || args['aslist'] == false
        @aslist = false
      elsif args[:aslist] == true || args['aslist'] == true
        @aslist = true
      end
      @boolean = args[:boolean] || args['boolean']
      @default = true if (@boolean && @default.nil?)
      @envdefault = args[:env] || args['env']
    end

    def parse_array(args)
      @choices = args.map(&:to_s).map do |a|
        if a[0] && a[0] == '='
          @default = a[1..-1]
        else
          a
        end
      end
      @aslist = (@choices.count > 5)
    end

    def parse_fixnum(arg)
      @default = arg.to_s
    end

    def parse_float(arg)
      @default = arg.to_s
    end

    def parse_string(arg)
      if /^[yY1](es)?(\/)?[nN0](o)?/.match(arg)
        @boolean = true
        if /y(es)?(\/)?N/.match(arg)
          @default = false
        else
          @default = true
        end
      else
        @default = arg
      end
    end

    def display
      if @boolean
        display_boolean
      elsif @choices.count > 0
        if @aslist
          display_list
        else
          display_choices
        end
      else
        display_default
      end
    end

    def display_boolean
      @default ? "[Y/n] " : "[y/N] "
    end

    def display_list
      back = "\n"
      choices.each_with_index do |choice, i|
        if @default == choice
          back << sprintf("> %-3s %s\n", i, choice)
        else
          back << sprintf("  %-3s %s\n", i, choice)
        end
      end
      back << "#{Cliprompt::MSG_CHOSE_A_NUMBER} "
      back << display_default_index.to_s
      return back
    end

    def display_choices
      back = ''
      if @choices.count > 0
        back << "(#{@choices.join(' / ')}) "
      end
      back << display_default.to_s
      return back
    end

    def display_default
      "[#{@default}] " if @default
    end

    def display_default_index
      "[#{@choices.index(@default)}] " if @default
    end

    def validate(question, answer)
      if answer == ''
        check_default question
      elsif @boolean
        check_boolean question, answer
      elsif @choices.count > 0
        if @aslist
          check_list question, answer
        else
          check_choices question, answer
        end
      else
        answer
      end
    end

    def check_default(question)
      return ask_again(question, Cliprompt::MSG_MANDATORY_TEXT) if @default.nil?
      @default
    end

    def check_boolean(question, answer)
      return ask_again(question, Cliprompt::MSG_YES_OR_NO) unless /^([yY1](es)?|[nN0](o)?)$/.match(answer)
      !/^[yY1](es)?$/.match(answer).nil?
    end

    def check_choices(question, answer)
      return ask_again(question, Cliprompt::MSG_CHOSE_IN_LIST) unless @choices.include?(answer)
      answer
    end

    def check_list(question, answer)
      return ask_again(question, Cliprompt::MSG_CHOSE_IN_LIST) unless answer.to_i < @choices.count
      return ask_again(question, Cliprompt::MSG_INPUT_A_NUMBER) unless /[0-9]+/.match(answer)
      @choices[answer.to_i]
    end

    def ask_again(question, msg)
      Cliprompt.shout msg
      Cliprompt.ask question, self
    end

  end
end