Class: Boson::OptionParser

Inherits:
Object
  • Object
show all
Includes:
API
Defined in:
lib/boson/option_parser.rb

Overview

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 indifferent access 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 (Ruby license).

Defined Under Namespace

Modules: API Classes: Error

Constant Summary collapse

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 =

Allow either -x -v or -xv style for single char args

/^-([a-zA-Z]{2,})$/i
SHORT_NUM =
/^(-[a-zA-Z])#{NUMERIC}$/i
STOP_STRINGS =
%w{-- -}

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from API

#get_fields_and_options, #render_table

Constructor Details

#initialize(opts) ⇒ OptionParser

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.



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/boson/option_parser.rb', line 139

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]
      if (type.key?(:values) || type.key?(:keys)) && !type.key?(:enum)
        @option_attributes[nice_name][:enum] = true
      end
      if type.key?(:keys)
        @option_attributes[nice_name][:default_keys] ||= type[:keys][0]
      end
      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

Instance Attribute Details

#leading_non_optsObject (readonly)

Returns the value of attribute leading_non_opts.



62
63
64
# File 'lib/boson/option_parser.rb', line 62

def leading_non_opts
  @leading_non_opts
end

#opt_aliasesObject (readonly)

Returns the value of attribute opt_aliases.



62
63
64
# File 'lib/boson/option_parser.rb', line 62

def opt_aliases
  @opt_aliases
end

#trailing_non_optsObject (readonly)

Returns the value of attribute trailing_non_opts.



62
63
64
# File 'lib/boson/option_parser.rb', line 62

def trailing_non_opts
  @trailing_non_opts
end

Instance Method Details

#aliasesObject

List of option aliases



325
326
327
# File 'lib/boson/option_parser.rb', line 325

def aliases
  @opt_aliases.keys.map {|e| undasherize e }
end

#current_attributesObject

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.



300
301
302
# File 'lib/boson/option_parser.rb', line 300

def current_attributes
  @option_attributes && @option_attributes[@current_option] || {}
end

#dasherize(str) ⇒ Object

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



310
311
312
# File 'lib/boson/option_parser.rb', line 310

def dasherize(str)
  (str.length > 1 ? "--" : "-") + str
end

#default_usage(opt, val) ⇒ Object

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



252
253
254
# File 'lib/boson/option_parser.rb', line 252

def default_usage(opt, val)
  opt + "=" + (@defaults[undasherize(opt)] || val).to_s
end

#delete_leading_invalid_optsObject



334
335
336
# File 'lib/boson/option_parser.rb', line 334

def delete_leading_invalid_opts
  delete_invalid_opts @leading_non_opts
end

#formatted_usageObject Also known as: to_s

Generates one-line usage of all options.



257
258
259
260
261
262
263
264
# File 'lib/boson/option_parser.rb', line 257

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

#indifferent_hashObject

Creates a Hash with indifferent access



330
331
332
# File 'lib/boson/option_parser.rb', line 330

def indifferent_hash
  Hash.new {|hash,key| hash[key.to_sym] if String === key }
end

#namesObject

List of option names



320
321
322
# File 'lib/boson/option_parser.rb', line 320

def names
  @opt_types.keys.map {|e| undasherize e }
end

#non_optsObject

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



65
66
67
# File 'lib/boson/option_parser.rb', line 65

def non_opts
  leading_non_opts + trailing_non_opts
end

#option_attributesObject

Hash of option names mapped to hash of its external attributes



292
293
294
# File 'lib/boson/option_parser.rb', line 292

def option_attributes
  @option_attributes || {}
end

#parse(args, flags = {}) ⇒ Object

Parses an array of arguments for defined options to return an indifferent access hash. Once the parser recognizes a valid option, it continues to parse until an non option argument is detected.

Parameters:

  • flags (Hash) (defaults to: {})

Options Hash (flags):

  • :opts_before_args (Boolean)

    When true options must come before arguments. Default is false.

  • :delete_invalid_opts (Boolean)

    When true deletes any invalid options left after parsing. Will stop deleting if it comes across - or –. Default is false.



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/boson/option_parser.rb', line 212

def parse(args, flags={})
  @args = args
  # start with symbolized defaults
  hash = Hash[@defaults.map {|k,v| [k.to_sym, v] }]

  @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]
  indifferent_hash.tap {|h| h.update hash }
end

More verbose option help in the form of a table.



269
270
271
272
273
# File 'lib/boson/option_parser.rb', line 269

def print_usage_table(options={})
  fields = get_usage_fields options[:fields]
  fields, opts =  get_fields_and_options(fields, options)
  render_table(fields, opts, options)
end

#typesObject

List of option types



315
316
317
# File 'lib/boson/option_parser.rb', line 315

def types
  @opt_types.values
end

#undasherize(str) ⇒ Object

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



305
306
307
# File 'lib/boson/option_parser.rb', line 305

def undasherize(str)
  str.sub(/^-{1,2}/, '')
end