commander-rb / commander

The complete solution for Ruby command-line executables
MIT License
821 stars 74 forks source link

Global options not available at command level when placed before command name #32

Closed msabramo closed 4 years ago

msabramo commented 8 years ago

At https://github.com/commander-rb/commander#global-options, it says:

---- begin snip ----

All global options regardless of providing a block are accessable at the command level. This means that instead of the following:

global_option('--verbose') { $verbose = true }
...
c.action do |args, options|
  say 'foo' if $verbose
...

You may:

global_option '--verbose'
...
c.action do |args, options|
  say 'foo' if options.verbose
...

---- end snip ----

but I have been unable to get this to work.

I have:

global_option('-s', '--server SERVER', 'Host name or IP address') do |server|
  $server = server
end
...
command :repo_package_query do |c|
  c.syntax = 'aptly-cli repo_package_query [options]'
  c.summary = 'List all packages in local repository or perform search on repository contents and return result., requires --name'
  c.description = 'List all packages or search on repo contents, requires --name'
  c.example 'description', 'aptly-cli repo_package_query --name megatronsoftware -query geoipupdate'
  c.option '--name NAME', String, 'Local repository name, required'
  c.option '--query QUERY', String, 'Package to query'
  c.option '--with_deps', 'Return results with dependencies'
  c.option '--format FORMAT', String, 'Format type to return, compact by default. "details" is an option'
  c.action do |args, options|
    require 'pry'; binding.pry
...
end

And then I do this:

$ aptly-cli --server 10.3.0.46 repo_package_query --name develop --query 'Name (~ anonweb)'

From: /usr/local/lib/ruby/gems/2.3.0/gems/aptly_cli-0.2.9/bin/aptly-cli @ line 158 :

    153:   c.option '--query QUERY', String, 'Package to query'
    154:   c.option '--with_deps', 'Return results with dependencies'
    155:   c.option '--format FORMAT', String, 'Format type to return, compact by default. "details" is an option'
    156:   c.action do |args, options|
    157:     require 'pry'; binding.pry
 => 158:     config = AptlyCli::AptlyLoad.new.configure_with($config_file)
    159:     aptly_command = AptlyCli::AptlyRepo.new(config)
    160:     if options.query
    161:       repo_options = { :name => options.name.to_s, :query => options.query.to_s }
    162:     elsif options.with_deps and options.query.nil?
    163:       repo_options = { :name => options.name.to_s, :with_deps => options.with_deps.to_s }

[1] pry(main)> options
=> <Commander::Command::Options name="develop", query="Name (~ anonweb)">
[2] pry(main)> $server
=> "10.3.0.46"
[3] pry(main)> options.server
=> nil

Note that $server is set, but options.server is not set. Shouldn't it be? Or am I misunderstanding how it works?

Same behavior if I simply do:

global_option '--server SERVER'

and it also occurs with commands that don't take options of their own:

command :repo_list do |c|
  c.syntax = 'aptly-cli repo_list [options]'
  c.summary = 'Show list of currently available local repositories'
  c.description = 'Show list of currently available local repositories'
  c.example 'description', 'aptly-cli repo_list'
  c.action do |args, options|
    require 'pry'; binding.pry
...
end

Running it:

$ aptly-cli --server 10.3.0.46 repo_list

From: /usr/local/lib/ruby/gems/2.3.0/gems/aptly_cli-0.2.9/bin/aptly-cli @ line 141 :

    136:   c.summary = 'Show list of currently available local repositories'
    137:   c.description = 'Show list of currently available local repositories'
    138:   c.example 'description', 'aptly-cli repo_list'
    139:   c.action do |args, options|
    140:     require 'pry'; binding.pry
 => 141:     config = AptlyCli::AptlyLoad.new.configure_with($config_file)
    142:     aptly_command = AptlyCli::AptlyRepo.new(config, options)
    143:     puts aptly_command.repo_list()
    144:   end
    145: end
    146:

[1] pry(main)> options
=> <Commander::Command::Options >
[2] pry(main)> $server
=> "10.3.0.46"
[3] pry(main)> options.server
=> nil

System info

$ gem list commander

*** LOCAL GEMS ***

commander (4.4.0)

$ ruby -v
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin14]
ggilder commented 8 years ago

Hmm, this appears to be a bug in the order of parsing global options. In your example, I believe this will work:

aptly-cli repo_list --server 10.3.0.46

Note that the --server flag comes after the command name there.

I will update the issue title to reflect this.

erniebrodeur commented 6 years ago

So, any word on a fix?

jeffshantz commented 5 years ago

Related to this, I observe inconsistent behaviour with the global options and whether or not they appear at the command level. For instance, here's a simple command that prints the value of options.__hash__ for the purpose of testing:

c.action do |args, options|
  puts options.__hash__.inspect
end

When I run this with both command-level and global options, I get the following:

# cmd test --trace --first Joe
{:first=>"Joe"}

When I run this with no command-level options, but I keep the global option, I get the following:

# cmd test --trace
{:trace=>true}

This behaviour is the same, even if I put the global option before the command name:

# cmd --trace test --first Joe
{:first=>"Joe"}

# cmd --trace test
{:trace=>true}

In my case, I would certainly appreciate the ability to turn off global options appearing at the command level altogether. I am already handling them at the global level by setting a flag to true when a particular option is specified.

Justin-W commented 5 years ago

I think I may have run into the same problem. Any global_option method call without a block doesn't seem to affect my command's options instance. So I'm currently having to add a block (which does manual value processing) to each global_option call. Rather irritating, and also completely contrary to the official documentation:

see: commander-rb/commander: The complete solution for Ruby command-line executables

... All global options regardless of providing a block are accessable at the command level. ...

Also, note that the buggy behavior appears to be identical regardless of whether I call global_option before vs. after the declaration(s) of my command(s).

binaryape commented 5 years ago

I'm having the same problem, I think - my CI tests broke recently and commenting out a global --verbose option and adding it to each command instead fixed the problem. This seems to imply it wasn't a bug in the past...

ggilder commented 5 years ago

@binaryape any chance you know which version of commander you were using before, where your CI was passing?

binaryape commented 5 years ago

My gem spec specifies "~>4.4""

On my old desktop Mac (where the tests pass) Commander version 4.4.4 is in the Gemfile.lock.

The CI has no state between runs so at the time I was using it for the original dev work it probably also used 4.4.4. I haven't made any changes for a long time so it didn't run again until yesterday.

Yesterday I was starting on a long-overdue patch release on a new laptop. It installed Commander 4.4.7. Tests failed there and on the CI, which probably also used 4.4.7.

The tests that failed check for a string sent to stderr when using verbose mode, to make sure a feature was enabled. Verbose mode was controlled using a global_option '--verbose' before the command blocks. In 4.4.7 verbose mode is not set, so the string wasn't detected, and the tests failed.

ggilder commented 5 years ago

@binaryape interesting, v4.4.4 and v4.4.7 were both released quite a bit after the original issue in this thread so I'm not sure if the bug you're encountering is strictly the same issue.

In any case, would you be able to put together a reduced test case that demonstrates the issue? That would help narrow down what change caused this bug.

tayjaybabee commented 5 years ago

I am developing a CLI program with Commander and I have --verbose specified as a global option but on the command level it's not recognized when passed. It seems that my issue and @binaryape 's issue could be related as they both have to do with global --verbose options

Edit: I should also add that I have other global options specified that work, but verbose does not

binaryape commented 5 years ago

In any case, would you be able to put together a reduced test case that demonstrates the issue?

Yes (hopefully soonish)