Program Arguments in Ruby II: OptionParser

Here is a link to my last medium post: Program Arguments in Ruby: Part I

In this article we’re going to explore the following topics:

  • the OptionParser class
  • OptionParser#on method
  • OptionParser#on and type coercion

The OptionParser class

OptionParser is a class that eases the parsing and the interpretation of command-line options.

This class provides a bunch of methods to handle banners, option help messages, type coercion, etc..

OptionParser#on method

The OptionParser#on method adds a switch associated to a handler to the parser.

A switch is an instance of an OptionParser::Switch-derived class.

The most important derived classes are:

- OptionParser::Switch::NoArgument
- OptionParser::Switch::RequiredArgument
- OptionParser::Switch::OptionalArgument

These classes are in charge of handling (or not) the argument according to their specialisation.

It also retains the block passed as argument of the OptionParser#on method. This block is stored as a proc inside the corresponding switch.

There is a lot of vocabulary to assimilate and this can be hard to unravel.

So, let’s break down this concept by a series of example for each switch.

OptionParser::Switch::NoArgument

The basic use of the OptionParser#on method is to handle an option that doesn’t expect any argument

# in parser.rb
require 'optparse'
require 'optparse/time'
OptionParser.new do |parser|
parser.on('-t', '--time') do |time|
p time
end
end.parse!

In the above example, the program will be able to associate the short option -t and the long option --time to the block passed as argument of the parser.on() method.

In this case, the option won’t require any argument.

So, the generated switch will be an instance of OptionParser::Switch::NoArgument.

This means that any argument passed to the -t option will be ignored

?> ruby parser.rb --time=11:12:13
true

Here, the 11:12:13 argument of the option--time is ignored and true is returned instead.

Now, let’s see how to require an argument for an given option.

OptionParser::Switch::RequiredArgument

Let’s see the syntax to require an argument.

# in parser.rb
require 'optparse'
require 'optparse/time'
OptionParser.new do |parser|
parser.on('-t', '--time=TIME') do |time|
p time
end
end.parse!
```

In this case, the -t option requires an argument designated by the TIME word.

So, the generated switch for the -t option will be an instance of OptionParser::Switch::RequiredArgument.

?> ruby parser.rb --time=11:12:13
"11:12:13"
?> ruby parser.rb --time
parser.rb:8:in `<main>’: missing argument: --time (OptionParser::MissingArgument)

Here, the 11:12:13 argument of the --time option is passed as argument of the block which is passed to the parser.on() method.

Notice that the time argument is a String.

Also, an OptionParser::MissingArgument error is raised if the argument is not provided to the --time option.

Now, let’s see how to add an optional argument to an option.

OptionParser::Switch::OptionalArgument

Let’s see the syntax to add an optional argument.


# in parser.rb
require 'optparse'
require 'optparse/time'
OptionParser.new do |parser|
parser.on('-t', '--time[=TIME]') do |time|
p time
end
end.parse!

The optional argument is designated by the =TIME word surrounded by square brackets ([=TIME]).

In this case, the generated switch for the -t option will be an instance of OptionParser::Switch::OptionalArgument.

?> ruby parser.rb --time=11:12:13
"11:12:13"
?> ruby parser.rb --time
nil

Here, the 11:12:13 argument of the--time option is passed as argument of the block passed to the parser.on() method.

Also, the time argument is nil if no argument is provided to the--time option.

The 11:12:13 contained in the time argument is a String. It should be cool to receive the block argument as a Time object.

OptionParser#on and Type Coercion

In the previous examples the 11:12:13 was always an instance of String.

It should be great to receive an instance of Time as block argument instead.

To do so, we can add a third argument to the OptionParser#on method. This argument excepts a Ruby class in order to convert the option’s argument to an instance of the given class type

require 'optparse'
require 'optparse/time'
OptionParser.new do |parser|
parser.on('-t', '--time=TIME', Time) do |time|
p time.class
end
end.parse!

Let’s run the program

?> ruby parser.rb --time=11:12:13
Time

Here, we can see that the time argument is now an instance of Time — as given as the third argument of the parser.on() method call.

Voilà!


Thank you for taking the time to read this post :-)

Feel free to 👏 and share this Medium post if it has been useful for you.

Here is a link to my last medium post:

Command-line Arguments in Ruby: Part I