Class Clio::Commandline
In: lib/clio/commandline.rb
Parent: Object

Commandline

Clio‘s Commandline class is a very versitile command line parser. A Command can be used either declaritively, defining usage and help information upfront; or lazily, whereby information about usage is built-up as the commandline actually gets use in one‘s program; or you can use a mixture of the two.

Underlying Notation

As you might expect the fluent notation can be broken down into block notation.

  cli = Clio::Command.new
  cli.usage do
    option(:verbose, :v) do
      help('verbose output')
    end
    option(:quiet, :q) do
      help('run silently')
      xor(:V)
    end
    command(:document) do
      help('generate documentation')
      option(:output, :o) do
        type('FILE')
        help('output directory')
      end
      argument('files') do
        multiple
      end
    end
  end

Clearly block notation is DRY and easier to read, but fluent notation is important to have because it allows the Commandline object to be passed around as an argument and modified easily.

Method Notation

This notation is very elegant, but slightly more limited in scope. For instance, subcommands that use non-letter characters, such as ’:’, can not be described with this notation.

  cli.usage.document('*files', '--output=FILE -o')
  cli.usage('--verbose -V','--quiet -q')

  cli.usage.help(
    'document'     , 'generate documentation',
    'validate'     , 'run tests or specifications',
    '--verbose'    , 'verbose output',
    '--quiet'      , 'run siltently'
  )

  cli.usage.document.help(
    '--output', 'output directory'
    'file*',    'files to document'
  )

This notation is slightly more limited in scope… so…

  cli.usage.command(:document, '--output=FILE -o', 'files*')

Bracket Shorthand Notation

The core notation can be somewhat verbose. As a further convenience commandline usage can be defined with a brief bracket shorthand. This is especailly useful when the usage is simple and statically defined.

  cli.usage['document']['--output=FILE -o']['FILE*']

Using a little creativity to improve readabilty we can convert the whole example from above using this notation.

  cli.usage['--verbose -V',        'verbose output'       ]    #            ['--quiet -q',          'run silently'         ]    #            ['document',            'generate documention' ]    #            [  '--output=FILE -o',  'output directory'     ]    #            [  'FILE*',             'files to document'    ]

Alternately the help information can be left out and defined in a seprate set of usage calls.

  cli.usage['--verbose -V']['--quiet -q']    #            ['document']['--output=FILE -o']['FILE*']

  cli.usage.help(
    'document'  , 'generate documentation',
    'validate'  , 'run tests or specifications',
    '--verbose' , 'verbose output',
    '--quiet'   , 'run siltently'
  )

  cli.usage['document'].help(
    '--output', 'output directory'
    'FILE',     'files to docment'
  )

A little more verbose, but a bit more intutive.

Combining Notations

Since the various notations all translate to same underlying structures, they can be mixed and matched as suites ones taste. For example we could mix Method Notation and Bracket Notation.

  cli.usage.document['--output=FILE -o']['file*']
  cli.usage['--verbose -V']['--quiet -q']

The important thing to keep in mind when doing this is what is returned by each type of usage call.

Commandline Parsing

With usage in place, call the parse method to process the actual commandline.

  cli.parse

If no command arguments are passed to parse, ARGV is used.

Usage Cache

Lastly, Commandline provides a simple means to cache usage information to a configuration file, which then can be used again the next time the same command is used. This allows Commandline to provide high-performane tab completion.

Methods

[]   argument   arguments   argv_set   cli   command   commands   completion   help   method_missing   new   opt   option   options   parameters   parse   parser   subcommand   switches   to_a   to_s   to_s_help   usage   usage   usage=   valid?  

External Aliases

subcommand -> command
subcommand -> cmd
option -> switch
opt -> swt

Public Class methods

[Source]

# File lib/clio/commandline.rb, line 225
      def argument(*n_type, &block)
        usage.argument(*n_type, &block)
      end

[Source]

# File lib/clio/commandline.rb, line 230
      def help(string=nil)
        usage.help(string)
      end

New Command.

[Source]

# File lib/clio/commandline.rb, line 242
    def initialize(argv=nil, opts={}, &block)
      argv_set(argv || ARGV)
      #if opts[:usage]
      #  @usage = opts[:usage]
      #else
      #  #@usage = load_cache
      #end
      if self.class == Commandline
        @usage = Usage.new
      else
        @usage = self.class.usage #|| Usage.new
      end
      @usage.instance_eval(&block) if block
    end

[Source]

# File lib/clio/commandline.rb, line 219
      def opt(label, help, &block)
        usage.opt(label, help, &block)
      end

[Source]

# File lib/clio/commandline.rb, line 213
      def option(name, *aliases, &block)
        usage.option(name, *aliases, &block)
      end

[Source]

# File lib/clio/commandline.rb, line 206
      def subcommand(name, help=nil, &block)
        usage.subcommand(name, help, &block)
      end

Command usage.

[Source]

# File lib/clio/commandline.rb, line 190
      def usage
        @usage ||= (
          if ancestors[1] < Commandline
            ancestors[1].usage.dup
          else
            Usage.new
          end
        )
      end

[Source]

# File lib/clio/commandline.rb, line 200
      def usage=(u)
        raise ArgumentError unless u <= Usage
        @usage = u
      end

Public Instance methods

[Source]

# File lib/clio/commandline.rb, line 311
    def [](i)
      @cli[i]
    end

[Source]

# File lib/clio/commandline.rb, line 322
    def arguments  ; cli.arguments  ; end

[Source]

# File lib/clio/commandline.rb, line 258
    def argv_set(argv)
      # reset parser
      @parser = nil
      # convert to array if string
      if String===argv
        argv = Shellwords.shellwords(argv)
      end
      # remove anything subsequent to '--'
      if index = argv.index('--')
        argv = argv[0...index]
      end
      @argv = argv
    end

[Source]

# File lib/clio/commandline.rb, line 273
    def cli
      #parse unless @cli
      @cli
    end

[Source]

# File lib/clio/commandline.rb, line 316
    def command    ; cli.command    ; end

[Source]

# File lib/clio/commandline.rb, line 319
    def commands   ; cli.commands   ; end

TODO: adding ’-’ is best idea?

[Source]

# File lib/clio/commandline.rb, line 347
    def completion(argv=nil)
      argv_set(argv) if argv
      @argv << "\t"
      parse
      @argv.pop
      parser.errors[0][1].completion.collect{ |s| s.to_s }
      #@argv.pop if @argv.last == '?'
      #load_cache
      #parse
    end

Method missing provide passive usage and parsing.

TODO: This reparses the commandline after every query.

      Need only parse if usage has change.

[Source]

# File lib/clio/commandline.rb, line 369
    def method_missing(s, *a)
      begin
        s = s.to_s
        case s
        when /[=]$/
          n = s.chomp('=')
          usage.option(n).type(*a)
          parse
          res = @cli.options[n.to_sym]
        when /[!]$/
          n = s.chomp('!')
          cmd = usage.commands[n.to_sym] || usage.command(n, *a)
          res = parse
        when /[?]$/
          n = s.chomp('?')
          u = usage.option(n, *a)
          parse
          res = @cli.options[u.key]
        else
          usage.option(s, *a)
          parse
          res = @cli.options[s.to_sym]
        end
      rescue Usage::ParseError => e
        res = nil
      end
      return res
    end
options()

Alias for switches

Parameters

[Source]

# File lib/clio/commandline.rb, line 332
    def parameters ; cli.parameters ; end

[Source]

# File lib/clio/commandline.rb, line 300
    def parse(argv=nil)
      argv_set(argv) if argv
      @cli = parser.parse
    end

[Source]

# File lib/clio/commandline.rb, line 306
    def parser
      @parser ||= Usage::Parser.new(usage, @argv)
    end

[Source]

# File lib/clio/commandline.rb, line 325
    def switches   ; cli.options    ; end

[Source]

# File lib/clio/commandline.rb, line 335
    def to_a
      cli.to_a
    end

[Source]

# File lib/clio/commandline.rb, line 290
    def to_s
      usage.to_s
    end

[Source]

# File lib/clio/commandline.rb, line 295
    def to_s_help
      usage.to_s_help
    end

def usage(name=nil, &block)

  @usage ||= Usage.new(name)
  @usage.instance_eval(&block) if block
  @usage

end

[Source]

# File lib/clio/commandline.rb, line 285
    def usage
      @usage
    end

Commandline fully valid?

[Source]

# File lib/clio/commandline.rb, line 341
    def valid?
      @cli.valid?
    end

[Validate]