File: README [cmd-0.7.2 Documentation]

### Home Classes Methods

### Files

- AUTHORS
- CHANGELOG
- INSTALL
- README
- THANKS
- TODO

### Class Index

Quicksearch

- Cmd
- Cmd::ClassMethods
- Object

No matching classes.

# Cmd

## What is Cmd?

cmd is a library for building line-oriented command interpreters in Ruby. Simply inherit from cmd's Cmd class, and methods whose names start with `do_` become interactive commands. cmd is inspired by the Python library of the same name, but offers a distinctive Ruby feel and several additional features.

## An Example

Consider the following example of a small program to manage a lightweight phone book.

We want to be able to add, find, list and delete phone book entries.

We are keeping it realy simple so the entries will be stored in a Hash with names as keys and numbers as values. Let's assume that `@numbers` is our hash. First we'll write a command to add an entry. Entries will be entered like so:

    PhoneBook> add Sam, 312-555-1212

So we define a `do_add` method.

    def do_add(args)
      name, number = args.to_s.split(/, +/)
      @numbers[name.strip] = number
    end

We add another entry for good measure.

    PhoneBook> add Amy, 227-328-2868

We make a `print_name_and_number` method to format our entries.

    protected

      def print_name_and_number(*args)
        puts "%-25s %s" % args
      end

Writing a command to list all numbers is straightforward:

    def do_list
      @numbers.sort.each do |name, number|
        print_name_and_number(name, number)
      end
    end

We run our `list` command:

    PhoneBook> list
    Amy                       227-328-2868
    Sam                       312-555-1212

Then we write a find command to get the number for a given person.

    def do_find(name)
      name.to_s.strip!
      if @numbers[name]
        print_name_and_number(name, @numbers[name])
      else
        puts "#{name} isn't in the phone book"
      end
    end

    PhoneBook> find Sam
    Sam                       312-555-1212
    PhoneBook> find Matz
    Matz isn't in the phone book

Well we are cruising for burgers. But say we have a falling out with Amy (she was taking up too much disk space anyway). No reason to keep her in the phone book, so we'll define a `delete` command.

    def do_delete(name)
      @numbers.delete(name) || write("No entry for '#{name}'")
    end

    PhoneBook> delete Amy
    PhoneBook> list
    Sam                       312-555-1212

## Shortcuts

Commands like `add` and `delete` have clear names. They are self-documenting. But it can get tedious to type them all out all the time.

You can add shortcuts for commands using Cmd::ClassMethods.shortcut.

    shortcut  '+',  :add

The default `help` command lists shortcuts for a given command. The `help` command itself has a shortcut: +?+.

    PhoneBook> ? add
    add    -- Add an entry (ex: add Sam, 312-555-1212) (aliases: +)

Additionally, any unambiguous abbreviation of a command will be translated to the full command (so aliases that simply shorten the name of a given command are unnecessary).

Since we only have one command that starts with `h`, the above could have been written as:

    PhoneBook> h add
    add    -- Add an entry (ex: add Sam, 312-555-1212) (aliases: +)

Furthermore, abbreviations are acceptable in any place a command name appears, so you could write the above in an even more abbreviated way:

    PhoneBook> h a
    add    -- Add an entry (ex: add Sam, 312-555-1212) (aliases: +)

## Documenting your commands

Our phone list now has its basic functionality. Let's add some documentation so that someone other than you can figure out how to use it.

You document your commands using Cmd::ClassMethods.doc. We'll add docs for our four commands so far:

    doc :add,    'Add an entry (ex: add Sam, 312-555-1212)'
    doc :find,   'Look up an entry (ex: find Sam)'
    doc :list,   'List all entries'
    doc :delete, 'Remove an entry'

## Getting Help

As illustrated above, there is a predefined `help` command. Called without arguments, it displays a help line for each command that has been documented using the Cmd::ClassMethods.doc class method. (See `Documenting your commands` for more on this.) By default, commands without documentation are listed at the end of the `help` output; this can be turned off by setting YourCmdClass.hide_undocumented_commands = true. You can get help for a single command by passing it as an argument to the `help` command.

    PhoneBook> help add
    add    -- Add an entry (ex: add Sam, 312-555-1212)

Typing `help` affords you tab completion on all available commands with documentation, so the above could be accomplished (assuming there are no other documented commands that start with the letter `a`) by typing:

    PhoneBook> help a<Tab>

The help command is aliased to +?+.

## Subcommands

Any method that is of the form `do_command_subcommand` will be interpreted as a subcommand of `command`. For example, if there was an `add` command, a `do_add_cellphone` method would be invoked if 'add cellphone' was entered at the prompt. If there was no `add` command, `do_add_cellphone` would not be interpreted as a subcommand; you'd need to enter 'add_cellphone' to invoke it.

## Missing commands

Much like `method_missing`, there is a `command_missing` method which is called if an undefined command is entered in at the prompt. By default it simply reports that the command does not exist; subclasses can override this behavior. `command_missing` is passed the entered command name as well as any arguments. You must define your `command_missing` this way.

Let's make phone book entry lookups more convenient by having `command_missing` delegate to the `find` command.

    protected

      def command_missing(command, args)
        do_find(command)
      end

Now we can do

    PhoneBook> Sam
    Sam                       312-555-1212

## Lifecycle callbacks

Right now our phone book isn't really useful as the hash gets lost any time you quit the program. Let's implement a simple storage scheme so that our phone book entries will persist between invocations. A simple solution is just to serialize the phone book hash to YAML in a file.

First we'll choose a place to store the file (apologies to people running Windows).

    PHONEBOOK_FILE = File.expand_path('~/.phonebook')

When the command loop is started, your subclass's `setup` method is called. Consider this your `initialize`. We can use this to grab the contents of our phone book file.

    protected

      def setup
        @numbers = get_store || {}
      end

      def get_store
        File.open(PHONEBOOK_FILE) {|store| YAML.load(store)} rescue nil
      end

Now when we start up our phone book it will grab our entries or create a fresh Hash in which to add entries. But we don't have any code to save our phone book entries!

A Cmd session happens mostly inside a loop. This loop accepts commands until it is told to stop. Like `setup`, there are several methods that are called automatically during the lifetime of this loop. One such method is `postloop`, which, as the name suggests, is called after the loop is done, or in other words, once the Cmd session is completed. This turns out to be a good candidate for the task of saving our phone book entries.

    protected

      def postloop
        File.open(PHONEBOOK_FILE, 'w') {|store| store.write YAML.dump(@numbers)}
      end

And that is that. Now when we exit the phone book our numbers will be saved to our phone book file.

    $ ruby phonebook.rb
    PhoneBook> l
    PhoneBook> a Sam, 312-555-1212
    PhoneBook> Sam
    Sam                       312-555-1212
    PhoneBook> exit
    $ ruby phonebook.rb
    PhoneBook> l
    Sam                       312-555-1212

There are five life-cycle callbacks. The complete list is below:

`setup` | Called when your Cmd subclass is created, like `initialize`.
`preloop` | Called before the command loop begins
`precmd` | Called before each command
`postcmd` | Called after each command; has access to the `current_command` method, which returns the name of the current command
`postloop` | Called after the command loop ends

Here we can have a look at a working copy of our PhoneBook.

## Customizing command completion

By default Cmd supports readline functionality if it is enabled on your system. This affords you command line history as well as command completion. The default completion procedure will complete command names for you when you hit the Tab key.

    PhoneBook> l<Tab>
    PhoneBook> list

As is the case in your standard shell, hitting tab twice when there is nothing to complete will list all commands.

    PhoneBook> <Tab><Tab>
    add     delete  exit    find    help    list    shell

Completion can be customized on a per-command basis by defining a method of the form `complete_command` (where `command` is a command name) which returns an array with zero or more strings. The following (contrived) example illustrates the idea:

    $ grep -A 3 complete_add
    def complete_add
      %w{ cellphone fax home office }
    end

    PhoneBook> add <Tab>
    cellphone    fax    home    office
    PhoneBook> add o<Tab>
    PhoneBook> add office

If a given command has subcommands, Cmd's built in completion method will complete with those subcommands automatically, so the above example would be redundant were there to be command methods such as `do_add_office` and `do_add_home`, etc.

FIXME These docs lie I'm afraid. The API is not that simple yet, though the above is the intended API. Check out the part of the files/TODO.html file that talks about improving how completion works.

## Setting your prompt

By default the prompt will look like 'YourSubclass> '. So in the example above, where we have been writing all that code inside a PhoneBook class that inherits from Cmd, the prompt reads 'PhoneBook> '. The Cmd::ClassMethods.prompt_with macro style method can be used to set a custom prompt. The simplest prompt would just be a static string:

    prompt_with '>  '

You can, alternatively, pass Cmd::ClassMethods.prompt_with a Proc or method reference.

### Proc

    # Contrived...
    prompt_with { "#{Time.now}> " }

### Method reference

    prompt_with :set_prompt

    protected

      # This assumes current_directory is defined by your Cmd subclass
      def set_prompt
        "#{ENV['USER']:#{current_directory}$ "
      end

Using a method reference affords you access to all the state of your Cmd instance.

N.B. The result of whatever is passed to Cmd::ClassMethods.prompt_with has `to_s` called on it.

## Trapping user interrupts

If a user attempts to exit the command loop (using, for example, Ctrl-C), the Cmd.user_interrupt method is called. Subclasses may override this. By default it simply exits.

## Customizing passed arguments

For commands that take arguments (determined by whether or not you define your `do_` method with arguments), a method Cmd#tokenize_args is called on the passed arguments. The default implementation has no side effects.

N.B. This API will more than likely change to something far more useful and Rubyesque. Please checkout the files/TODO.html for details.

## Handling exceptions

All exceptions raised within the command loop are caught. You can specify what action should be taken if a specific exception is raised by using the Cmd::ClassMethods.handle method.

    handle StackOverflowError, :handle_stack_overflow

If you specify a symbol the referenced method will be called. If you supply a string, such as

    handle StackOverflowError, 'Stack underflowed'

the string will be displayed to the user.

All other exceptions are passed to a Cmd.handle_exception method which by default simply reraises the exception. Subclasses may use this to customize how exceptions are handled.

    def handle_exception(exception)
      write 'Error'
    end

## Not running interactively

Though subclasses of Cmd are meant to be run interactively, you may find that you'd like to have access to a given command without starting up a session with the interactive interpreter. Cmd allows you to run commands from the command line. If you supply a command (with optional arguments) when invoking the program that runs your command loop, the supplied command will be invoked, and execution will stop.

    $ ruby phonebook.rb Sam
    Sam                       312-555-1212

## Empty command lines

If a user enters an empty line at the command prompt, the `empty_line` method is called. By default it does nothing.

## Stopping the loop

Calling the `stoploop` method will stop the command loop once the current command is complete.

## Hidding undocumented commands

By default undocumented commands (if any) are listed at the bottom of the default help message. This behaviour can be disabled by setting `hide_undocumented_commands` to `true`.

    MyCmdClass.hide_undocumented_commands = true

## Callback reference

`handle_exception` | see Handling exceptions
`user_interrupt` | see Trapping user interrupts
`tokenize_args` | see Customizing passed in arguments
`setup` | see Setting up your environment
`command_missing` | see Missing commands
`empty_line` | see Emtpy command lines

## Download

Subversion

- svn.vernix.org/main/library/cmd/trunk

Documentation can be found at

- code.vernix.org/cmd

## Install

See the files/INSTALL.html doc.

[Validate]

Generated with the Darkfish Rdoc Generator 2.