Skip to content

Instantly share code, notes, and snippets.

@JoshCheek
Created February 8, 2015 09:27
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JoshCheek/7adc25a46e735510558d to your computer and use it in GitHub Desktop.
Save JoshCheek/7adc25a46e735510558d to your computer and use it in GitHub Desktop.
Why I will not use Ruby's OptionParser
# Have to hijack the global environment, b/c that's how invasive it is!
def self.wtf
to_return = {exception: nil, stdout: nil, stderr: nil}
require 'stringio'
$stdout = StringIO.new
$stderr = StringIO.new
to_return[:returned] = yield
rescue Exception
to_return[:exception] = $!
ensure
to_return[:stdout] = $stdout.string
to_return[:stderr] = $stderr.string
$stdout = STDOUT
$stderr = STDERR
return to_return
end
require 'optparse/time'
parser = OptionParser.new do |o|
o.version = '1.2.3'
o.on '-x'
end
# Parsing has the side effect of printing directly to stdout
wtf { parser.parse ['-v'] }[:stdout] # => "program 1.2.3\n"
wtf { parser.parse ['--version'] }[:stdout] # => "program 1.2.3\n"
wtf { parser.parse ['-h'] }[:stdout] # => "Usage: program [options]\n -x\n"
wtf { parser.parse ['--help'] }[:stdout] # => "Usage: program [options]\n -x\n"
# Parsing some arguments might cause your program to suddenly quit...
wtf { parser.parse ['-v'] }[:exception] # => #<SystemExit: exit>
wtf { parser.parse ['--version'] }[:exception] # => #<SystemExit: exit>
wtf { parser.parse ['-h'] }[:exception] # => #<SystemExit: exit>
wtf { parser.parse ['--help'] }[:exception] # => #<SystemExit: exit>
# Note that while it takes flags for help and version, it does not list these
wtf { parser.parse ['-h'] }[:stdout] # => "Usage: program [options]\n -x\n"
# Normal parsing seems to do nothing (removes parsed vars, but doesn't return anything useful, and doesn't change any state)
pre_inspect = parser.inspect # => "#<OptionParser:0x007f8e1c8d27a8 @stack=[#<OptionParser::List:0x007f8e1c04ab08 @atype={Object=>[/.*/m, #<Proc:0x007f8e1c04a090@/Users/josh/.rubies/ruby-2.1.2/lib/ruby/2.1.0/optparse.rb:1618>], NilClass=>[/.*/m, #<Proc:0x007f8e1c049fc8@/Users/josh/.rubies/ruby-2.1.2/lib/ruby/2.1.0/optparse.rb:1620>], String=>[/.+/m, #<Proc:0x007f8e1c049f00@/Users/josh/.rubies/ruby-2.1.2/lib/ruby/2.1.0/optpa...
parser.parse ['arg', '-x'] # => ["arg"]
pre_inspect == parser.inspect # => true
# Guerilla patches ARGV
ARGV.getopts("ab:", "foo", "bar:") # => {"a"=>false, "b"=>nil, "foo"=>false, "bar"=>nil}
# But only ARGV, so if you wanted to use this, you'd have to also have the global dep
[].getopts("ab:", "foo", "bar:") rescue $! # => #<NoMethodError: undefined method `getopts' for []:Array>
# Parses options that weren't specified and don't make sense
# and prints directly to stderr about it
wtf { OptionParser.new.parse ['-v'] }[:stderr] # => "program: version unknown\n"
# Doesn't complete on its version or help flags, also, more printing to stdout
wtf { parser.parse ['--*-completion-bash', '-'] }[:stdout] # => "-x\n"
# Raises exceptions instead of setting errors
wtf { parser.parse! ['-i'] } # => {:exception=>#<OptionParser::InvalidOption: -i>, :stdout=>"", :stderr=>""}
# Parses time very poorly
to_parse = "...take one down, pass it around, 10 bottles of beer on the wall!"
parsed = nil
parser.on('-t T', Time) { |t| parsed = t }.parse ['-t', to_parse] # => []
parsed # => 2015-02-10 00:00:00 -0700
@noahgibbs
Copy link

Wow. This is a fabulous explanation of what's wrong with Ruby's default option parser.

Now I need to go check Trollop and see how many of these it does :-)

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment