OptionParser Argument Casting

drbrain | Sun, 06 Jan 2008 04:56:00 GMT

Posted in

OptionParser is a command-line argument parsing library for Ruby that provides several really nice features. Using OptionParser tends to be verbose, but it is also very flexible. One of it's features that I really like is argument casting.

Argument casting allows you to validate a command-line option and convert it from the user-supplied String into whichever object you like. The ri for OptionParser has an example similar to this one for casting a floating-point argument into a Float value:

require 'optparse'

options = {}

opts = OptionParser.new do |opts|
  # Cast 'delay' argument to a Float.
  opts.on("--delay N", Float,
          "Delay N seconds before executing") do |n|
    options[:delay] = n
  end
end

opts.parse! ARGV

p options

The second argument to opts.on, Float, tells OptionParser to cast the option's value to a Float before passing it to the handler block. When you run this example with a number as the argument, you'll see the value in options is a Float:

$ ruby op_test.rb --delay 1
{:delay=>1.0}

If you pass a value that can't be cast into a Float an OptionParser::InvalidArgument is raised:

$ ruby op_test.rb --delay X
[...]/optparse.rb:454:in `parse': invalid argument: --delay X (OptionParser::InvalidArgument)
    [...]
	from [...]/optparse.rb:1353:in `parse!'
	from op_test.rb:13

You can rescue this and provide an appropriate help message.

At the bottom you'll find some tables of the various casts that are built-in to OptionParser. If none of those do what you want, writing your own is very easy.

In RubyGems, various arguments are automatically cast to the appropriate objects. For example, when you specify a version with `gem install --version '= 1.2.3'`, the argument '= 1.2.3' is turned into a Gem::Requirement:

OptionParser.accept Gem::Requirement do |value|
  Gem::Requirement.new value
end

Gem::Requirement.new knows how to cast a String and raises an exception if it can't, so we delegate to it to do the work.

If you only want to have a specially formatted string, you can provide a regular expression instead. The DecimalInteger cast is defined like this:

DecimalInteger = /\A[-+]?\d+(?:_\d+)*/io
accept(DecimalInteger) {|s,| s.to_i if s}

So the pattern referenced by the name is used to validate the argument.

You can also provide a pattern as the second argument. The Float cast is defined like this:

floatpat = %r"\A[-+]?[...]"io
accept(Float, floatpat) {|s,| s.to_f if s}

Notice that for each of these, you still need to turn the string argument into the appropriate object.

OptionParser Built-in Casts

With no extra requires, OptionParser can cast the following arguments for you:

NameRequirementsCast to
Object, NilClassAny string, no conversionString
StringAny non-empty stringString
IntegerBinary (0b), octal (0), hexadecimal (0x), or decimal numberInteger
FloatFloating point numberFloat
NumericGeneric number formatFloat for floats, Integer for integers
OptionParser::DecimalIntegerDecimal integerInteger
OptionParser::OctalIntegerOctal, binary or hexadecimal numberInteger
OptionParser::DecimalNumericDecimal numberInteger or Float
TrueClass+, -, yes, no, true, false, niltrue or false, defaults to true
FalseClass+, -, yes, no, true, false, niltrue or false, defaults to false
ArrayComma-separated listArray of Strings
RegexpRegular expression with optionsRegexp

If you require 'optparse/date':

NameRequirementsCast to
DateTimeAnything handled by DateTime.parseDateTime
DateAnything handled by Date.parseDate

If you require 'optparse/shellwords':

NameRequirementsCast to
ShellwordsAnything handled by Shellwords.shellwordsArray of Strings

If you require 'optparse/time':

NameRequirementsCast to
TimeAnything handled by Time.parseTime

If you require 'optparse/uri':

NameRequirementsCast to
URIAnything handled by URI.parseURI
comments

Comments RSS FEED

Sweet. I did not know about value casting.

Dr Nic said 36 minutes later

There’s also value completion, but I don’t use that yet.

Eric Hodel said about 2 hours later

Comments are disabled