Ruby OptionParser Short Code for Boolean Option?
Asked Answered
C

2

7

I am using Ruby's OptionParser (require 'optparse') processing a "verbose" option that can be either true or false. It is in the code like this:

  parser.on('-v', '--[no-]verbose', 'Verbose mode') do |v|
    self.verbose = v
  end

I support specifying options in an environment variable (I prepend its content to ARGV), so it is possible to set verbose mode on in that environment variable, and override it on the command line with --no-verbose. However, I cannot find a way to override it with a short option. I've tried these without success:

-v-
-v0
-v=0

I found the source code at https://github.com/ruby/ruby/blob/trunk/lib/optparse.rb but could not figure out the answer from that.

How can I do this?

Crayton answered 7/2, 2019 at 15:35 Comment(4)
If you don't pass it at all then it will be falsey (e.g. nil) so I am not sure why you would need to override it using the short code? You could also initialize it as false e.g. def initialize; self.verbose = false; endNunes
I explained why I need this in the second (noncode) paragraph of the question; could you read it again please, and if it is still not clear I will try to explain another way.Crayton
So you are saying the environment would be configured for verbosity but sometimes you want to turn it off from the command line? How are you prepending this to ARGV can you post that code as well?Nunes
Yes, exactly, effectively for creating a nonstandard default temporarily, but one that can be overridden for an individual invocation of rexe. The method prepend_environment_options that prepends to ARGV is at github.com/keithrbennett/rexe/blob/master/exe/rexe#L53-L59 at the moment, but the code may change over time; if so, search for the method name or ARGV.Crayton
W
10

Based on https://github.com/ruby/ruby/blob/trunk/lib/optparse.rb#L1936-L1949 and given how a -v flag works for most commands the following should work:

  • -v - similar to what you tried but with a space
  • -v no
  • -v false

Edit

After the comment I looked further into the problem and tried it out myself. This is what I ended up with:

# optparser.rb

require 'optparse'

options = {}
OptionParser.new do |opts|
  opts.on("-v", "--[no-]verbose [FLAG]", TrueClass, "Run verbosely") do |v|
    options[:verbose] = v.nil? ? true : v
  end
end.parse!

p options

The important changes to the code by OP are:

  • Adding the [FLAG] 'argument'. This will enable an argument for the option like -v no, -v yes, -v false, -v true, -v n, -v y, -v + (I did not get the -v - to work).
  • Adding the explicit argument coercion TrueClass. Without it, the argument will be interpreted as a string (e.g. 'false').
  • Turning the argument optional by wrapping the argument definition in [ ] and then ensuring that true is considered the default via v.nil? ? true : v. Without the braces, the argument parser does not accept -v (without an argument)
Weatherbound answered 7/2, 2019 at 16:16 Comment(4)
I could not get any of these to work with a space. I was able to get it to work with -vn. If you'd like to test this you can gem install rexe here is a simple command line: rexe -mn -v off_flag 1, where off_flag is the `-v -', -v no', etc. I tried escaping the space with a backslash and putting the option in double quotes (e.g. "-v n") but none of that worked.Crayton
@KeithBennett, I updated my answer after trying out for myself. Before, I simply for skimmed over the documentation. Sorry for being hasty there.Weatherbound
Well done, my friend! Many thanks. BTW, I like to use OpenStruct objects for options. More convenient than hashes.Crayton
I learned quite a few things so that has been worth it. Yes OpenStructs are fine and way more readable in most scenarios. Not that it is a problem here, but they also tend to be quite slow.Weatherbound
C
0

Putting n directly after the boolean flag (e.g. -vn) sets it to false. This example turns on and then off the verbose boolean flag and then prints the number 1:

rexe -mn -v -vn 1

Crayton answered 10/2, 2019 at 4:34 Comment(4)
No Boolean class in Ruby is pathetic and anti user friendly. Using "-vn 1" is cryptic, hacky, and not intuitive at all.Underthrust
In Ruby values have classes, not variables and turns out there are only few common methods to true and false. rubytapas.com/2019/01/08/booleanQuackenbush
true's class is TrueClass and false's is FalseClass. I recently had a situation where I wished those 2 classes subclassed a common class like Boolean. Also, I wonder why true and false could not have been instances of a Boolean class. I don't know of any good way to test whether a value is boolean other than something like [true, false].include?(value) or (value == true || value == false).Crayton
@Underthrust I don't know if you understand...Ruby does have true and false; the question of this post is how to specify boolean values on the Posix command line. That has to be done with a string of some kind. Ruby's OptionParser class does convert that string to true or false, as long as the arguments to opts.on are correct.Crayton

© 2022 - 2024 — McMap. All rights reserved.