lib/cmdparse.rb in cmdparse-3.0.1 vs lib/cmdparse.rb in cmdparse-3.0.2
- old
+ new
@@ -10,17 +10,46 @@
require 'optparse'
OptionParser::Officious.delete('version')
OptionParser::Officious.delete('help')
+
+# Extension for OptionParser objects to allow access to some internals.
+class OptionParser #:nodoc:
+
+ # Access the option list stack.
+ attr_reader :stack
+
+ # Returns +true+ if at least one local option is defined.
+ #
+ # The zeroth stack element is not respected when doing the query because it contains either the
+ # OptionParser::DefaultList or a CmdParse::MultiList with the global options of the
+ # CmdParse::CommandParser.
+ def options_defined?
+ stack[1..-1].each do |list|
+ list.each_option do |switch|
+ return true if switch.kind_of?(OptionParser::Switch) && (switch.short || switch.long)
+ end
+ end
+ false
+ end
+
+ # Returns +true+ if a banner has been set.
+ def banner?
+ !@banner.nil?
+ end
+
+end
+
+
# Namespace module for cmdparse.
#
# See CmdParse::CommandParser and CmdParse::Command for the two important classes.
module CmdParse
# The version of this cmdparse implemention
- VERSION = '3.0.1'
+ VERSION = '3.0.2'.freeze
# Base class for all cmdparse errors.
class ParseError < StandardError
@@ -74,20 +103,25 @@
# This error is thrown when not enough arguments are provided for the command.
class NotEnoughArgumentsError < ParseError
reason 'Not enough arguments provided, minimum is'
end
+ # This error is thrown when too many arguments are provided for the command.
+ class TooManyArgumentsError < ParseError
+ reason 'Too many arguments provided, maximum is'
+ end
+
# Command Hash - will return partial key matches as well if there is a single non-ambigous
# matching key
class CommandHash < Hash #:nodoc:
def key?(name) #:nodoc:
!self[name].nil?
end
def [](cmd_name) #:nodoc:
- super or begin
+ super || begin
possible = keys.select {|key| key[0, cmd_name.length] == cmd_name }
fetch(possible[0]) if possible.size == 1
end
end
@@ -123,37 +157,10 @@
EOF
end
end
- # Extension for OptionParser objects to allow access to some internals.
- class ::OptionParser #:nodoc:
-
- # Access the option list stack.
- attr_reader :stack
-
- # Returns +true+ if at least one local option is defined.
- #
- # The zeroth stack element is not respected when doing the query because it contains either the
- # OptionParser::DefaultList or a CmdParse::MultiList with the global options of the
- # CmdParse::CommandParser.
- def options_defined?
- stack[1..-1].each do |list|
- list.each_option do |switch|
- return true if ::OptionParser::Switch === switch && (switch.short || switch.long)
- end
- end
- false
- end
-
- # Returns +true+ if a banner has been set.
- def banner?
- !@banner.nil?
- end
-
- end
-
# === Base class for commands
#
# This class implements all needed methods so that it can be used by the CommandParser class.
#
# Commands can either be created by sub-classing or on the fly when using the #add_command method.
@@ -256,12 +263,12 @@
# Sets whether this command can take sub-command.
#
# The argument +val+ needs to be +true+ or +false+.
def takes_commands(val)
- if !val && commands.size > 0
- raise Error, "Can't change value of takes_commands to false because there are already sub-commands"
+ if !val && !commands.empty?
+ raise Error, "Can't change takes_commands to false because there are already sub-commands"
else
@takes_commands = val
end
end
alias takes_commands= takes_commands
@@ -484,11 +491,11 @@
#
# A typical return value would look like the following:
#
# {command | other_command | another_command }
def usage_commands
- (commands.size > 0 ? "{#{commands.keys.sort.join(" | ")}}" : '')
+ (commands.empty? ? '' : "{#{commands.keys.sort.join(" | ")}}")
end
# Returns the formatted short description.
#
# For the output format see #cond_format_help_section
@@ -509,11 +516,11 @@
#
# For the output format see #cond_format_help_section
def help_commands
describe_commands = lambda do |command, level = 0|
command.commands.sort.collect do |name, cmd|
- str = " "*level << name << (name == command.default_command ? " (*)" : '')
+ str = " " * level << name << (name == command.default_command ? " (*)" : '')
str = str.ljust(command_parser.help_desc_indent) << cmd.short_desc.to_s
str = format(str, width: command_parser.help_line_width - command_parser.help_indent,
indent: command_parser.help_desc_indent)
str << "\n" << (cmd.takes_commands? ? describe_commands.call(cmd, level + 1) : "")
end.join('')
@@ -525,11 +532,11 @@
# Returns the formatted arguments of this command.
#
# For the output format see #cond_format_help_section
def help_arguments
desc = @argument_desc.map {|k, v| k.to_s.ljust(command_parser.help_desc_indent) << v.to_s}
- cond_format_help_section('Arguments', desc, condition: @argument_desc.size > 0)
+ cond_format_help_section('Arguments', desc, condition: !@argument_desc.empty?)
end
# Returns the formatted option descriptions for the given OptionParser instance.
#
# The section title needs to be specified with the +title+ argument.
@@ -552,11 +559,11 @@
def on_after_add
end
# For sorting commands by name.
def <=>(other)
- self.name <=> other.name
+ name <=> other.name
end
protected
# Conditionally formats a help section.
@@ -583,11 +590,11 @@
def cond_format_help_section(title, *lines, condition: true, indent: true, preformatted: false)
if condition
out = "#{title}:\n"
lines = lines.flatten.join("\n").split(/\n/)
if preformatted
- lines.map! {|l| ' '*command_parser.help_indent << l} if indent
+ lines.map! {|l| ' ' * command_parser.help_indent << l} if indent
out << lines.join("\n")
else
out << format(lines.join("\n"), indent: (indent ? command_parser.help_indent : 0), indent_first_line: true)
end
out << "\n\n"
@@ -615,15 +622,15 @@
(first_line_pattern = /\A.{1,#{width}}\z|\A.{1,#{width}}[ \n]/m) unless indent_first_line
pattern = first_line_pattern
content.split(/\n\n/).map do |paragraph|
lines = []
- while paragraph.length > 0
+ unless paragraph.empty?
unless (str = paragraph.slice!(pattern).sub(/[ \n]\z/, ''))
str = paragraph.slice!(0, line_length)
end
- lines << (lines.empty? && !indent_first_line ? '' : ' '*indent) + str.gsub(/\n/, ' ')
+ lines << (lines.empty? && !indent_first_line ? '' : ' ' * indent) + str.tr("\n", ' ')
pattern = other_lines_pattern
end
lines.join("\n")
end.join("\n\n")
end
@@ -646,12 +653,12 @@
class HelpCommand < Command
def initialize #:nodoc:
super('help', takes_commands: false)
short_desc('Provide help for individual commands')
- long_desc('This command prints the program help if no arguments are given. If one or ' <<
- 'more command names are given as arguments, these arguments are interpreted ' <<
+ long_desc('This command prints the program help if no arguments are given. If one or ' \
+ 'more command names are given as arguments, these arguments are interpreted ' \
'as a hierachy of commands and the help for the right most command is show.')
argument_desc(COMMAND: 'The name of a command or sub-command')
end
def on_after_add #:nodoc:
@@ -664,11 +671,11 @@
def usage_arguments #:nodoc:
"[COMMAND COMMAND...]"
end
def execute(*args) #:nodoc:
- if args.length > 0
+ if !args.empty?
cmd = command_parser.main_command
arg = args.shift
while !arg.nil? && cmd.commands.key?(arg)
cmd = cmd.commands[arg]
arg = args.shift
@@ -787,12 +794,12 @@
#
# 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.
+ # 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.
def initialize(handle_exceptions: false, takes_commands: true)
@global_options = OptionParser.new
@main_command = Command.new('main', takes_commands: takes_commands)
@@ -866,10 +873,14 @@
@current_command = @current_command.commands[cmd_name]
level += 1
else
original_n = @current_command.arity
n = (original_n < 0 ? -original_n - 1 : original_n)
- raise NotEnoughArgumentsError.new(n) if argv.size < n
+ if argv.size < n
+ raise NotEnoughArgumentsError.new("#{n} - #{@current_command.usage_arguments}")
+ elsif argv.size > n && original_n > 0
+ raise TooManyArgumentsError.new("#{n} - #{@current_command.usage_arguments}")
+ end
argv.slice!(n..-1) unless original_n < 0
@current_command.execute(*argv)
break
end