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).
Methods
public class
public instance
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
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.
# 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
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.
# 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 string summarizing options defined in parse
# File lib/boson/option_parser.rb, line 93 def self.usage @opt_parser.to_s end
Public instance methods
List of option aliases
# File lib/boson/option_parser.rb, line 349 def aliases @opt_aliases.keys.map {|e| undasherize e } end
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.
# File lib/boson/option_parser.rb, line 324 def current_attributes @option_attributes && @option_attributes[@current_option] || {} end
Adds dashes to an option name i.e. ‘date’ -> ’—date’ and ‘d’ -> ’-d’.
# File lib/boson/option_parser.rb, line 334 def dasherize(str) (str.length > 1 ? "--" : "-") + str end
Helper method to generate usage. Takes a dashed option and a string value indicating an option value’s format.
# File lib/boson/option_parser.rb, line 267 def default_usage(opt, val) opt + "=" + (@defaults[undasherize(opt)] || val).to_s end
Generates one-line usage of all options.
# 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
List of option names
# File lib/boson/option_parser.rb, line 344 def names @opt_types.keys.map {|e| undasherize e } end
Array of arguments left after defined options have been parsed out by parse.
# File lib/boson/option_parser.rb, line 106 def non_opts leading_non_opts + trailing_non_opts end
Hash of option names mapped to hash of its external attributes
# File lib/boson/option_parser.rb, line 312 def option_attributes @option_attributes || {} end
# 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
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.
# 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
More verbose option help in the form of a table.
# 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
List of option types
# File lib/boson/option_parser.rb, line 339 def types @opt_types.values end
Removes dashes from a dashed option i.e. ’—date’ -> ‘date’ and ’-d’ -> ‘d’.
# File lib/boson/option_parser.rb, line 329 def undasherize(str) str.sub(/^-{1,2}/, '') end