Search code examples
rubycommand-lineoptparse

How can I configure optparse to accept both parameter options as a command?


I'm using Ruby's optparse library to parse the options for my command line app but I can't figure out how to also accept a command.

It would be something like this:

commit -f -d init

init would be the command in this case. It isn't always required, as there is a default command that should be run if the user hasn't provided any.

Here's the code I have as of now:

OptionParser.new do |opts|
  opts.banner  = %Q!Usage:
  pivotal_commit                                          # to commit with a currently started issue
  pivotal_commit -f                                       # to commit with a currently started issue and finish it
  pivotal_commit -d                                       # to commit with a currently started issue and deliver it
  pivotal_commit init -e "me@gmail.com" -p my_password -l #to generate a config file at the current directory!

  opts.on("-e", "--email [EMAIL]", String, "The email to the PT account you want to access") do |v|
    options[:email] = v
  end

  opts.on("-p", "--password [PASSWORD]", String, "The password to the PT account you want to access") do |v|
    options[:password] = v
  end

  opts.on("-f", '--finish', 'Finish the story you were currently working on after commit') do |v|
    options[:finish] = v
  end

  opts.on('-d', '--deliver', 'Deliver the story you were currently working on after commit') do |v|
    options[:deliver] = v
  end

  opts.on_tail('-h', '--help', 'Show this message') do
    puts opts
    exit
  end

  opts.on_tail('-v', '--version', 'Show version') do
    puts "pivotal_committer version: #{PivotalCommitter::VERSION}"
    exit
  end

end.parse!

Solution

  • Command-line arguments(not options) are in ARGV after calling OptionParser#parse! because #parse! extracts options from ARGV. So, you can get subcommand like this:

    options = {}
    
    OptionParser.new do |opts|
    # definitions of command-line options...
    # ...
    end.parse!
    
    subcommand = ARGV.shift || "init"
    
    print "options: "
    p options
    puts "subcommand: #{subcommand}"
    

    If you have many subcommands, Thor gem might help you.

    And, although this is not answer for your question, brackets([]) in option definition mean that the option's argument is optional. For instance, at your definitions, email and password might be nil even when the options are passed:

    $ pivotal_commit -e
    options: {:email=>nil}
    subcommand: init
    

    If you require argument when the option is passed, remove brackets:

    # ...
      opts.on("-e", "--email EMAIL", String, "The email to the PT account you want to access") do |v|
        options[:email] = v
      end
    # ...
    

    Now argument for email is required:

    $ pivotal_commit -e
    pivotal_commit:6:in `<main>': missing argument: -e (OptionParser::MissingArgument)