Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Option argument parsed as a global option #80

Open
jeffshantz opened this issue May 13, 2019 · 4 comments
Open

Option argument parsed as a global option #80

jeffshantz opened this issue May 13, 2019 · 4 comments

Comments

@jeffshantz
Copy link

jeffshantz commented May 13, 2019

I'm using Commander 4.4.7. I am noticing that if I have an option that takes an argument, and the argument starts with a global option, the argument is parsed as a global option.

require 'commander/import'

program :name, 'Name'
program :version, '0.1'
program :description, 'Issue with argument parsing.'

default_command :mount

command :mount do |c|
  c.option('--options=STRING', String) do |opts|
    puts "Parsed mount options: #{opts}"
  end
  c.action do
    puts 'Success'
  end
end

When I run the program, it interprets -vers=3.0 as the global option -v:

$ ruby test.rb --options "-vers=3.0"
Name 0.1

If I use = to separate --options from its argument, it works as expected:

$ ruby test.rb --options="-vers=3.0"
Parsed mount options: -vers=3.0
Success

So, we have a workaround, but I still argue that it's a bug. First, it's very common to omit the =.
Second, it works in either case using optparse directly. Here is an example:

require 'optparse'

OptionParser.new do |opts|
  opts.banner = "Usage: example.rb [options]"

  opts.on("-v", "--version", "Show version") do |v|
    puts "-v received"
  end

  opts.on("-o OPTIONS", "--options OPTIONS", "Provide mount options") do |opts|
    puts "-o received with #{opts}"
  end

end.parse!

Observe that it works as expected in either case:

$ ruby test2.rb --options "-vers=3.0"
-o received with -vers=3.0

$ ruby test2.rb --options="-vers=3.0"
-o received with -vers=3.0
@ggilder
Copy link
Member

ggilder commented Jan 21, 2020

Hmm, I'm not sure this is a bug so much as undefined behavior. From the shell's perspective, the invocation you mention is no different from the following:

$ ruby test.rb --options -vers=3.0

In other words, the quotes don't matter because they're interpreted away by the shell. That might clarify why in this situation, commander sees a global option.

I'm not totally sure what the correct behavior is here, but it is interesting that optparse behaves differently. However, since optparse doesn't have the same notion of global options it's a little difficult to directly compare.

@jeffshantz
Copy link
Author

I understand that the quotes are removed by the shell, but I would argue that the parser should understand that --options takes an argument and therefore the next argument would be the argument to --options and not an option of its own.

@orien
Copy link
Contributor

orien commented Mar 9, 2020

Looks like the problem is in the parsing of global options. In particular, the parse, rescue, remove and retry portion:

begin
parser.parse!(options)
rescue OptionParser::InvalidOption => e
# Remove the offending args and retry.
options = options.reject { |o| e.args.include?(o) }
retry
end

In normal usage, it'll raise on the command specific options, which will then be removed from the array of options before retrying the parse.

In the scenario detailed in this issue: test.rb --options -vers=3.0. Only the --options token will be removed, as -vers=3.0 satisfies the --version global. Hence in the retry, the options are only -vers=3.0 which triggers the --version behaviour.

@ggilder
Copy link
Member

ggilder commented Mar 9, 2020

Exactly, global options are parsed in a first pass before parsing command options. The OptionParser example provided by @jeffshantz above works because there is only one pass on parsing the options.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants