Skip to content

Commands

Oleg Blashchuk edited this page Oct 24, 2020 · 6 revisions

For larger tool-sets it may be preferable to use commands in a manner similar to git: e.g: git --global-option command --command-option1 --command-option2

To do this with Optimist, we can use the stop_on or stop_on_unknown feature to get the global-option and command in one pass. A second pass of Optimist is used to get the command-specific options.

Commentary:

I cannot find much of a difference between stop_on and stop_on_unknown, as they both seem to stop on unknown words.

Example script:

require 'optimist'

class GitStyle
  COMMAND_MAP = {
    'clone' => 'Clone a repository into a new directory',
    'init' => 'Create an empty Git repository or reinitialize an existing one',
  }
  def initialize
    @subopts, @command = nil, nil
    @mainopts = Optimist::options do
      version 'gitstyle.rb v0.0.1'
      banner "Usage:"
      banner "  gitstyle.rb [options] [<command> [suboptions]]\n \n"
      banner "Options:"
      opt :version, "Print version and exit"  ## add this here or it goes to bottom of help
      opt :help, "Show help message"          ## add this here or it goes to bottom of help
      opt :no_pager, "Disable paging"
      stop_on COMMAND_MAP.keys
      #stop_on_unknown
      banner "\nCommands:"
      COMMAND_MAP.each { |cmd, desc| banner format("  %-10s %s", cmd, desc) }
    end
    return if ARGV.empty?
    @command = ARGV.shift
    Optimist.die "unknown subcommand #{@command.inspect}" unless COMMAND_MAP.key? @command
    self.send("opt_#{@command}")  ## dispatch to command handling method
  end
  
  def opt_clone
    cmd = @command
    @subopts = Optimist::options do
      banner "options for #{cmd}"
      opt :depth, "depth", :type => :integer
      opt :bare, "bare mode"
      opt :mirror, "mirror something"
    end
  end

  def opt_init
    cmd = @command
    @subopts = Optimist::options do
      banner "options for #{cmd}"
      opt :quiet, "be quiet"
    end
  end

  attr_reader :mainopts, :command, :subopts
end

optdb = GitStyle.new()
p [:mainopts, optdb.mainopts]
p [:command, optdb.command]
p [:subopts, optdb.subopts]

Sample non-help output:

$ ./gitstyle.rb 
[:mainopts, {:version=>false, :help=>false, :no_pager=>false}]
[:command, nil]
[:subopts, nil]

$ ./gitstyle.rb clone --depth 5
[:mainopts, {:version=>false, :help=>false, :no_pager=>false}]
[:command, "clone"]
[:subopts, {:depth=>5, :bare=>false, :mirror=>false, :help=>false, :depth_given=>true}]

$ ./gitstyle.rb init
[:mainopts, {:version=>false, :help=>false, :no_pager=>false}]
[:command, "init"]
[:subopts, {:quiet=>false, :help=>false}]

Sample help output:

Top-level help message for the tool

$ ./gitstyle.rb -h
Usage:
  git [options] [<command> [suboptions]]
 
Options:
  -v, --version     Print version and exit
  -h, --help        Show help message
  -n, --no-pager    Disable paging

Commands:
  clone      Clone a repository into a new directory
  init       Create an empty Git repository or reinitialize an existing one

Help message for a command

$ ./gitstyle.rb init -h
options for init
  -q, --quiet    be quiet
  -h, --help     Show this message