module OptParseGen

Small lib for generating an OptionParser from an OpenStruct

Constants

SPECIAL_POSTFIXES

Special postfixes for Hash keys

Public Class Methods

[](ostruct, **options) click to toggle source

Does the magic

@todo write documentation :( @todo split this up @param ostruct [OpenStruct] Default values with special values @param :ignore_collisions [Boolean] ignore bool key collisions see OptionCollision @param :generate_no_help [Boolean] when set to true donesn't generates help command

# File lib/optsparser_generator.rb, line 60
def self.[](ostruct, **options)
  defaults = handle_arguments(ostruct)

  optparser = OptionParser.new do |opts|
    opts.instance_variable_set(:@required, {})
    defaults.each_pair do |key, val|
      trigger = key.to_s.tr('_', '-')
      next if trigger.end_with?(*SPECIAL_POSTFIXES)

      arguments = generate_arguments(defaults, key, val)
      case val
      when FalseClass, TrueClass
        uneven_no = /\Ano(-no-no)*-(?!no)/ =~ trigger
        if trigger.start_with?('no-')
          trigger.gsub!(/\A(no-)+/, '') # removes no- prefixes
          check_collisions(trigger, key, defaults) unless options[:ignore_collisions]
        end
        arguments.unshift "--[no-]#{trigger}"
        block = lambda do |bool|
          # inverted when it starts with no_
          bool ^ uneven_no
        end
      else
        klass = val.class
        klass = val.is_a?(Integer) ? Integer : klass
        klass = defaults.special_value(key, 'class') || klass
        arguments.push klass

        values = defaults.special_value(key, 'values')
        arguments.push values if values
        arguments.unshift "--#{trigger}=ARG"
        block = lambda do |str|
          str
        end
      end
      if (proc = defaults.special_value(key, 'proc'))
        block = proc
      end
      opts.on(*arguments) do |arg|
        opts.instance_variable_get(:@required)[key] = true
        opts.instance_variable_get(:@out)[key] = block.call(arg)
      end
      if defaults.special_value(key, 'required')
        opts.instance_variable_get(:@required)[key] = false
      end
    end

    unless options[:generate_no_help]
      opts.on('-h', '--help') do
        puts opts
        exit
      end
    end
  end

  # add default values
  optparser.instance_variable_set(:@defaults, defaults)
  optparser.extend(OptParsePatch)
end
parse(ostruct, argv, **opt) click to toggle source

Shorthand when parsing is only needed once.

Generates an OptionParser and calls parse on it @see OptionParserGenerator#[] @return [OpenStruct]

# File lib/optsparser_generator.rb, line 173
def self.parse(ostruct, argv, **opt)
  self[ostruct, **opt].parse(argv)
end
parse!(ostruct, argv = ARGV, **opt) click to toggle source

Same as parse, removes parsed elements from argv @see OptionParserGenerator#parse @return [OpenStruct]

# File lib/optsparser_generator.rb, line 180
def self.parse!(ostruct, argv = ARGV, **opt)
  self[ostruct, **opt].parse!(argv)
end

Private Class Methods

check_collisions(trigger, key, defaults) click to toggle source

@api private

# File lib/optsparser_generator.rb, line 140
def self.check_collisions(trigger, key, defaults)
  if defaults.each_pair.map{ |v| v.first.to_s }.include?(trigger)
    raise OptionCollision, "on #{key}"
  end
end
generate_arguments(defaults, key, val) click to toggle source

returns an array of helptext and if set the short version of trigger @api private

# File lib/optsparser_generator.rb, line 123
def self.generate_arguments(defaults, key, val)
  short = defaults.special_value(key, 'short') || ''
  if short.length > 1
    raise ArgumentError, 'short is too long, it has to be only one character'
  end

  help = "#{defaults.special_value(key, 'help')} (Default: #{val})"

  if short.empty?
    [help]
  else
    [help, "-#{short}"]
  end
end
handle_arguments(ostruct) click to toggle source

Does some sanity checks and prepares the OpenStruct @todo preprocess data here instead of doing it adhoc @todo raise more exceptions @api private @param ostruct [OpenStruct]

# File lib/optsparser_generator.rb, line 43
def self.handle_arguments(ostruct)
  unless ostruct.is_a?(OpenStruct)
    raise WrongArgumentType, 'needs an OpenStruct'
  end
  ostruct = ostruct.dup
  ostruct.extend(OpenStructExtension)
  ostruct.freeze # freeze is not needed but makes development easier
end