lib/como.rb in como-0.1.1 vs lib/como.rb in como-0.1.2
- old
+ new
@@ -44,11 +44,11 @@
# "Spec.command" method takes 4 arguments:
# [progname] Name of the program (or command).
# [author] Author of the program.
# [year] Year (or any date) for the program.
# [option table] Description of the command options.
-#
+#
# Each option table entry (row/sub-array) includes 4 fields and
# specifies one option:
# [ type, name, mnemonic, doc ]
#
# Two different types are present in the example:
@@ -98,21 +98,21 @@
# shell> como_simple
#
# The following is displayed on the screen:
#
# como_simple error: Option "-f" missing for "como_simple"...
-#
+#
# Usage:
# como_simple -f <file> [-d]
-#
+#
# -f File argument.
# -d Enable debugging.
-#
-#
+#
+#
# Copyright (c) 2013 by Programmer
-#
-#
+#
+#
# Missing option error is displayed since "file" is a mandatory
# option. The error message is followed by "usage" display (Usage
# Help). Documentation string is taken from the option specification to
# "usage" display.
#
@@ -120,49 +120,49 @@
# shell> como_simple -h
#
# would display the same "usage" screen except without the error
# line.
#
-# === Subccommand example
+# === Subcommand example
#
# Subcmd example includes a program which has subcommands. Subcommands
# can have their own command line switches and options.
#
# ==== Program listing
#
# require "como"
# include Como
-#
+#
# Spec.program( "Programmer", "2013" ) do
-#
+#
# subcmd( "como_subcmd", [
# [ :subcmd, "add", nil, "Add file." ],
# [ :subcmd, "rm", nil, "Remove file." ],
# ], )
-#
+#
# subcmd( "add", [
# [ :switch, "force", "-fo", "Force operation." ],
# [ :opt_single, "password", "-p", "User password." ],
# [ :opt_single, "username", "-u", "Username." ],
# [ :single, "file", "-f", "File." ],
# ] )
-#
+#
# checkRule do
# one(
# '-fo',
# all( 'password', 'username' )
# )
# end
-#
+#
# subcmd( "rm", [
# [ :single, "file", "-f", "File." ],
# ] )
-#
+#
# end
-#
+#
# subcmd = Opt.main.givenSubcmd
-#
+#
# case subcmd.name
# when 'add'; puts " Adding file \"#{subcmd['file'].value}\"..."
# when 'rm'; puts " Removing file \"#{subcmd['file'].value}\"..."
# end
#
@@ -210,27 +210,27 @@
# checked. Thus executing:
# shell> como_subcmd add -f example
#
# Would result to:
# como_subcmd error: Option combination mismatch!
-#
+#
# Subcommand "add" usage:
# como_subcmd add [-fo] [-p <password>] [-u <username>] -f <file>
-#
+#
# -fo Force operation.
# -p User password.
# -u Username.
# -f File.
-#
-#
+#
+#
# Option Combinations:
# |--# One of:
# | |--<-fo>
# | |--# All of:
# | | |--<password>
# | | |--<username>
-#
+#
# Since the combination rule requires either "-fo" or both "password"
# and "username" in a pair.
#
# Help is automatically provided on each command level, thus these are
# both valid.
@@ -239,13 +239,13 @@
# shell> como_subcmd rm -h
#
#
#
# == Option specification
-#
+#
# === Overview
-#
+#
# Option specification includes the minimum set of information
# required for command line parsing. It is used to:
# * Parse the command line.
# * Check for wrong options and report.
# * Check for mandatory arguments and report.
@@ -270,11 +270,14 @@
# accepted). Option values in array.
# [:default] Default option (no switch associated). Name and option
# String values can be left out, since only the document
# string is used. Default option is referred with
# ":default" or "nil".
-# [:exclusive] Option that does not coexist with other options.
+# [:exclusive] Option that does not coexist with other
+# options. :exclusive can have arguments as with
+# :opt_any, however :exclusive is documented like
+# :switch.
# [:silent] Option that does not coexist with other options and is not
# displayed as an option in Usage Help display. In effect a
# sub-option of :exclusive.
#
# Options use typically all the 4 option fields:
@@ -295,11 +298,11 @@
# list. For example:
# [ :silent, "terminator", "-", "The terminator." ],
#
#
# === Option specification method configuration
-#
+#
# Option behavior can be controlled with several configuration options.
#
# The configuration options are provided in a Hash. These are the
# passed as the last regular parameter for both "Spec.command" and
# "Spec.program" methods. Setting the configuration at "Spec.program"
@@ -316,18 +319,18 @@
# [:header] Header lines before standard usage printout.
# [:footer] Footer lines after standard usage printout.
# [:subcheck] Automatically check that a subcommand is provided
# (default: true).
# [:check_missing] Check for missing arguments (default: true).
+# [:check_invalid] Error for unknown options (default: true).
# [:tab] Tab stop column for option documentation (default: 12).
# [:help_exit] Exit program if help displayed (default: true).
-# [:error_exit] Exit program if error in options (default: true).
#
#
#
# == Option referencing
-#
+#
# === Existence and values
#
# Opt class includes the parsed option values. All options can be
# tested whether they are specified on the command line using:
# Opt['name'].given
@@ -392,11 +395,11 @@
#
# Como provides a facility to create relations between options using
# RuleCheck DSL. This is needed since sometimes options have to be
# used in combination to make sense for the program. Also options
# might be mutually exclusive.
-#
+#
# The following rules can be used (in combination):
# [all] All options in the list.
# [one] One and only one from the list.
# [any] At least one of the list is given.
# [none] No options are required.
@@ -419,11 +422,11 @@
module Como
# IO stream options for Como classes.
class ComoCommon
-
+
# Default value for display output.
@@io = STDOUT
# Set @@io.
def ComoCommon.setIo( io )
@@ -465,11 +468,11 @@
# @param defs [Array<Array>] Option definitions.
# @param config [Hash] Option definition's behavioral config
# (changes @@config defaults).
def Spec.command( prog, author, year, defs, config = {} )
Spec.defineCheck( prog, author, year, defs, config )
- Spec.usage if Opt['help'].given
+ # Spec.usage if Opt['help'].given
end
# Alias to Spec.command.
def Spec.defineCheckHelp( prog, author, year, defs, config = {} )
Spec.command( prog, author, year, defs, config )
@@ -505,11 +508,11 @@
def subcmd( cmd, defs, config = {} )
unless Opt.main
main = MainOpt.new( @author, @year,
- cmd, nil, :subcmd, nil )
+ cmd, nil, :subcmd, nil )
Opt.setMain( main )
subcmd = main
else
@@ -547,11 +550,11 @@
Spec.ArgCheck( table.class == Array, "Option table is not an Array" )
table.each_index do |idx|
i = table[ idx ]
-
+
Spec.ArgCheck( i.class == Array, "Option table entry is not an Array" )
if i[0] == :default && i.length == 2
# Add 2 dummy entries for :default type if needed.
@@ -561,14 +564,15 @@
# Add 1 dummy entry for :subcmd type if needed.
table[ idx ] = [ i[0], i[1], nil, i[2] ]
end
- Spec.ArgCheck( table[ idx ].length == 4, "Option table entry length not 4" )
+ Spec.ArgCheck( table[ idx ].length == 4,
+ "Option table entry length not 4" )
end
-
+
table.each do |e|
if e[0] == :subcmd
subcmds[ e[1] ] = Opt.subcmd( e[1], e[3] )
@@ -577,35 +581,27 @@
option = nil
case e[0]
- when :switch
- option = Opt.switch( e[1], e[2], e[3] )
-
- when :exclusive
- option = Opt.exclusive( e[1], e[2], e[3] )
-
- when :silent
- option = Opt.exclusive( e[1], e[2], e[3], true )
-
- when :single, :multi, :opt_single, :opt_multi, :opt_any
+ when :switch, :exclusive, :silent, :single, :multi,
+ :opt_single, :opt_multi, :opt_any
option = Opt.full( e[1], e[2], e[0], e[3] )
when :default
option = Opt.defaultOpt( e[3] )
else
raise "Unknown option type: \"#{e[0]}\"..."
end
-
+
options[ option.name ] = option
end
-
+
end
-
+
[ options.values, subcmds.values ]
end
# Command line options source.
@@ -724,19 +720,19 @@
@@subcmd = nil
# Set of default configs for printout.
@@config = {
- :autohelp => true,
- :rulehelp => false,
- :header => nil,
- :footer => nil,
- :subcheck => true,
- :check_missing => true,
- :tab => 12,
- :help_exit => true,
- :error_exit => true,
+ :autohelp => true,
+ :rulehelp => false,
+ :header => nil,
+ :footer => nil,
+ :subcheck => true,
+ :check_missing => true,
+ :check_invalid => true,
+ :tab => 12,
+ :help_exit => true,
}
# Set main option.
def Opt.setMain( main )
@@ -753,17 +749,17 @@
# Add option to options list.
def Opt.addOpt( opt )
@@opts.push opt
end
-
+
# Set current subcmd.
def Opt.setSubcmd( opt )
@@subcmd = opt
end
-
+
# Current subcmd processed.
def Opt.current
@@subcmd
end
@@ -842,37 +838,28 @@
# Create sub-command option spec.
def Opt.subcmd( name, doc = "No doc." )
new( name, nil, :subcmd, doc, false )
end
- # Create switch option spec.
- def Opt.switch( name, opt, doc = "No doc." )
- new( name, opt, :switch, doc, false )
- end
-
- # Create exclusive option spec.
- def Opt.exclusive( name, opt, doc = "No doc.", silent = false )
- o = new( name, opt, :exclusive, doc, false )
- o.silent = silent
- o
- end
-
# Create default option spec, no switch.
def Opt.defaultOpt( doc = "No doc." )
- new( "<default>", "<args>", :default, doc, [] )
+ new( "<default>", "<default>", :default, doc, [] )
end
+
# Options iterator for all options.
def Opt.each( &blk )
Opt.main.each &blk
end
+
# Options iterator for given options.
def Opt.each_given( &blk )
Opt.main.each_given( &blk )
end
+
# Overlay Opt default configuration options.
def Opt.configOverlay( config )
@@config.merge!( config )
end
@@ -887,11 +874,11 @@
# Option name.
attr_accessor :name
# Short option string.
- attr_accessor :opt
+ attr_accessor :shortOpt
# Long option string.
attr_accessor :longOpt
# Option type.
@@ -904,13 +891,10 @@
attr_accessor :doc
# Is option specified?
attr_writer :given
- # Is option hidden (usage).
- attr_accessor :silent
-
# List of suboptions.
attr_reader :subopt
# List of subcommands.
attr_reader :subcmd
@@ -939,16 +923,15 @@
# [value] Default value.
def initialize( name, opt, type, doc, value = nil )
@parent = nil
@name = name
- @opt = opt
+ @shortOpt = opt
@longOpt = "--#{name}"
@type = type
@value = value
@doc = doc
- @silent = false
# Whether option was set or not.
@given = false
@subopt = nil
@subcmd = nil
@rules = nil
@@ -959,11 +942,11 @@
end
# Set subcommand suboptions.
#
- # @param opts [Array<Opt>]
+ # @param opts [Array<Opt>]
def setSubopt( opts, subs )
opts.each do |i|
i.parent = self
end
@@ -1003,29 +986,26 @@
begin
# Parse and check for invalid arguments.
begin
- top = top.parse( argsState, top.config[ :check_missing ] )
+ top = top.parse( argsState, top.config[ :check_invalid ] )
end while( top )
# Check for any missing valid arguments.
checkMissing
rescue Opt::MissingArgument, Opt::InvalidOption => err
- @@io.puts
-
error( err.to_s )
# Display subcmd specific usage info.
err.data.usage
- exit( 1 ) if Opt.main.config[ :error_exit ]
-
+ exit( 1 )
end
-
+
# Revert back to top after hierarchy travelsal.
usageIfHelp
# Check rules.
cur = self
@@ -1059,17 +1039,17 @@
if !o
if checkInvalids
raise \
InvalidOption.new( "Unknown option \"#{args.get}\"...",
- self )
+ self )
else
o = findOpt( nil )
if !o
raise \
InvalidOption.new(
- "No default option specified for \"#{args.get}\"...",
+ "No default option specified to allow \"#{args.get}\"...",
self )
else
# Default option.
o.value.push args.toValue
args.next
@@ -1079,20 +1059,20 @@
elsif o && o.hasArg
args.next
if ( !args.get || args.isOpt ) &&
- o.type != :opt_any
+ o.type != :opt_any && o.type != :exclusive
raise MissingArgument.new(
- "No argument given for \"#{o.opt}\"...",
- self )
+ "No argument given for \"#{o.opt}\"...",
+ self )
else
if o.hasMany
-
+
# Get all argument for multi-option.
o.value = [] if !o.given
while args.get && !args.isOpt
o.value.push args.toValue
args.next
@@ -1103,12 +1083,12 @@
# Get one argument for single-option.
if o.given
raise \
InvalidOption.new(
- "Too many arguments for option (\"#{o.name}\")...",
- self )
+ "Too many arguments for option (\"#{o.name}\")...",
+ self )
else
o.value = args.toValue
end
args.next
end
@@ -1118,13 +1098,12 @@
else
if !o
raise InvalidOption.new( "No valid options specified...",
- self )
+ self )
else
- o.value = !o.value if !o.given
o.given = true
args.next
end
end
@@ -1140,25 +1119,26 @@
# Search for default option.
o = findOpt( nil )
if !o
raise \
InvalidOption.new(
- "No default option specified for \"#{args.get}\"...",
- self )
+ "No default option specified to allow \"#{args.get}\"...",
+ self )
else
# Default option.
o.given = true
o.value.push args.toValue
args.next
end
else
-
+
+ # Subcmd.
o.given = true
args.next
return o
-
+
end
end
end
@@ -1169,16 +1149,18 @@
# Check for any non-given required arguments recursively
# through hierarchy of subcommands. MissingArgument Exception
# is generated if argument is missing.
def checkMissing
+ return unless config[ :check_missing ]
+
# Full cmd name.
cmd = ( getParents.map do |i| i.name end ).join( ' ' )
# Check for any exclusive args first.
@subopt.each do |o|
- if o.type == :exclusive && o.given
+ if o.isExclusive && o.given
return
end
end
@@ -1186,12 +1168,12 @@
# subcmds.
@subopt.each do |o|
if o.isRequired
unless o.given
raise MissingArgument.new(
- "Option \"#{o.opt}\" missing for \"#{cmd}\"...",
- self )
+ "Option \"#{o.opt}\" missing for \"#{cmd}\"...",
+ self )
end
end
end
if hasSubcmd
@@ -1213,13 +1195,13 @@
return sub.checkMissing
end
# If no subcmds are given, issue error.
raise MissingArgument.new(
- "Subcommand required for \"#{cmd}\"...",
- self ) if subcmdMissing
-
+ "Subcommand required for \"#{cmd}\"...",
+ self ) if subcmdMissing
+
end
# Check option combination rules.
@@ -1273,10 +1255,16 @@
end
ret
end
+ # Option's opt id. Short if exists otherwise long.
+ def opt
+ @shortOpt ? @shortOpt : @longOpt
+ end
+
+
# All subcommand options, options and subcommands.
def suball
@subopt + @subcmd
end
@@ -1422,20 +1410,20 @@
# Option requires argument?
def hasArg
case @type
- when :single, :multi, :opt_single, :opt_multi, :opt_any; true
+ when :single, :multi, :opt_single, :opt_multi, :opt_any, :exclusive; true
else false
end
end
# Option requires many arguments?
def hasMany
case @type
- when :multi, :opt_multi, :opt_any; true
+ when :multi, :opt_multi, :opt_any, :exclusive; true
else false
end
end
@@ -1448,14 +1436,24 @@
end
# Test if option is silent.
def silent?
- @silent
+ @type == :silent
end
+ # Test if option is exclusive. In addition :exclusive also
+ # :silent is exclusive.
+ def isExclusive
+ case @type
+ when :exclusive, :silent; true
+ else false
+ end
+ end
+
+
# Test if option is of switch type.
def isSwitch
case @type
when :switch, :exclusive, :default; true
else false
@@ -1485,11 +1483,11 @@
# if nil.
# @param ruleHelp [Boolean] Include rule help to help
# display. Default to rulehelp config
# if nil.
def usage( doExit = nil, ruleHelp = nil )
-
+
doExit = @config[ :help_exit ] if doExit == nil
ruleHelp = @config[ :rulehelp ] if ruleHelp == nil
@@io.puts usageNormal
@@ -1513,22 +1511,22 @@
# Usage printout for command.
def usageCommand
str = ""
- str += "
+ str += "\
Subcommand \"#{@name}\" usage:
#{fullCommand} #{cmdline.join(" ")}
"
str += suboptDoc
str += "\n"
end
# Usage info for Opt:s.
- def usageNormal
+ def usageNormalOld
str = ""
if @config[ :header ]
str += "\n"
str += @config[ :header ]
@@ -1536,18 +1534,39 @@
end
str += usageCommand
if @config[ :footer ]
- str += @config[ :footer ]
+ str += @config[ :footer ]
str += "\n"
end
str
end
+ # Usage info for Opt:s.
+ def usageNormal
+ str = ""
+ if @config[ :header ]
+ str += @config[ :header ]
+ else
+ str += "\n"
+ end
+
+ str += usageCommand
+
+ if @config[ :footer ]
+ str += @config[ :footer ]
+ else
+ str += "\n"
+ end
+
+ str
+ end
+
+
# Return cmdline usage strings for options in an Array.
def cmdline
opts = []
@subopt.each do |o|
@@ -1566,24 +1585,24 @@
name = " <#{o.name}>#{prural}"
else
name = ""
end
- if o.opt == nil
+ if o.shortOpt == nil
opt = o.longOpt
else
- opt = o.opt
+ opt = o.shortOpt
end
-
+
if o.isRequired
opts.push "#{opt}#{name}"
else
opts.push "[#{opt}#{name}]"
end
end
-
+
if hasSubcmd
opts.push "<<subcommand>>"
end
opts
@@ -1595,15 +1614,15 @@
def suboptDoc
str = ""
# format = Proc.new do |s,d| ( " %-#{@config[ :tab ]}s%s\n" % [ s, d ] ) end
- str += suboptDocFormat( "Options:", "" ) if hasSubcmd && hasVisibleOptions
+ str += " Options:\n" if hasSubcmd && hasVisibleOptions
@subopt.each do |o|
next if o.silent?
- str += suboptDocFormat( o.opt ? o.opt : o.longOpt, o.doc )
+ str += suboptDocFormat( o.opt, o.doc )
end
str += "\n" + suboptDocFormat( "Subcommands:", "" ) if hasSubcmd
@subcmd.each do |o|
@@ -1624,20 +1643,20 @@
suball.detect { |i| i.name == str }
else
suball.detect { |i| i.opt == str }
end
end
-
+
# Como error printout.
def error( str )
- @@io.puts "#{Opt.progname} error: #{str}"
+ STDERR.puts "\n#{Opt.progname} error: #{str}"
end
-
+
# ------------------------------------------------------------
# Internal methods:
private
@@ -1703,20 +1722,20 @@
# Format doc nicely to multiple lines if newline is
# followed by tab stop.
parts = doc.split( "\n" )
lines = [ ( " %-#{@config[ :tab ]}s%s\n" % [ switch, parts[0] ] ) ]
-
+
if parts[1]
parts[1..-1].each do |p|
if p[0] == "\t"
lines.push ( " #{' '*@config[ :tab ]}%s\n" % [ p[1..-1] ] )
else
lines.push p
end
-
+
end
end
lines.join
end
@@ -1729,33 +1748,33 @@
# subcommand).
class MainOpt < Opt
# Program external arguments:
attr_accessor :external
-
+
# Program author and year (date).
attr_reader :author, :year
-
+
def initialize( author, year,
name, opt, type, doc, value = nil )
@author = author
@year = year
@external = nil
super( name, opt, type, doc, value = nil )
end
-
+
# Full command name.
def fullCommand
Opt.progname
end
# Usage printout for command.
- def usageCommand
+ def usageCommandOld
str = "
Usage:
#{fullCommand} #{cmdline.join(" ")}
"
@@ -1766,10 +1785,24 @@
Copyright (c) #{Opt.year} by #{Opt.author}
"
end
+ # Usage printout for command.
+ def usageCommand
+ str = "\
+ #{fullCommand} #{cmdline.join(" ")}
+
+"
+ str += suboptDoc
+
+ str += "
+
+ Copyright (c) #{Opt.year} by #{Opt.author}
+"
+ end
+
end
# Command argument parsing state.
class ArgsParseState
@@ -1886,10 +1919,10 @@
end
# Incremental options in order i.e. have to have previous
# to have later.
def incr( *args )
-
+
# Opts given consecutive.
consecutiveCnt = 0
# Consecutive flag.
consecutive = true