Class Boson::OptionParser

  1. lib/boson/option_parser.rb
Parent: Object

This class concisely defines commandline options that when parsed produce a Hash of option keys and values. Additional points:

  • Setting option values should follow conventions in *nix environments. See examples below.
  • By default, there are 5 option types, each which produce different objects for option values.
  • The default option types can produce objects for one or more of the following Ruby classes: String, Integer, Float, Array, Hash, FalseClass, TrueClass.
  • Users can define their own option types which create objects for any Ruby class. See Options.
  • Each option type can have attributes to enable more features (see OptionParser.new).
  • When options are parsed by parse(), an IndifferentAccessHash hash is returned.
  • Options are also called switches, parameters, flags etc.
  • Option parsing stops when it comes across a ’—’.

Default option types:

:boolean
This option has no passed value. To toogle a boolean, prepend with ’—no-’. Multiple booleans can be joined together.
'--debug'    -> {:debug=>true}
'--no-debug' -> {:debug=>false}
'--no-d'     -> {:debug=>false}
'-d -f -t' same as '-dft'
:string
Sets values by separating name from value with space or ’=’.
'--color red' -> {:color=>'red'}
'--color=red' -> {:color=>'red'}
'--color "gotta love spaces"' -> {:color=>'gotta love spaces'}
:numeric
Sets values as :string does or by appending number right after aliased name. Shortened form can be appended to joined booleans.
'-n3'  -> {:num=>3}
'-dn3' -> {:debug=>true, :num=>3}
:array
Sets values as :string does. Multiple values are split by a configurable character Default is ’,’ (see OptionParser.new). Passing ’*’ refers to all known :values.
'--fields 1,2,3' -> {:fields=>['1','2','3']}
'--fields *'     -> {:fields=>['1','2','3']}
:hash
Sets values as :string does. Key-value pairs are split by ’:’ and pairs are split by a configurable character (default ’,’). Multiple keys can be joined to one value. Passing ’*’ as a key refers to all known :keys.
'--fields a:b,c:d' -> {:fields=>{'a'=>'b', 'c'=>'d'} }
'--fields a,b:d'   -> {:fields=>{'a'=>'d', 'b'=>'d'} }
'--fields *:d'     -> {:fields=>{'a'=>'d', 'b'=>'d', 'c'=>'d'} }

This is a modified version of Yehuda Katz’s Thor::Options class which is a modified version of Daniel Berger’s Getopt::Long class (licensed under Ruby’s license).

Classes and Modules

Class Boson::OptionParser::Error

Constants

NUMERIC = /(\d*\.\d+|\d+)/
LONG_RE = /^(--\w+[-\w+]*)$/
SHORT_RE = /^(-[a-zA-Z])$/i
EQ_RE = /^(--\w+[-\w+]*|-[a-zA-Z])=(.*)$/i
SHORT_SQ_RE = /^-([a-zA-Z]{2,})$/i
SHORT_NUM = /^(-[a-zA-Z])#{NUMERIC}$/i
STOP_STRINGS = %w{-- -}

Attributes

leading_non_opts [R]
opt_aliases [R]
trailing_non_opts [R]

Public class methods

new (opts)

Takes a hash of options. Each option, a key-value pair, must provide the option’s name and type. Names longer than one character are accessed with ’—’ while one character names are accessed with ’-’. Names can be symbols, strings or even dasherized strings:

Boson::OptionParser.new :debug=>:boolean, 'level'=>:numeric,
  '--fields'=>:array

Options can have default values and implicit types simply by changing the option type for the default value:

Boson::OptionParser.new :debug=>true, 'level'=>3.1, :fields=>%w{f1 f2}

By default every option name longer than one character is given an alias, the first character from its name. For example, the —fields option has -f as its alias. You can override the default alias by providing your own option aliases as an array in the option’s key.

Boson::OptionParser.new [:debug, :damnit, :D]=>true

Note that aliases are accessed the same way as option names. For the above, —debug, —damnit and -D all refer to the same option.

Options can have additional attributes by passing a hash to the option value instead of a type or default:

Boson::OptionParser.new :fields=>{:type=>:array, :values=>%w{f1 f2 f3},
 :enum=>false}

These attributes are available when an option is parsed via current_attributes(). Here are the available option attributes for the default option types:

:type
This or :default is required. Available types are :string, :boolean, :array, :numeric, :hash.
:default
This or :type is required. This is the default value an option has when not passed.
:bool_default
This is the value an option has when passed as a boolean. However, by enabling this an option can only have explicit values with ’=’ i.e. ’—index=alias’ and no ’—index alias’. If this value is a string, it is parsed as any option value would be. Otherwise, the value is passed directly without parsing.
:required
Boolean indicating if option is required. Option parses raises error if value not given. Default is false.
:alias
Alternative way to define option aliases with an option name or an array of them. Useful in yaml files. Setting to false will prevent creating an automatic alias.
:values
An array of values an option can have. Available for :array and :string options. Values here can be aliased by typing a unique string it starts with or underscore aliasing (see Util.underscore_search). For example, for values foo, odd and obnoxiously_long, f refers to foo, od to odd and o_l to obnoxiously_long.
:enum
Boolean indicating if an option enforces values in :values or :keys. Default is true. For :array, :hash and :string options.
:split
For :array and :hash options. A string or regular expression on which an array value splits to produce an array of values. Default is ’,’.
:keys
:hash option only. An array of values a hash option’s keys can have. Keys can be aliased just like :values.
:default_keys
For :hash option only. Default keys to assume when only a value is given. Multiple keys can be joined by the :split character. Defaults to first key of :keys if :keys given.
:regexp
For :array option with a :values attribute. Boolean indicating that each option value does a regular expression search of :values. If there are values that match, they replace the original option value. If none, then the original option value is used.
[show source]
# File lib/boson/option_parser.rb, line 165
    def initialize(opts)
      @defaults = {}
      @opt_aliases = {}
      @leading_non_opts, @trailing_non_opts = [], []

      # build hash of dashed options to option types
      # type can be a hash of opt attributes, a default value or a type symbol
      @opt_types = opts.inject({}) do |mem, (name, type)|
        name, *aliases = name if name.is_a?(Array)
        name = name.to_s
        # we need both nice and dasherized form of option name
        if name.index('-') == 0
          nice_name = undasherize name
        else
          nice_name = name
          name = dasherize name
        end
        # store for later
        @opt_aliases[nice_name] = aliases || []

        if type.is_a?(Hash)
          @option_attributes ||= {}
          @option_attributes[nice_name] = type
          @opt_aliases[nice_name] = Array(type[:alias]) if type.key?(:alias)
          @defaults[nice_name] = type[:default] if type[:default]
          @option_attributes[nice_name][:enum] = true if (type.key?(:values) || type.key?(:keys)) &&
            !type.key?(:enum)
          @option_attributes[nice_name][:default_keys] ||= type[:keys][0] if type.key?(:keys)
          type = type[:type] || (!type[:default].nil? ? determine_option_type(type[:default]) : :boolean)
        end

        # set defaults
        case type
        when TrueClass                     then  @defaults[nice_name] = true
        when FalseClass                    then  @defaults[nice_name] = false
        else @defaults[nice_name] = type unless type.is_a?(Symbol)
        end
        mem[name] = !type.nil? ? determine_option_type(type) : type
        mem
      end

      # generate hash of dashed aliases to dashed options
      @opt_aliases = @opt_aliases.sort.inject({}) {|h, (nice_name, aliases)|
        name = dasherize nice_name
        # allow for aliases as symbols
        aliases.map! {|e| e.to_s.index('-') == 0 || e == false ? e : dasherize(e.to_s) }
        if aliases.empty? and nice_name.length > 1
          opt_alias = nice_name[0,1]
          opt_alias = h.key?("-"+opt_alias) ? "-"+opt_alias.capitalize : "-"+opt_alias
          h[opt_alias] ||= name unless @opt_types.key?(opt_alias)
        else
          aliases.each {|e| h[e] = name if !@opt_types.key?(e) && e != false }
        end
        h
      }
    end
parse (options, args=ARGV)

Given options to pass to OptionParser.new, this method parses ARGV and returns the remaining arguments and a hash of parsed options. This is useful for scripts outside of Boson.

[show source]
# File lib/boson/option_parser.rb, line 86
    def self.parse(options, args=ARGV)
      @opt_parser ||= new(options)
      parsed_options = @opt_parser.parse(args)
      [@opt_parser.non_opts, parsed_options]
    end
usage ()

Usage string summarizing options defined in parse

[show source]
# File lib/boson/option_parser.rb, line 93
    def self.usage
      @opt_parser.to_s
    end

Public instance methods

aliases ()

List of option aliases

[show source]
# File lib/boson/option_parser.rb, line 349
    def aliases
      @opt_aliases.keys.map {|e| undasherize e }
    end
current_attributes ()

Hash of option attributes for the currently parsed option. Any hash keys passed to an option are available here. This means that an option type can have any user-defined attributes available during option parsing and object creation.

[show source]
# File lib/boson/option_parser.rb, line 324
    def current_attributes
      @option_attributes && @option_attributes[@current_option] || {}
    end
dasherize (str)

Adds dashes to an option name i.e. ‘date’ -> ’—date’ and ‘d’ -> ’-d’.

[show source]
# File lib/boson/option_parser.rb, line 334
    def dasherize(str)
      (str.length > 1 ? "--" : "-") + str
    end
default_usage (opt, val)

Helper method to generate usage. Takes a dashed option and a string value indicating an option value’s format.

[show source]
# File lib/boson/option_parser.rb, line 267
    def default_usage(opt, val)
      opt + "=" + (@defaults[undasherize(opt)] || val).to_s
    end
formatted_usage ()

Generates one-line usage of all options.

[show source]
# File lib/boson/option_parser.rb, line 272
    def formatted_usage
      return "" if @opt_types.empty?
      @opt_types.map do |opt, type|
        val = respond_to?("usage_for_#{type}", true) ? send("usage_for_#{type}", opt) : "#{opt}=:#{type}"
        "[" + val + "]"
      end.join(" ")
    end
names ()

List of option names

[show source]
# File lib/boson/option_parser.rb, line 344
    def names
      @opt_types.keys.map {|e| undasherize e }
    end
non_opts ()

Array of arguments left after defined options have been parsed out by parse.

[show source]
# File lib/boson/option_parser.rb, line 106
    def non_opts
      leading_non_opts + trailing_non_opts
    end
option_attributes ()

Hash of option names mapped to hash of its external attributes

[show source]
# File lib/boson/option_parser.rb, line 312
    def option_attributes
      @option_attributes || {}
    end
option_type (opt)
[show source]
# File lib/boson/option_parser.rb, line 353
    def option_type(opt)
      if opt =~ /^--no-(\w+)$/
        @opt_types[opt] || @opt_types[dasherize($1)] || @opt_types[original_no_opt($1)]
      else
        @opt_types[opt]
      end
    end
parse (args, flags={})

Parses an array of arguments for defined options to return an IndifferentAccessHash. Once the parser recognizes a valid option, it continues to parse until an non option argument is detected. Flags that can be passed to the parser:

  • :opts_before_args: When true options must come before arguments. Default is false.
  • :delete_invalid_opts: When true deletes any invalid options left after parsing. Will stop deleting if it comes across - or —. Default is false.
[show source]
# File lib/boson/option_parser.rb, line 228
    def parse(args, flags={})
      @args = args
      # start with defaults
      hash = IndifferentAccessHash.new @defaults
      
      @leading_non_opts = []
      unless flags[:opts_before_args]
        @leading_non_opts << shift until current_is_option? || @args.empty? || STOP_STRINGS.include?(peek)
      end

      while current_is_option?
        case @original_current_option = shift
        when SHORT_SQ_RE
          unshift $1.split('').map { |f| "-#{f}" }
          next
        when EQ_RE, SHORT_NUM
          unshift $2
          option = $1
        when LONG_RE, SHORT_RE
          option = $1
        end

        dashed_option = normalize_option(option)
        @current_option = undasherize(dashed_option)
        type = option_type(dashed_option)
        validate_option_value(type)
        value = create_option_value(type)
        # set on different line since current_option may change
        hash[@current_option.to_sym] = value
      end

      @trailing_non_opts = @args
      check_required! hash
      delete_invalid_opts if flags[:delete_invalid_opts]
      hash
    end
print_usage_table (render_options={})

More verbose option help in the form of a table.

[show source]
# File lib/boson/option_parser.rb, line 283
    def print_usage_table(render_options={})
      user_fields = render_options.delete(:fields)
      fields = get_usage_fields user_fields
      (fields << :default).uniq! if render_options.delete(:local) || user_fields == '*'
      opts = all_options_with_fields fields
      fields.delete(:default) if fields.include?(:default) && opts.all? {|e| e[:default].nil? }
      render_options = default_render_options.merge(:fields=>fields).merge(render_options)
      View.render opts, render_options
    end
to_s ()

Alias for formatted_usage

types ()

List of option types

[show source]
# File lib/boson/option_parser.rb, line 339
    def types
      @opt_types.values
    end
undasherize (str)

Removes dashes from a dashed option i.e. ’—date’ -> ‘date’ and ’-d’ -> ‘d’.

[show source]
# File lib/boson/option_parser.rb, line 329
    def undasherize(str)
      str.sub(/^-{1,2}/, '')
    end