class CmdParse::CommandParser

Main Class for Creating a Command Based CLI Program

This class can directly be used (or sub-classed, if need be) to create a command based CLI program.

The CLI program itself is represented by the main_command, a Command instance (as are all commands and sub-commands). This main command can either hold sub-commands (the normal use case) which represent the programs top level commands or take no commands in which case it acts similar to a simple OptionParser based program (albeit with better help functionality).

Parsing the command line for commands is done by this class, option parsing is delegated to the battle tested OptionParser of the Ruby standard library.

Usage

After initialization some optional information is expected to be set on the Command#options of the main_command:

banner

A banner that appears in the help output before anything else.

program_name

The name of the program. If not set, this value is computed from $0.

version

The version string of the program.

In addition to the main command's options instance (which represents the top level options that need to be specified before any command name), there is also a global_options instance which represents options that can be specified anywhere on the command line.

Top level commands can be added to the main command by using the add_command method.

Once everything is set up, the parse method is used for parsing the command line.

Attributes

current_command[R]

The command that is being executed. Only available during parsing of the command line arguments.

data[RW]

A data store (initially an empty Hash) that can be used for storing anything. For example, it can be used to store global option values. cmdparse itself doesn't do anything with it.

handle_exceptions[R]

Should exceptions be handled gracefully? I.e. by printing error message and the help screen?

See ::new for possible values.

help_desc_indent[RW]

The indentation used for, among other things, command descriptions.

help_indent[RW]

The amount of spaces to indent the content of help sections.

help_line_width[RW]

The maximum width of the help lines.

main_command[R]

The top level command representing the program itself.

Public Class Methods

new(handle_exceptions: false, takes_commands: true) click to toggle source

Creates a new CommandParser object.

Options:

handle_exceptions

Set to true if exceptions should be handled gracefully by showing the error and a help message, or to false if exception should not be handled at all. If this options is set to :no_help, the exception is handled but no help message is shown.

takes_commands

Specifies whether the main program takes any commands.

    # File lib/cmdparse.rb
807 def initialize(handle_exceptions: false, takes_commands: true)
808   @global_options = OptionParser.new
809   @main_command = Command.new('main', takes_commands: takes_commands)
810   @main_command.super_command = self
811   @main_command.options.stack[0] = MultiList.new(@global_options.stack)
812   @handle_exceptions = handle_exceptions
813   @help_line_width = 80
814   @help_indent = 4
815   @help_desc_indent = 18
816   @data = {}
817 end

Public Instance Methods

add_command(*args, **kws, &block) click to toggle source

Adds a top level command.

See Command#add_command for detailed invocation information.

    # File lib/cmdparse.rb
848 def add_command(*args, **kws, &block)
849   @main_command.add_command(*args, **kws, &block)
850 end
global_options → OptionParser instance click to toggle source
gloabl_options {|opts| ...} → opts (OptionParser instance)

Yields the global options if a block is given and returns them.

The global options are those options that can be used on the top level and with any command.

    # File lib/cmdparse.rb
840 def global_options
841   yield(@global_options) if block_given?
842   @global_options
843 end
main_options → OptionParser instance click to toggle source
main_options {|opts| ...} → opts (OptionParser instance)

Yields the main options (that are only available directly after the program name) if a block is given and returns them.

The main options are also used for setting the program name, version and banner.

    # File lib/cmdparse.rb
827 def main_options
828   yield(@main_command.options) if block_given?
829   @main_command.options
830 end
parse(argv = ARGV) { |level, command_name| ... } click to toggle source

Parses the command line arguments.

If a block is given, the current hierarchy level and the name of the current command is yielded after the option parsing is done but before a command is executed.

    # File lib/cmdparse.rb
856 def parse(argv = ARGV) # :yields: level, command_name
857   level = 0
858   @current_command = @main_command
859 
860   while true
861     argv = if @current_command.takes_commands? || ENV.include?('POSIXLY_CORRECT')
862              @current_command.options.order(argv)
863            else
864              @current_command.options.permute(argv)
865            end
866     yield(level, @current_command.name) if block_given?
867 
868     if @current_command.takes_commands?
869       cmd_name = argv.shift || @current_command.default_command
870 
871       if cmd_name.nil?
872         raise NoCommandGivenError.new
873       elsif !@current_command.commands.key?(cmd_name)
874         raise InvalidCommandError.new(cmd_name)
875       end
876 
877       @current_command = @current_command.commands[cmd_name]
878       level += 1
879     else
880       original_n = @current_command.arity
881       n = (original_n < 0 ? -original_n - 1 : original_n)
882       if argv.size < n
883         raise NotEnoughArgumentsError.new("#{n} - #{@current_command.usage_arguments}")
884       elsif argv.size > n && original_n > 0
885         raise TooManyArgumentsError.new("#{n} - #{@current_command.usage_arguments}")
886       end
887 
888       argv.slice!(n..-1) unless original_n < 0
889       @current_command.execute(*argv)
890       break
891     end
892   end
893 rescue ParseError, OptionParser::ParseError => e
894   raise unless @handle_exceptions
895   puts "Error while parsing command line:\n    " + e.message
896   if @handle_exceptions != :no_help && @main_command.commands.key?('help')
897     puts
898     @main_command.commands['help'].execute(*@current_command.command_chain.map(&:name))
899   end
900   exit(64) # FreeBSD standard exit error for "command was used incorrectly"
901 rescue Interrupt
902   exit(128 + 2)
903 rescue Errno::EPIPE
904   # Behave well when used in a pipe
905 ensure
906   @current_command = nil
907 end