6

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?

Keith Bennett
  • 4,722
  • 1
  • 25
  • 35
  • 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; end` – engineersmnky Feb 07 '19 at 17:11
  • 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. – Keith Bennett Feb 08 '19 at 19:46
  • 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? – engineersmnky Feb 08 '19 at 19:57
  • 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 https://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`. – Keith Bennett Feb 10 '19 at 04:23

2 Answers2

9

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)
ulferts
  • 2,187
  • 12
  • 19
  • 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. – Keith Bennett Feb 10 '19 at 04:32
  • @KeithBennett, I updated my answer after trying out for myself. Before, I simply for skimmed over the documentation. Sorry for being hasty there. – ulferts Feb 10 '19 at 12:17
  • 1
    Well done, my friend! Many thanks. BTW, I like to use `OpenStruct` objects for options. More convenient than hashes. – Keith Bennett Feb 11 '19 at 13:25
  • I learned quite a few things so that has been worth it. Yes `OpenStruct`s are fine and way more readable in most scenarios. Not that it is a problem here, but they also tend to be quite slow. – ulferts Feb 11 '19 at 15:11
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

Keith Bennett
  • 4,722
  • 1
  • 25
  • 35
  • No Boolean class in Ruby is pathetic and anti user friendly. Using "-vn 1" is cryptic, hacky, and not intuitive at all. – hipertracker Jun 05 '20 at 08:41
  • In Ruby values have classes, not variables and turns out there are only few common methods to `true` and `false`. https://www.rubytapas.com/2019/01/08/boolean/ – karatedog Jul 21 '21 at 01:30
  • 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)`. – Keith Bennett Jul 22 '21 at 04:21
  • @hipertracker 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. – Keith Bennett Jul 22 '21 at 04:26