Ehud Lev
Ehud Lev

Reputation: 2901

How to pass empty string to command line using OptionParser

I am trying to write a script that get some arguments where some of them might be empty.

It seems that Ruby's OptionParser is not allowing that and throws (OptionParser::InvalidArgument).

Code:

require 'optparse'

options = {}
OptionParser.new do |opt|

  opt.on('--might_be_empty might_be_empty', String) { |o| options[:might_be_empty] = o }

end.parse!

puts "might_be_empty: #{options[:might_be_empty]}"

Happy flow:

ruby ./for_stack.rb --might_be_empty "some_real_data"
might_be_empty: some_real_data

When the value is empty:

ruby ./for_stack.rb --might_be_empty ""
./for_stack.rb:10:in `<main>': invalid argument: --might_be_empty  (OptionParser::InvalidArgument)

How can I tell the OptionParser to allow empty strings?

Upvotes: 2

Views: 773

Answers (3)

Casper
Casper

Reputation: 34308

Leave coercion type unspecified, or use Object instead of String. Both behave the same.

opt.on('--might_be_empty might_be_empty') { ... }
# ..or
opt.on('--might_be_empty might_be_empty', Object) { ... }

Test:

ruby ./for_stack.rb --might_be_empty "some_real_data"
might_be_empty: some_real_data

ruby ./for_stack.rb --might_be_empty ""
might_be_empty: 

Upvotes: 5

the Tin Man
the Tin Man

Reputation: 160549

Option Parser allows optional values:

Running this several times:

require 'optparse'

options = {}
OptionParser.new do |opt|

  opt.on('--might_be_empty [arg]') { |o| options[:might_be_empty] = o }

end.parse!

puts 'options[:might_be_empty].has_key? is %s' % options.has_key?(:might_be_empty)
puts 'options[:might_be_empty] is "%s"' % options[:might_be_empty]
puts 'options[:might_be_empty] is a %s' % options[:might_be_empty].class
pp ARGV

Shows me:

$ ruby test.rb -m
options[:might_be_empty].has_key? is true
options[:might_be_empty] is ""
options[:might_be_empty] is a NilClass
[]

$ ruby test.rb -m foo
options[:might_be_empty].has_key? is true
options[:might_be_empty] is "foo"
options[:might_be_empty] is a String
[]

$ ruby test.rb -m 1
options[:might_be_empty].has_key? is true
options[:might_be_empty] is "1"
options[:might_be_empty] is a String
[]

This is documented a couple times in the sample code but not explicitly stated in the text:

def perform_inplace_option(parser)
  # Specifies an optional option argument
  parser.on("-i", "--inplace [EXTENSION]",
            "Edit ARGV files in place",
            "(make backup if EXTENSION supplied)") do |ext|
    self.inplace = true
    self.extension = ext || ''
    self.extension.sub!(/\A\.?(?=.)/, ".")  # Ensure extension begins with dot.
  end
end

Also note that you don't have to coerce the returned value because it's already a String. Any values passed in from the command-line are strings as they're snatched from ARGV.

Upvotes: 0

Simple Lime
Simple Lime

Reputation: 11035

According to the docs for OptionParser Type Coercion, passing String isn't just a "do nothing":

String – Any non-empty string

However, if you just leave the Argument pattern off of on (which directs you to the docs for make_switch):

Acceptable option argument format, must be pre-defined with #accept or #accept, or Regexp. This can appear once or assigned as String if not present, otherwise causes an ArgumentError.

While slightly confusing that it's "assigned as String if not present", it's not "assigned as a non-empty String if not present", and it will default to passing you any String, and work as you want it to:

opt.on('--might_be_empty might_be_empty') { |o| options[:might_be_empty] = o }

# is optional
% ruby example.rb       
might_be_empty:
# if passed, must have a value
% ruby example.rb --might_be_empty 
Traceback (most recent call last):
example.rb:8:in '<main>': missing argument: --might_be_empty (OptionParser::MissingArgument)
# can pass an empty string
% ruby example.rb --might_be_empty ""
might_be_empty:
# can pass any string
% ruby example.rb --might_be_empty "not empty"
might_be_empty: not empty

If you don't want to just leave the argument pattern off, you can create custom conversions, though this seems like overkill to me.

Upvotes: 1

Related Questions