Reason #163 • June 12th, 2026

Parsing command-line options with OptionParser

For the past week we've been implementing a simple CLI tool using ARGV for argument handling and an assortment of dynamic dispatch techniques. Hand-rolling the argument handling is perfectly fine for small scripts, but if our tools start needing more complex option flags, we end up writing parsing, validation, type conversion and help text ourselves.

Fortunately, Ruby ships with a standard gem called optparse, which provides the OptionParser class to handle all of that for us:

Ruby (greet.rb)
require "optparse"

options = { greeting: "Hello", repeat: 1 }

OptionParser.new do |op|
  op.banner = "Usage: greet.rb [options] NAME"

  op.on("-g", "--greeting GREETING", "Custom greeting (default: Hello)") do |greeting|
    options[:greeting] = greeting
  end

  op.on("-s", "--shout", "Shout the greeting") do
    options[:shout] = true
  end

  op.on("-r", "--repeat COUNT", Integer, "Repeat the greeting COUNT times") do |count|
    options[:repeat] = count
  end
end.parse!

name = ARGV.first or abort "ERROR: Please provide a name"

greeting = "#{options[:greeting]}, #{name}!"
greeting = greeting.upcase if options[:shout]

options[:repeat].times { puts greeting }
    

Each call to on registers an option, with a short variant, a long variant, an optional type to coerce the value into, a description for the help text and a block to be called when the option is encountered. Calling parse! then consumes all the options from ARGV, leaving only the positional arguments behind for us to handle as we please:

$ ruby greet.rb Frodo
Hello, Frodo!

$ ruby greet.rb --greeting Howdy --shout Frodo
HOWDY, FRODO!

$ ruby greet.rb -r 3 Frodo
Hello, Frodo!
Hello, Frodo!
Hello, Frodo!

On top of this we get support for --help out of the box:

$ ruby greet.rb --help
Usage: greet.rb [options] NAME
    -g, --greeting GREETING          Custom greeting (default: Hello)
    -s, --shout                      Shout the greeting
    -r, --repeat COUNT               Repeat the greeting COUNT times

We also get automatic validation. Since we declared --repeat to be an Integer, passing something non-numeric raises an OptionParser::InvalidArgument, and unknown options raise an OptionParser::InvalidOption:

$ ruby greet.rb --repeat foo Gandalf
invalid argument: --repeat foo (OptionParser::InvalidArgument)

$ ruby greet.rb --waldo Gandalf
invalid option: --waldo (OptionParser::InvalidOption)

Besides Integer, we can coerce into Float, Array (comma-separated values), Regexp and more. There are even Date, Time and URI coercions available via require "optparse/date" and friends.

Finally, when options simply go into a Hash variable, like in our example, the into: keyword can do this for us, deriving the keys from the long option names:

Ruby
options = { greeting: "Hello", repeat: 1 }

OptionParser.new do |op|
  op.banner = "Usage: greet.rb [options] NAME"

  op.on("-g", "--greeting GREETING", "Custom greeting (default: Hello)")
  op.on("-s", "--shout", "Shout the greeting")
  op.on("-r", "--repeat COUNT", Integer, "Repeat the greeting COUNT times")
end.parse!(into: options)

# Given: ruby greet.rb -g Howdy --shout -r 2 Alice
options
# => {greeting: "Howdy", repeat: 2, shout: true}
ARGV
# => ["Alice"]
    

Consise declarative option parsing, type coercion, validation and help text. Gotta love the Ruby standad library!

History

OptionParser was written by Nobuyoshi "Nobu" Nakada, one of the most prolific Ruby core committers, affectionately known as "patch monster". It shipped with Ruby 1.8.0, released in August 2003.

Before OptionParser, the standard library offered getopts.rb and GetoptLong, both modeled after the GNU C function getopt_long. The OptionParser documentation pitched it as "much more advanced, yet also easier to use" than GetoptLong, and "a more Ruby-oriented solution".

The into: keyword was added in Ruby 2.4, released on Christmas 2016.

Reason #164 ?