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
The command that is being executed. Only available during parsing of the command line arguments.
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.
Should exceptions be handled gracefully? I.e. by printing error message and the help screen?
See ::new
for possible values.
The indentation used for, among other things, command descriptions.
The amount of spaces to indent the content of help sections.
The maximum width of the help lines.
The top level command representing the program itself.
Public Class Methods
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 tofalse
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
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
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
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
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