This may be the last article on building command line apps with Ruby you’ll ever have to read.

Okay, not literally! But I think you’ll know enough to accomplish quite a lot by the time you’re done reading this.

There are a lot of Ruby gems that help you build command line apps. Thor, Escort, CRI, GLI, Methadone, and executable, just to name some of the libraries out there. While results have varied, I’ve personally had my share of hangups with these gems:

  • I get the whole kitchen sink. ASCII tables and ncurses support? I don’t need those things.
  • Bugs. Real, show stopping bugs. Several of these libraries have a few dozen open Github issues that show no promise of getting fixed any time soon. Call me cynical, but if I’m going to tie this gem into my app as a dependency, I want to have faith it’s going to be maintained.

While building my latest command line app, I eventually gave up and decided to just roll my own with Ruby’s standard libraries. The results were surprising.

It turns out Ruby’s OptionParser library offers everything you need. It’s just not very well documented. For those of you that aren’t familiar with the library, OptionParser parses out option arguments from ARGV (text such as “–option value”). You can also set defaults and define help text. What you’re left with after you call the parse! method is an ARGV array of commands the user passed (i.e. “test units”).

Taking Subcommand Options

This is almost enough to write a command line app, but not quite. When we’re dealing with subcommands, we often need to parse different options. “–debug” makes sense for a “show logs” command, but not so much for a “version” command.

By calling order! on a OptionParser object, we can actually build an ordered list of only the options we’re interested in paying attention to. The following was my first attempt at making this work.

As you can see, we’re simply looking up our subcommand in a hash and getting an OptionParser object. I don’t know about you, but I can’t imagine command parsing getting more simple than that. There’s no DSL magic here and no confusing configurations to worry about. We can now parse only the options we’re interested in.

Making Subcommands Do Stuff

This is all very nice and pretty looking, but how can we get our subcommands to actually do stuff? I mean they’re pretty useless if all they do is allow us to fill our options hash! The following is one solution I came up that I think works pretty well.

This uses the same hash lookup design to translate our subcommands into options, but allows us to also call a method on the classes of our choice (I’ve picked call here because it’s synonymous with Ruby’s proc#call, but you can pick anything you’d like). This ties our subcommand functionality together, and what we’re left with resembles a very simple command line framework using nothing but OptionParser and a hash.

Next Steps

There’s a really neat gem called Subcommand that takes a similar approach and provides a small DSL. I personally don’t like DSLs when I can avoid them, but I know other people like them.

If you wanted to have the ability to have sub subcommands, all you would need to do is look deeper into a hash, using what remains in ARGV as keys. I will leave this as an exercise to the reader!

If you need to accept an argument that is not a command (i.e. app new [name_of_app] –options), you could set an “arguments” option for each subcommand that tells it how many arguments to grab. This way, we know that X number of elements in ARGV should get added into options and where we can stop parsing subcommands. I will also leave this as an exercise to the reader.

There are other things you can do to take this further, but I’m sure you get the idea by now. All of these designs revolve around the hash, and I think that’s pretty cool!

Ruby’s OptionParser Is All You Need

Leave a Reply

Your email address will not be published.