Module: Como

Defined in:
lib/como.rb,
lib/version.rb

Overview

Como

Introduction

Como provides low manifest command line option parsing and deployment. The command line options are described in compact table format and option values are stored to conveniently named properties. Como builds command usage information based on the option table (+ generic program info) and displays it automatically if necessary. Como supports also subcommands and checking for option combinations using a simple DSL.

Usage Examples

Two simple examples are presented in this section. First one includes a straight forward command definition and the second is a bit more complicated with subcommand feature in use.

Simple example

Below is a small example program (“como_simple”) that demonstrates typical usage.

Program listing

require "como"
include Como

# Define command line arguments:
Spec.command( "como_simple", "Programmer", "2013",
  [
    [ :single, "file",  "-f", "File argument." ],
    [ :switch, "debug", "-d", "Enable debugging." ],
  ] )

puts "  File option: #{Opt['file'].value}"
puts "  Debugging selected!" if Opt['debug'].given

First Como is required and Como module is included.

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:

:single

Single means that the option requires one argument (and only one).

:switch

Switch is an optional flag (default value is false).

Option name is used to reference the option value that user has given. The command line option values are stored automatically. For example the file option value is returned by:

Opt['file'].value

The option name also doubles as long option format, i.e. one could use “–file <filename>” on the command line.

Existence of optional options can be tested using the Opt#given method. For example

Opt['debug'].given

would return “true” if “-d” was given on the command line.

Mnemonic is the short form option specification e.g. “-f”. If short form is replaced with “nil”, the long option format is only available.

Doc includes documentation for the option. It is displayed when “help” (“-h”) option is given. Help option is added to the command automatically as default behavior.

Simple example executions

Normal behavior would be achieved by executing:

shell> como_simple -f example -d

The program would execute with the following output:

File option: example
Debugging selected!

Same output would be achieved with:

shell> como_simple --file example --debug

Since option name doubles as long option.

Como includes certain “extra” behavior out-of-box. Required arguments are checked for existence and error is displayed if arguments are not given.

For example given the command:

shell> como_simple

The following is displayed on the screen:

como_simple error: Option "-f" missing for "como_simple"...

  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.

Given the command:

shell> como_simple -h

would display the same “usage” screen except without the error line.

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

    command( "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." ],
      ] )

    check 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

Spec.program method defines a program (command) with subcommands. The author and date are provided as parameters, and the program and subcommand options are defined in block.

The first Spec#command (or Spec#subcmd) method call defines the main command (Opt.main) which represents the program. It has two “subcmd” options (“add” and “rm”).

The rest of the “subcmd” methods define subcommands for the parent command. This example includes one subcommand level, but multiple levels are allowed.

The “check” (or “checkRule”) method defines option combination (RuleCheck) for the previous subcommand definition. In this case the definition allows “add” to have either the “-fo” option defined or “password” and “username” in combination.

Main (root) commands can be referenced through

Opt.main

or alternatively

Opt['como_subcmd']

The subcommands can be referenced through Opt.main (etc.)

Opt.main['add']
Opt['como_subcmd']['add']

or directly from Opt if subcommand names do not collide:

Opt['add']

The given subcommand can be accessed with Opt#givenSubcmd method from each parent command.

Subcommand example executions

Normal behavior would be achieved by executing:

shell> como_subcmd add -fo -f example

The program would execute with the following output:

Adding file "example"...

The option combinations for “add” subcommand are automatically 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”.

Help is automatically provided on each command level, thus these are both valid.

shell> como_subcmd -h

and

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.

  • Set the options given/non-given state.

  • Set the options value. Array/String for all except true/false for switches.

  • Generate Usage Help printout.

Option types

The following types can be defined for the command line options:

:subcmd

Subcmd option. Subcmd specific options are provided separately.

:switch

Single switch option (no arguments).

:single

Mandatory single argument option.

:multi

Mandatory multiple argument option (one or many). Option values in array.

:opt_single

Optional single argument option. Value is nil when option is not given.

:opt_multi

Optional multiple argument option (one or many). Option values in array.

:opt_any

Optional multiple argument option (also none 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 co-exist with other options. :exclusive can have arguments as with :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.

“name” field is also mandatory for all options. “mnemonic” can be left out, but then option accepts only long option format.

“:default” uses only “doc” and “:subcmd” doesn't use the “mnemonic” field.

“:multi”, “:opt_multi”, and “:opt_any” option arguments are terminated only when an option specifier is found. This can be a problem if “:default” option follows. The recommended solution is to 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 passed as the last regular parameter for both Spec.command and Spec.program methods. Setting the configuration at Spec.program will propagate the config options to all the subcommands as well. Configuration can be given to each subcommand separately to override the inherited config values. Subcommand settings are not inherited, but apply only in the subcommand.

The usable configuration Hash keys:

:autohelp

Add help option automatically (default: true). Custom help option can be provided and it can be made also visible to user.

:rulehelp

Include RuleCheck help to Usage Help (default: false).

: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).

:copyright

Display copyright/author in usage printout (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

The Opt#given method takes optionally a block argument. When block argument is used, the block is supplied with option value and the block is executed if the option has been set (See: Opt#given).

Provided value is returned by:

Opt['name'].value

For “:switch” type it is true/false value and for the other types a String or an Array of Strings.

If an option takes multiple arguments, the value for the option is an Array. The values can be iterated simply by:

Opt['files'].value.each do |val|
    puts val
end

Short syntax for value referencing is performed with unary operator “~”. Thus

~Opt['files']

is equal to

Opt['files'].value

With “:opt_any” type, the user should first check if the option was given:

Opt['many_files_or_none'].given

Then check how many arguments where given:

Opt['many_files_or_none'].value.length

And finally decide what to do.

Options including parameters

Sometimes it is convenient for the program to use an option to include multiple parameter settings. These settings can be parsed and mapped to a Hash. Como performs automatic conversion to numeric values if possible. For example with option:

--set rounds=10 length=5

Como can be used extract the parameter values with the “params” method:

Opt['set'].params

And a Hash is returned:

{ 'rounds' => 10, 'length' => 5 }

Subcommand options

The given subcommand for the parent command is return by Opt#givenSubcmd. Commonly the program creator should just check directly which subcommand has been selected and check for any subcommand options set. For example:

if Opt['como_subcmd']['add'].given
   ...

Program external options

If the user gives the “–” option (double-dash), the arguments after that option are returned as an Array with Opt.external.

Option combination checks

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.

inv

Logical negation for existence.

incr

Incremental options in order i.e. have to have previous to have later.

follow

Incremental options in order i.e. have to have all later if had first.

meh

Dont care, always succeeds.

Examples can be found above.

Customization

A Como user specific customization file can be referenced through the “COMO” environment variable. If environment variable “COMO” is defined, the referenced file is read in as Ruby file as a last phase when Como is loaded from the program (require). Proposed naming convention for the customization is:

$HOME/.como

User can define a pre and a post action hook in the file.

The pre-hook can be used for example to change the Como config defaults. It is run before the body of Spec.command or Spec.program is executed. It is passed all the parameters that has been passed to Spec.command or Spec.program, only collected into a Hash. The Hash keys are method parameter names as symbols.

Example:

# Define pre parser hook for Como.
Como.preHook do |args|

    # Get default config for options.
    config = Como::Opt.configGet

    # Disable 'copyright' lines from usage.
    config[ :copyright ] = false

    # Test if "Spec.command" is the entry method (it has arg named "prog").
    if args[ :prog ]
        # Place a custom header for all programs.
        config[ :header ] = "\nProgram \"#{args[ :prog ]}\" is ...\n\n"
    end
end

There is no predefined use cases for post-hook. Post-hook is passed the Opt.main as parameter.

Spec.program and Spec.command both process and check options in one pass. Como user can separate the definition and checking phase. Definition phase is performed by executing Spec.defineProgram or Spec.defineCommand. After definition phase the user can for example programmatically add new subcommands or options, in addition to existing options. When the subcommands and options are complete, Spec.execute should be called to perform options checking.

If the provided customization facilities are not satisfactory, changes can be implemented simply by overloading the existing functions. Some knowledge of the internal workings of Como is required though.

Como version is returned with:

Como.version

Defined Under Namespace

Classes: ArgsParseState, ComoCommon, MainOpt, Opt, RuleCheck, RuleDisplay, Spec

Constant Summary

VERSION =
"0.2.1"

Class Method Summary (collapse)

Class Method Details

+ (Object) postHook(&code)

Set “postHook” routine.



554
555
556
# File 'lib/como.rb', line 554

def Como.postHook( &code )
    ComoCommon.setHook( :postHook, &code )
end

+ (Object) preHook(&code)

Set “preHook” routine.



548
549
550
# File 'lib/como.rb', line 548

def Como.preHook( &code )
    ComoCommon.setHook( :preHook, &code )
end

+ (Object) version



3
4
5
# File 'lib/version.rb', line 3

def Como.version
    Como::VERSION
end