Object
Cri::Command represents a command that can be executed on the commandline. It is also used for the commandline tool itself.
@return [Array<String>] A list of aliases for this command that can be
used to invoke this command
@return [Proc] The block that should be executed when invoking this
command (ignored for commands with subcommands)
@return [Cri::Command, nil] This command’s supercommand, or nil if the
command has no supercommand
Creates a new command using the DSL. If a string is given, the command will be defined using the string; if a block is given, the block will be used instead.
If the block has one parameter, the block will be executed in the same context with the command DSL as its parameter. If the block has no parameters, the block will be executed in the context of the DSL.
@param [String, nil] The string containing the command’s definition
@return [Cri::Command] The newly defined command
# File lib/cri/command.rb, line 95 def self.define(string=nil, filename=nil, &block) dsl = Cri::CommandDSL.new if string args = filename ? [ string, filename ] : [ string ] dsl.instance_eval(*args) elsif [ -1, 0 ].include? block.arity dsl.instance_eval(&block) else block.call(dsl) end dsl.command end
# File lib/cri/command.rb, line 126 def initialize @aliases = Set.new @commands = Set.new @option_definitions = Set.new end
Returns a new command that implements showing help.
@return [Cri::Command] A basic help command
# File lib/cri/command.rb, line 121 def self.new_basic_help filename = File.dirname(__FILE__) + '/commands/basic_help.rb' self.define(File.read(filename)) end
Returns a new command that has support for the `-h`/`--help` option and also has a `help` subcommand. It is intended to be modified (adding name, summary, description, other subcommands, …)
@return [Cri::Command] A basic root command
# File lib/cri/command.rb, line 113 def self.new_basic_root filename = File.dirname(__FILE__) + '/commands/basic_root.rb' self.define(File.read(filename)) end
Compares this command's name to the other given command's name.
# File lib/cri/command.rb, line 394 def <=>(other) self.name <=> other.name end
Adds the given command as a subcommand to the current command.
@param [Cri::Command] command The command to add as a subcommand
@return [void]
# File lib/cri/command.rb, line 163 def add_command(command) @commands << command command.supercommand = self end
Returns the command with the given name. This method will display error messages and exit in case of an error (unknown or ambiguous command).
The name can be a full command name, a partial command name (e.g. “com” for “commit”) or an aliased command name (e.g. “ci” for “commit”).
@param [String] name The full, partial or aliases name of the command
@return [Cri::Command] The command with the given name
# File lib/cri/command.rb, line 218 def command_named(name) commands = commands_named(name) if commands.size < 1 $stderr.puts "#{self.name}: unknown command '#{name}'\n" exit 1 elsif commands.size > 1 $stderr.puts "#{self.name}: '#{name}' is ambiguous:" $stderr.puts " #{commands.map { |c| c.name }.sort.join(' ') }" exit 1 else commands[0] end end
Returns the commands that could be referred to with the given name. If the result contains more than one command, the name is ambiguous.
@param [String] name The full, partial or aliases name of the command
@return [Array<Cri::Command>] A list of commands matching the given name
# File lib/cri/command.rb, line 196 def commands_named(name) # Find by exact name or alias @commands.each do |cmd| found = cmd.name == name || cmd.aliases.include?(name) return [ cmd ] if found end # Find by approximation @commands.select do |cmd| cmd.name[0, name.length] == name end end
Defines a new subcommand for the current command using the DSL.
@param [String, nil] name The name of the subcommand, or nil if no name
should be set (yet)
@return [Cri::Command] The subcommand
# File lib/cri/command.rb, line 174 def define_command(name=nil, &block) # Execute DSL dsl = Cri::CommandDSL.new dsl.name name unless name.nil? if [ -1, 0 ].include? block.arity dsl.instance_eval(&block) else block.call(dsl) end # Create command cmd = dsl.command self.add_command(cmd) cmd end
@return [Hash] The option definitions for the command itself and all its
ancestors
# File lib/cri/command.rb, line 151 def global_option_definitions res = Set.new res.merge(option_definitions) res.merge(supercommand.global_option_definitions) if supercommand res end
@return [String] The help text for this command
# File lib/cri/command.rb, line 299 def help(params={}) is_verbose = params.fetch(:verbose, false) text = '' # Append name and summary if summary text << "name".formatted_as_title << "\n" text << " #{name.formatted_as_command} - #{summary}" << "\n" unless aliases.empty? text << " aliases: " << aliases.map { |a| a.formatted_as_command }.join(' ') << "\n" end end # Append usage if usage path = [ self.supercommand ] path.unshift(path[0].supercommand) until path[0].nil? formatted_usage = usage.gsub(/^([^\s]+)/) { |m| m.formatted_as_command } full_usage = path[1..-1].map { |c| c.name.formatted_as_command + ' ' }.join + formatted_usage text << "\n" text << "usage".formatted_as_title << "\n" text << full_usage.wrap_and_indent(78, 4) << "\n" end # Append long description if description text << "\n" text << "description".formatted_as_title << "\n" text << description.wrap_and_indent(78, 4) + "\n" end # Append subcommands unless self.subcommands.empty? text << "\n" text << (self.supercommand ? 'subcommands' : 'commands').formatted_as_title text << "\n" visible_cmds, invisible_cmds = self.subcommands.partition { |c| !c.hidden? } commands_for_length = is_verbose ? self.subcommands : visible_cmds length = commands_for_length.map { |c| c.name.formatted_as_command.size }.max # Visible visible_cmds.sort_by { |cmd| cmd.name }.each do |cmd| text << sprintf(" %-#{length+4}s %s\n", cmd.name.formatted_as_command, cmd.summary) end # Invisible if is_verbose invisible_cmds.sort_by { |cmd| cmd.name }.each do |cmd| text << sprintf(" %-#{length+4}s %s\n", cmd.name.formatted_as_command, cmd.summary) end else case invisible_cmds.size when 0 when 1 text << " (1 hidden command ommitted; show it with --verbose)\n" else text << " (#{invisible_cmds.size} hidden commands ommitted; show them with --verbose)\n" end end end # Append options groups = { 'options' => self.option_definitions } if self.supercommand groups["options for #{self.supercommand.name}"] = self.supercommand.global_option_definitions end length = groups.values.inject(&:+).map { |o| o[:long].size }.max groups.each_pair do |name, defs| unless defs.empty? text << "\n" text << "#{name}".formatted_as_title text << "\n" defs.sort { |x,y| x[:long] <=> y[:long] }.each do |opt_def| text << sprintf( " -%1s --%-#{length+4}s", opt_def[:short], opt_def[:long]).formatted_as_option text << opt_def[:desc] << "\n" end end end text end
Modifies the command using the DSL.
If the block has one parameter, the block will be executed in the same context with the command DSL as its parameter. If the block has no parameters, the block will be executed in the context of the DSL.
@return [Cri::Command] The command itself
# File lib/cri/command.rb, line 139 def modify(&block) dsl = Cri::CommandDSL.new(self) if [ -1, 0 ].include? block.arity dsl.instance_eval(&block) else block.call(dsl) end self end
Runs the command with the given commandline arguments, possibly invoking subcommands and passing on the options and arguments.
@param [Array<String>] opts_and_args A list of unparsed arguments
@param [Hash] parent_opts A hash of options already handled by the
supercommand
@return [void]
# File lib/cri/command.rb, line 242 def run(opts_and_args, parent_opts={}) # Parse up to command name stuff = partition(opts_and_args) opts_before_subcmd, subcmd_name, opts_and_args_after_subcmd = *stuff if subcommands.empty? || (subcmd_name.nil? && !self.block.nil?) run_this(opts_and_args, parent_opts) else # Handle options self.handle_options(opts_before_subcmd) # Get command if subcmd_name.nil? $stderr.puts "#{name}: no command given" exit 1 end subcommand = self.command_named(subcmd_name) # Run subcommand.run(opts_and_args_after_subcmd, opts_before_subcmd) end end
Runs the actual command with the given commandline arguments, not invoking any subcommands. If the command does not have an execution block, an error ir raised.
@param [Array<String>] opts_and_args A list of unparsed arguments
@param [Hash] parent_opts A hash of options already handled by the
supercommand
@raise [NotImplementedError] if the command does not have an execution
block
@return [void]
# File lib/cri/command.rb, line 278 def run_this(opts_and_args, parent_opts={}) # Parse parser = Cri::OptionParser.new( opts_and_args, self.global_option_definitions) self.handle_parser_errors_while { parser.run } local_opts = parser.options global_opts = parent_opts.merge(parser.options) args = parser.arguments # Handle options self.handle_options(local_opts) # Execute if self.block.nil? raise NotImplementedError, "No implementation available for '#{self.name}'" end self.block.call(global_opts, args, self) end
# File lib/cri/command.rb, line 400 def handle_options(opts) opts.each_pair do |key, value| opt_def = global_option_definitions.find { |o| o[:long] == key.to_s } block = opt_def[:block] block.call(value, self) if block end end
# File lib/cri/command.rb, line 424 def handle_parser_errors_while(&block) begin block.call rescue Cri::OptionParser::IllegalOptionError => e $stderr.puts "#{name}: illegal option -- #{e}" exit 1 rescue Cri::OptionParser::OptionRequiresAnArgumentError => e $stderr.puts "#{name}: option requires an argument -- #{e}" exit 1 end end
# File lib/cri/command.rb, line 408 def partition(opts_and_args) # Parse delegate = Cri::Command::OptionParserPartitioningDelegate.new parser = Cri::OptionParser.new(opts_and_args, global_option_definitions) parser.delegate = delegate self.handle_parser_errors_while { parser.run } parser # Extract [ parser.options, delegate.last_argument, parser.unprocessed_arguments_and_options ] end
Generated with the Darkfish Rdoc Generator 2.