Friday, November 04, 2011

Ruby, OptionParser, and Switches with Optional Arguments

Ruby's OptionParser supports switches with optional arguments. Both the official documentation and the pickaxe book give examples of various syntax choices like these:

"--switch [OPTIONAL]"
"--switch [=OPTIONAL]"
"--switch" "=[OPTIONAL]"

Somewhere along the way the first time I needed a switch with an optional argument I copied the third syntax, where the argument is specified as a separate string.

Later I noticed odd behavior. While my switches with required arguments could be used with or without an equals sign, my switches with optional arguments required an equals sign. I.e. "--switch foo" did not work for the switches with optional arguments, while "--switch=foo" did work.

I've lived with this oddity for several years. But then recently I picked up a copy of the new "Build Awesome Command-Line Applications in Ruby" book and was giving it a read. In their section about OptionParser they only listed the first syntax (all one string, no equals sign). That got me wondering if the
equals sign made a difference.

I went back and re-read the OptionParser section of the pickaxe book and noticed an interesting statement differentiating "--switch=ARG" from "--switch ARG". Specifically they say the later syntax "specifies the argument follows the switch."

Hmm, that sounds promising. Write a quick test. Yes! "--switch [ARG]" makes the argument optional, and with or without the equals sign. I should have stopped there, but now I was curious. So I tried "--switch=[ARG]", and oddly it works the same way. Optional argument, optional equals sign. Hmm, that's odd. Try the "--switch" "=[ARG]" syntax again just to make sure I'm not going crazy. Nope, not crazy. Argument is optional, but equals sign is not.

So, a bit more pondering and I come up with 9 possible syntaxes and set about to try them all:

"--switch [OPTIONAL]"
"--switch [=OPTIONAL]"
"--switch =[OPTIONAL]"
"--switch" "=[OPTIONAL]"
"--switch" "[=OPTIONAL]"
"--switch" "[OPTIONAL]"
"--switch[OPTIONAL]"
"--switch[=OPTIONAL]"
"--switch=[OPTIONAL]"

Interesting, all 9 are valid syntax for OptionParser. 8 work in terms of the argument being optional, "--switch =[OPTIONAL]" being the odd one where the argument is required despite the square brackets (tested with both ruby 1.8.7 and 1.9.2).

And finally, drumroll please, the distinction that actually makes the equals sign required is when you specify the argument as a separate string. The presence or position of the equals sign when specifying the switch to OptionParser makes no difference, except as noted with the "--switch =[OPTIONAL]" syntax. "--switch [=OPTIONAL]"? No equals sign required. "--switch" "[OPTIONAL]"? Equals sign required.  Wacky.

+1 to the Pickaxe book for prompting me to ponder the different syntaxes, but -1 for being somewhere between misleading and wrong in actually documenting the difference.