davetron5000 / gli

Make awesome command-line applications the easy way
http://davetron5000.github.io/gli
Apache License 2.0
1.26k stars 102 forks source link

Mutually Exclusive Options? #283

Closed IotaSpencer closed 3 years ago

IotaSpencer commented 5 years ago

Would there be an easy way for either me or you and myself to implement mutually exclusive options so that either a default_value: true can be disabled when using an MEO, or another flag parameter that you list the options that are not to be used with it.

davetron5000 commented 5 years ago

Can you show an example of what you mean? I'm not 100% sure I follow.

IotaSpencer commented 5 years ago

https://github.com/skorks/escort/blob/d0c021b92ea41128807a37854ec5486d0aa7150c/lib/escort/option_parser.rb#L69-L79

https://github.com/skorks/escort/blob/master/lib/escort/option_dependency_validator.rb

https://github.com/skorks/escort/blob/master/lib/escort/trollop.rb#L252-L264

And then https://github.com/skorks/escort#option-dependencies from the anchor down

davetron5000 commented 5 years ago

Sorry, I still don't understand. What is the behavior you would like to see? Can you show some command-line invocations of a hypothetical app that had this feature and how that app would behave?

IotaSpencer commented 5 years ago

Well first off, either way, I'd want it to be added onto the flag invocation, but something like this out of my project would qualify as a option I don't want to have to do a --no-* on

$ cloudflare_cli zones list 

outputed is a ascii table (code mostly irrelavent to how options are parsed) (--table is because default_value: true)

$ cloudflare_cli zones --output 'result.name' --output 'result.status' --output-sep newline

which if table was a conflicted option and disabled itself when another conflicting option was used, should only output the following

ken@kenny:~/everygit/cloudflare_cli$ bundle exec bin/cloudflare_cli zones list \
  --output "result.first.name" \
  --output "result.first.status" \
  --output-sep newline
e-code.in
active

If there were a 'depends_on' then I could make --output-sep require at least one --output

$ cloudflare_cli --output-sep newline
error: flag 'output-sep' requires the option 'output' to be used (in tandem or similar word)

Then what was actually used (--table put in asterisks for clarity)

bundle exec bin/cloudflare_cli zones list \
  **--no-table** \
    --output 'result.name' \
    --output 'result.status' \
    --output-sep newline
IotaSpencer commented 5 years ago

What actually gets outputted is then

ken@kenny:~/everygit/cloudflare_cli$ bundle exec bin/cloudflare_cli zones list --output "result.first.name" --output "result.first.status" --output-sep newline
+-----------------------+------------------------------------------------------------------+
|                                          Zones                                           |
+-----------------------+------------------------------------------------------------------+
|                                        e-code.in                                         |
+-----------------------+------------------------------------------------------------------+
.
. table contents
.
+-----------------------+------------------------------------------------------------------+
|                                     electrocode.net                                      |
+-----------------------+------------------------------------------------------------------+
.
. table contents
.
+-----------------------+------------------------------------------------------------------+
|                                      iotaspencer.me                                      |
+-----------------------+------------------------------------------------------------------+
.
. table contents
.
+-----------------------+------------------------------------------------------------------+
| success               | true                                                             |
| errors                | none                                                             |
| messages              | none                                                             |
+-----------------------+------------------------------------------------------------------+
e-code.in
active
davetron5000 commented 5 years ago

OK, so it sounds like you are wanting a few things:

Much of GLI's option parsing is deferred to the standard Ruby OptionParser, so to implement something like this, we'd need to:

GLI has the concept of a pre hook that is given all the parsed options and can decide to execute the command or not, based on what's in there. That's how you could implement this in your app directly, but I would think to make this a GLI feature, we could have some sort of built-in pre blocks to handle these checks.

IotaSpencer commented 5 years ago

Yes, and I can show you what I'm currently having to do to check all this, though I have made the default_value for table currently false so I don't have to deal with that as much

IotaSpencer commented 5 years ago
          if options[:json] and options[:table] and options[:output].empty?
            puts js.to_json
          elsif options[:table] and not options[:json] and options[:output].empty?
            table = CloudflareCli::Methods::Tables::Memberships.new('Memberships', js)
            table.make_it_so
            table.beam_me_out
          elsif not options[:output].empty? and options[:'output-sep'] and not options[:table] and not options[:json]
            want = CloudflareCli::Methods::Output.new(options[:output], options, js)
            want.put
          else
            if options[:json] and options[:table]
              raise FlagSwitchConflictError.new('json', 'table')
            end
            if options[:output].empty? and options[:'output-sep']
              raise FlagSwitchDependencyError.new('option-sep', 'output')
            end
          end

This being from here as you can see its a bit cumbersome and hopefully I can do something like

        lm.flag :'output-sep',
                   desc: 'choose character to separate output fields',
                   must_match: [:newline, :crlf, :space],
                   required: false,
                   default_value: :space,
                   depends_on: [:output]
        lm.flag :output,
                     desc: 'output fields, use multiple times to output multiple fields',
                     multiple: true,
                     conflicts_with: [:table, :json]
        lm.switch :table,
                         desc: 'Put some of the output into a table',
                         default_value: false,
                         conflicts_with: [:output, :json]
        lm.switch :json,
                         desc: 'output raw json',
                         default_value: false,
                         conflicts_with: [:table, :output]

considering output is a multiple value and does exist in the options no matter what, unlike something being nil or not in the hash, this may cause some problems, do to most likely having to check if --output was given rather than just being empty

IotaSpencer commented 5 years ago

Well easy stuff first, help output:


ken@kenny:~/everygit/cloudflare_cli$ bundle exec bin/cloudflare_cli help memberships list
NAME
    list - list memberships

SYNOPSIS
    cloudflare_cli [global options] memberships list [command options] 

COMMAND OPTIONS
    --direction, --dir=arg - direction to order results (default: asc)
    --[no-]json            - output raw json (conflicts with: --table, --output) # <---
    --order=arg            - field to order memberships by (default: none)
    --output=arg           - output fields, use multiple times to output multiple fields (may be used more than once, default: none, conflicts with: --table, --json) # <---
    --output-sep=arg       - choose character to separate output fields (default: space, depends on: --output) # <---
    --page=arg             - page to query (default: 1)
    --per_page=arg         - how many accounts listed per page (default: 20)
    --[no-]table           - Put some of the output into a table (conflicts with: --json, --output) # <---
IotaSpencer commented 5 years ago

oddly, I just noticed trollops successor optimist has constraints and dependencies as well

https://github.com/ManageIQ/optimist/blob/master/lib/optimist.rb

https://github.com/ManageIQ/optimist/blob/master/lib/optimist.rb#L233-L341

IotaSpencer commented 5 years ago

something simple like this would also work

https://github.com/ManageIQ/optimist/wiki/Medium-Example#dependencies-and-conflicts

davetron5000 commented 3 years ago

Closing as old. Given that it can work with pre I hope that is working for you, but please re-open if not