lib/como.rb in como-0.1.7 vs lib/como.rb in como-0.2.0

- old
+ new

@@ -272,15 +272,13 @@ # 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 co-exist 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. +# :opt_any. +# [:silent] Switch option that is not displayed as an option in Usage +# Help display. # # Options use typically all the 4 option fields: # [ type, name, mnemonic, doc ] # # "type" field is mandatory for all options. @@ -297,10 +295,47 @@ # use a ":silent" option that can be used to terminate the argument # list. For example: # [ :silent, "terminator", "-", "The terminator." ], # # +# == Option type primitives +# +# Como converts option types into option type primitives. Option types +# are not completely orthogonal, but primitives are. +# +# Primitives: +# +# [:none] No arguments (i.e. switch). +# [:one] One argument. +# [:many] More than one argument. +# [:opt] Optional argument(s). +# [:default] Default option. +# [:mutex] Mutually exclusive option. +# [:hidden] Hidden option (no usage doc). +# +# Types to primitives mapping: +# +# [:switch] :none, :opt +# [:single] :one +# [:multi] :one, :many +# [:opt_single] :one, :opt +# [:opt_multi] :one, :many, :opt +# [:opt_any] :none, :one, :many, :opt +# [:default] :none, :one, :many, :opt, :default +# [:exclusive] :none, :one, :many, :opt, :mutex +# [:silent] :none, :opt, :hidden +# +# Primitives can be used in place of types if exotic options are +# needed. Instead of a single Symbol an Array of primitives are given +# for option type. Order of primitives is not significant. +# +# For example: +# [ [ :none, :hidden, :opt ], "terminator", "-", "The terminator." ], +# +# Como does not check the primitive combinations, thus care and +# consideration should be applied. +# # == Option specification method configuration # # Option behavior can be controlled with several configuration options. # # The configuration options are provided in a Hash. These are the @@ -469,11 +504,10 @@ # Como version is returned with: # Como.version module Como - # IO stream options for Como classes. class ComoCommon # Default value for display output. @@io = STDOUT @@ -730,29 +764,85 @@ Opt.subcmd( opt_or_sub[1], opt_or_sub[3] ) else - case opt_or_sub[0] + types = Spec.mapTypeToPrims( opt_or_sub[0] ) - when :switch, :exclusive, :silent, :single, :multi, - :opt_single, :opt_multi, :opt_any - Opt.full( opt_or_sub[1], opt_or_sub[2], opt_or_sub[0], opt_or_sub[3] ) - - when :default + if types.index( :default ) Opt.defaultOpt( opt_or_sub[3] ) - else - raise "Unknown option type: \"#{opt_or_sub[0]}\"..." - + Opt.full( opt_or_sub[1], + opt_or_sub[2], + Spec.mapTypeToPrims( opt_or_sub[0] ), + opt_or_sub[3] ) end end end + # Option type primitives. + TYPE_PRIMS = [ + # No arguments (i.e. switch). + :none, + # One argument. + :one, + # More than one argument. + :many, + # Optional argument(s). + :opt, + # Default option. + :default, + # Mutually exclusive option. + :mutex, + # Hidden option (no usage doc). + :hidden + ] + + + # Convert option types (type definitions) to option type + # primitives. + def Spec.mapTypeToPrims( type ) + + prims = nil + + if type.kind_of? Symbol + case type + when :switch; prims = [ :none, :opt ] + when :single; prims = [ :one ] + when :multi; prims = [ :one, :many ] + when :opt_single; prims = [ :one, :opt ] + when :opt_multi; prims = [ :one, :many, :opt ] + when :opt_any; prims = [ :none, :one, :many, :opt ] + when :default; prims = [ :none, :one, :many, :default, :opt ] + when :exclusive; prims = [ :none, :one, :many, :opt, :mutex ] + when :silent; prims = [ :none, :opt, :hidden ] + else + raise "Unknown option type: \"#{type}\"..." + end + + elsif type.kind_of? Array + prims = [] + + # Check that type primivives are valid before taking + # them into use. + type.each do |t| + if TYPE_PRIMS.index( t ) + prims.push t + else + raise "Unknown option type primitive: \"#{t}\"..." + end + end + else + raise "Invalid option type definition: \"#{type}\"..." + end + + prims + end + # Command line options source. @@argv = ARGV # Set command line options source, i.e. @@argv (default: ARGV). def Spec.setArgv( newArgv ) @@ -1002,11 +1092,11 @@ new( name, nil, :subcmd, doc, false ) end # Create default option spec, no switch. def Opt.defaultOpt( doc = "No doc." ) - new( "<default>", "<default>", :default, doc, [] ) + new( "<default>", "<default>", [ :default, :none, :one, :many, :opt ], doc, [] ) end # Options iterator for all options. def Opt.each( &blk ) @@ -1072,11 +1162,11 @@ attr_accessor :shortOpt # Long option string. attr_accessor :longOpt - # Option type. + # Option type as array of primitives (or :subcmd). attr_accessor :type # Option value. attr_accessor :value @@ -1100,32 +1190,27 @@ # Create Opt object. # @param name [String] Option name. # @param opt [String] Switch. - # @param type [Symbol] Option type. One of: - # * :switch - # * :single - # * :multi - # * :opt_single - # * :opt_multi - # * :opt_any - # * :default - # * :exclusive - # * :silent + # @param type [Array<Symbol>, Symbol] Option type in + # primitives (or :subcmd). # @param doc [String] Option documentation. # @param value [Object] Default value. def initialize( name, opt, type, doc, value = nil ) @parent = nil @name = name @shortOpt = opt @longOpt = "--#{name}" @type = type + @value = value - if hasMany && value == nil - @value = [] + if @type != :subcmd + if prim?( :many ) && value == nil + @value = [] + end end @doc = doc # Whether option was set or not. @given = false @@ -1239,11 +1324,11 @@ # Parse cmdline options from args. def parse( args, checkInvalids = true ) while args.get - #puts "Opt.parse (#{@name}): #{args.get}" + # puts "Opt.parse (#{@name}): #{args.get}" if args.isOptTerm # Rest of the args do not belong to this program. args.next @@ -1271,24 +1356,24 @@ o.value.push args.toValue args.next end end - elsif o && o.hasArg + elsif o && ( o.prim?( :one ) || o.prim?( :many ) ) args.next if ( !args.get || args.isOpt ) && - o.type != :opt_any && o.type != :exclusive + !o.prim?( :none ) raise MissingArgument.new( "No argument given for \"#{o.opt}\"...", self ) else - if o.hasMany + if o.prim?( :many ) # Get all argument for multi-option. o.value = [] if !o.given while args.get && !args.isOpt o.value.push args.toValue @@ -1373,20 +1458,20 @@ # Full cmd name. cmd = ( getParents.map do |i| i.name end ).join( ' ' ) # Check for any exclusive args first. @subopt.each do |o| - if o.isExclusive && o.given + if o.prim?( :mutex ) && o.given return end end # Check for required arguments for this level before # subcmds. @subopt.each do |o| - if o.isRequired + if !o.prim?( :opt ) unless o.given raise MissingArgument.new( "Option \"#{o.opt}\" missing for \"#{cmd}\"...", self ) end @@ -1521,11 +1606,11 @@ # Return option value if given otherwise the default. # Example usage: fileName = Opt["file"].apply( "no_name.txt" ) def apply( default = nil ) if given - if @type == :switch + if prim?( :none ) true else value end else @@ -1593,11 +1678,11 @@ # Select option object by name. def argByName( str ) if str == nil || str == :default @subopt.each do |o| - if o.type == :default + if o.prim?( :default ) return o end end nil else @@ -1613,11 +1698,11 @@ # Select option object by name/opt/longOpt. def argById( str ) if str == nil || str == :default @subopt.each do |o| - if o.type == :default + if o.prim?( :default ) return o end end nil else @@ -1629,58 +1714,23 @@ nil end end - # Option requires argument? - def hasArg - case @type - when :single, :multi, :opt_single, :opt_multi, :opt_any, :exclusive; true - else false - end + # Check for primitive. + def prim?( prim ) + @type.index( prim ) end - # Option requires many arguments? - def hasMany - case @type - when :multi, :opt_multi, :opt_any, :exclusive, :default; true - else false - end - end - - - # Is mandatory argument? - def isRequired - case @type - when :single, :multi; true - else false - end - end - - - # Test if option is silent. - def 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 + def hasSwitchStyleDoc + if ( prim?( :none ) && !prim?( :many ) ) || + prim?( :default ) + true + else + false end end # ------------------------------------------------------------ @@ -1770,21 +1820,22 @@ def cmdline opts = [] @subopt.each do |o| - next if o.silent? + next if o.prim?( :hidden ) prural = nil - case o.type - when :multi, :opt_multi; prural = "+" - when :opt_any; prural = "*" - else prural = "" + if o.prim?( :none ) && o.prim?( :many ) + prural = "*" + elsif o.prim?( :one ) && o.prim?( :many ) + prural = "+" + else + prural = "" end - - if !( o.isSwitch ) + if !( o.hasSwitchStyleDoc ) name = " <#{o.name}>#{prural}" else name = "" end @@ -1792,11 +1843,11 @@ opt = o.longOpt else opt = o.shortOpt end - if o.isRequired + if !o.prim?( :opt ) opts.push "#{opt}#{name}" else opts.push "[#{opt}#{name}]" end end @@ -1820,11 +1871,11 @@ str += "\n" str += " Options:\n" if hasSubcmd && hasVisibleOptions @subopt.each do |o| - next if o.silent? + next if o.prim?( :hidden ) str += suboptDocFormat( o.opt, o.doc ) end str += "\n" + suboptDocFormat( "Subcommands:", "" ) if hasSubcmd @@ -1837,11 +1888,11 @@ # Find option object by option str. def findOpt( str ) if str == nil - suball.detect { |i| i.type == :default } + suball.detect { |i| i.prim?( :default ) } elsif str[0..1] == "--" suball.detect { |i| i.longOpt == str } elsif str[0..0] != "-" suball.detect { |i| i.name == str } else @@ -1967,10 +2018,10 @@ # Test if option is of subcmd type. def hasVisibleOptions # Count non-silent options. - ( @subopt.select do |i| !i.silent? end ).length > 0 + ( @subopt.select do |i| !i.prim?( :hidden ) end ).length > 0 end # Test if option is of subcmd type. def hasSubcmd