Bug #20328
closedoptparse omits the option's description in the --help output if the description is an Array
Description
If you define an option using OptionParser#on
, but give the option's description as a multi-line Array, then the option's description is omitted from the --help
output.
Steps To Reproduce¶
#!/usr/bin/env ruby
require 'optparse'
optparser = OptionParser.new do |opts|
opts.banner = 'usage: test.rb [options]'
opts.on('-o', '--opt [OPT]', 'Line one') do |opt|
end
opts.on('-m', '--multiline-opt', ['Line one', 'Line two']) do |test|
end
opts.on('-h', '--help', 'Prints this help') do
puts opts
exit
end
end
optparser.parse(['--help'])
Expected result¶
usage: test.rb [options]
-o, --opt [OPT] Line one
-m, --multiline-opt Line one
Line two
-h, --help Prints this help
Actual Result¶
usage: test.rb [options]
-o, --opt [OPT] Line one
-m, --multiline-opt
-h, --help Prints this help
or an ArgumentError
should be raised if Array descriptions are not allowed/supported.
Version Info¶
Tested against optparse 0.1.0, 0.2.0, 0.3.1, and the master branch.
Updated by bkuhlmann (Brooke Kuhlmann) 8 months ago
The confusion is with the array (third argument) used with your --multiline-opt
option. If you change it to the following, you'll get the desired result:
opts.on "-m", "--multiline-opt", "Line one", "Line two"
The reason this happens is because the method signature for #on
is *args, &block
which means all arguments must be in the correct position (this isn't entirely true because some positions are optional, though, but is a decent rule to follow). In your implementation, use of ['Line one', 'Line two']
is ignored because --multiline-opt
is considered a flag since it takes no arguments. This might help:
require "optparse"
parser = OptionParser.new do |opts|
opts.on("-a", %w[One Two]) { |value| puts value }
opts.on("-b OPTION", %w[One Two]) { |value| puts value }
opts.on("-c", "One", "Two") { |value| puts value }
end
parser.parse ["-a"]
parser.parse ["-b", "One"]
parser.parse ["-c"]
parser.parse ["--help"]
# true
# One
# true
# Usage: demo [options]
# -a
# -b OPTION
# -c One
# Two
Notice how both the -a
and -c
options are flags (booleans) but only -c
prints the ancillary text you are looking for. This is because you can have multiple lines of ancillary text as long as they are splatted the end of the argument list. In the case of the -b
option, if you supplied a value other than "One" or "Two", you'd end up with a OptionParser::InvalidArgument
error because the %w[One Two]
array restricts what value is allowed.
💡 If it helps, I detail all of this -- and more -- in this article.
Updated by postmodern (Hal Brodigan) 8 months ago
Ah ha! I suppose also adding an explicit option type class would help differentiate between option's desired value and the description lines?
opts.on('-m', '--multiline-opt', String, 'Line one', 'Line two') do |test|
end
However, if I pass in both a String
class, indicating the option's value type, and follow it with an Array of description lines, the description still gets silently omitted.
opts.on('-m', '--multiline-opt VALUE', String, ['Line one', 'Line two']) do |test|
end
usage: test.rb [options]
-o, --opt [OPT] Line one
-m, --multiline-opt VALUE
-h, --help Prints this help
Updated by bkuhlmann (Brooke Kuhlmann) 8 months ago
Correct. The type
(third position) must be a primitive (or custom type, example: Versionaire).
Keep in mind -- in your first example above -- you must supply a required (or optional) argument for the type to take effect. So what you have in that example is still invalid. To fix it, use: opts.on '-m', '--multiline-opt VALUE', String, 'Line one', 'Line two'
. The VALUE
is important so it can be cast as a String
. Otherwise, it's still a flag (boolean). Actually, in your example, the value of test
will be nil
instead of a boolean.
I'm afraid, in your second example, it's still invalid. 😅 It's best to think of the positional arguments this way:
- Alias (short).
- Alias (long) plus argument (required or optional).
- Type.
- Allowed values.
- Description.
- Ancillary (a.k.a. sub-description).
So in your second example, you've supplied the short alias, long alias (with a required argument), specified the type is a String
, and finally specified the value for VALUE
can only be: "Line one" or "Line two". You need to remove the brackets around ['Line one', 'Line two']
if you want that information to show up as ancillary help text. Otherwise, they work as allowed values (i.e. position four).
Updated by jeremyevans0 (Jeremy Evans) 7 months ago
- Status changed from Open to Rejected