Class: Cmd

### Home Classes Methods

### In Files

- lib/cmd.rb

### Parent

Object

### Namespace

- MODULE Cmd::ClassMethods

### Methods

- ::new
- ::run
- #arguments_missing
- #cmdloop
- #command
- #command_abbreviations
- #command_list
- #command_lookup_table
- #command_missing
- #command_shortcuts
- #complete
- #complete_help
- #complete_method
- #completion_grep
- #current_command
- #custom_exception_handler
- #default_prompt
- #display_prompt
- #display_shortcuts
- #do_exit
- #do_help
- #do_shell
- #documented_commands
- #empty_line
- #exception_is_handled?
- #execute_command
- #execute_line
- #find_subcommand_in_args
- #handle_all_remaining_exceptions
- #handle_exception
- #has_shortcuts?
- #has_subcommands?
- #interrupt
- #no_help
- #parse_line
- #postcmd
- #postloop
- #precmd
- #preloop
- #print
- #print_help
- #print_undocumented_commands
- #puts
- #readline_supported?
- #run
- #run_command
- #run_custom_exception_handling
- #sanitize_readline_history
- #set_completion_proc
- #setup
- #stoploop
- #subcommand_list
- #subcommands
- #tokenize_args
- #translate_shortcut
- #turn_off_readline
- #undocumented_commands
- #undocumented_commands?
- #undocumented_commands_hidden?
- #user_interrupt
- #write

### Included Modules

- Cmd::ClassMethods

### Files

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

### Class/Module Index

Quicksearch

- Cmd
- Cmd::ClassMethods
- Object

No matching classes.

# Cmd

A simple framework for writing line-oriented command interpreters, based heavily on Python's cmd.py.

These are often useful for test harnesses, administrative tools, and prototypes that will later be wrapped in a more sophisticated interface.

A Cmd instance or subclass instance is a line-oriented interpreter framework. There is no good reason to instantiate Cmd itself; rather, it's useful as a superclass of an interpreter class you define yourself in order to inherit Cmd's methods and encapsulate action methods.

### Attributes

hide_undocumented_commands[RW]

Flag that sets whether undocumented commands are listed in the help

current_command[W]

The current command

stdin[W]

STDIN stream used

stdout[W]

STDOUT stream used

### Public Class Methods

new() click to toggle source

    # File lib/cmd.rb, line 140
    def initialize
      @stdin, @stdout = STDIN, STDOUT
      @stop = false
      setup
    end

run(intro = nil) click to toggle source

    # File lib/cmd.rb, line 124
    def run(intro = nil)
      new.cmdloop(intro)
    end

### Public Instance Methods

cmdloop(intro = nil) click to toggle source

Starts up the command loop

    # File lib/cmd.rb, line 147
    def cmdloop(intro = nil)
      preloop
      write intro if intro
      begin
        set_completion_proc(:complete)
        begin
          execute_command
        # Catch ^C
        rescue Interrupt
          user_interrupt
        # I don't know why ZeroDivisionError isn't caught below...
        rescue ZeroDivisionError
          handle_all_remaining_exceptions(ZeroDivisionError)
        rescue => exception
          handle_all_remaining_exceptions(exception)
        end
      end until @stop
      postloop
    end

Also aliased as: run
do_exit() click to toggle source

    # File lib/cmd.rb, line 187
    def do_exit; stoploop end

do_help(command = nil) click to toggle source

    # File lib/cmd.rb, line 170
    def do_help(command = nil)
      if command
        command = translate_shortcut(command)
        docs.include?(command) ? print_help(command) : no_help(command)
      else
        documented_commands.each {|cmd| print_help cmd}
        print_undocumented_commands if undocumented_commands?
      end
    end

no_help(command) click to toggle source

Called when the `command` has no associated documentation, this could potentially mean that the command is non existant

    # File lib/cmd.rb, line 182
    def no_help(command)
      write "No help for command '#{command}'"
    end

run(intro = nil) click to toggle source
Alias for: cmdloop
turn_off_readline() click to toggle source

Turns off readline even if it is supported

    # File lib/cmd.rb, line 190
    def turn_off_readline
      @readline_supported = false
      self
    end

### Protected Instance Methods

arguments_missing() click to toggle source

XXX Not implementd yet. Called when a do_ method that takes arguments doesn't get any

    # File lib/cmd.rb, line 282
    def arguments_missing
      write 'Invalid arguments'
      do_help(current_command) if docs.include?(current_command)
    end

command(cmd) click to toggle source

The method name that corresponds to the passed in command.

    # File lib/cmd.rb, line 306
    def command(cmd)
      "do_#{cmd}".intern
    end

command_abbreviations() click to toggle source

Returns lookup table of unambiguous identifiers for commands.

    # File lib/cmd.rb, line 364
    def command_abbreviations
      return @command_abbreviations if @command_abbreviations
      @command_abbreviations = Abbrev::abbrev(command_list)
    end

command_list() click to toggle source

Lists of commands (i.e. do_* methods minus the 'do_' part).

    # File lib/cmd.rb, line 353
    def command_list
      collect_do - subcommand_list
    end

command_lookup_table() click to toggle source

Definitive list of shortcuts and abbreviations of a command.

    # File lib/cmd.rb, line 358
    def command_lookup_table
      return @command_lookup_table if @command_lookup_table
      @command_lookup_table = command_abbreviations.merge(shortcut_table)
    end

command_missing(command, args) click to toggle source

Called when the line entered at the prompt does not map to any of the defined commands. By default it reports that there is no such command.

    # File lib/cmd.rb, line 539
    def command_missing(command, args)
      write "No such command '#{command}'"
    end

command_shortcuts(cmd) click to toggle source

Returns the set of registered shortcuts for a command, or nil if none.

    # File lib/cmd.rb, line 507
    def command_shortcuts(cmd)
      shortcuts[cmd]
    end

complete(command) click to toggle source

The default completor. Looks up all do_* methods.

    # File lib/cmd.rb, line 343
    def complete(command)
      commands = completion_grep(command_list, command)
      if commands.size == 1
        cmd = commands.first
        set_completion_proc(complete_method(cmd)) if collect_complete.include?(cmd)
      end
      commands
    end

complete_help(command) click to toggle source

Completor for the help command.

    # File lib/cmd.rb, line 418
    def complete_help(command)
      completion_grep(documented_commands, command)
    end

complete_method(cmd) click to toggle source

The method name that corresponds to the complete command for the pass in command.

    # File lib/cmd.rb, line 312
    def complete_method(cmd)
      "complete_#{cmd}".intern
    end

completion_grep(collection, pattern) click to toggle source

    # File lib/cmd.rb, line 422
    def completion_grep(collection, pattern)
      collection.grep(/^#{Regexp.escape(pattern)}/)
    end

current_command() click to toggle source

The current command.

    # File lib/cmd.rb, line 271
    def current_command
      translate_shortcut @current_command
    end

custom_exception_handler(exception) click to toggle source

Returns the customized handler for the exception

    # File lib/cmd.rb, line 242
    def custom_exception_handler(exception)
      custom_exception_handlers[exception.to_s]
    end

default_prompt() click to toggle source

    # File lib/cmd.rb, line 543
    def default_prompt
      "#{self.class.name}> "
    end

display_prompt(prompt, with_history = true) click to toggle source

Displays the prompt.

    # File lib/cmd.rb, line 260
    def display_prompt(prompt, with_history = true)
      line = if readline_supported?
        Readline::readline(prompt, with_history)
      else
        print prompt
        @stdin.gets
      end
      line.respond_to?(:strip) ? line.strip : line
    end

display_shortcuts(cmd) click to toggle source

    # File lib/cmd.rb, line 301
    def display_shortcuts(cmd)
      "(aliases: #{shortcuts[cmd].join(', ')})"
    end

do_shell(line) click to toggle source

Executes a shell, perhaps should only be defined by subclasses.

    # File lib/cmd.rb, line 444
    def do_shell(line)
      shell = ENV['SHELL']
      line ? write(%(#{line}).strip) : system(shell)
    end

documented_commands() click to toggle source

List of commands which are documented.

    # File lib/cmd.rb, line 386
    def documented_commands
      docs.keys.sort
    end

empty_line() click to toggle source

Called when an empty line is entered in response to the prompt.

    # File lib/cmd.rb, line 336
    def empty_line
    end

exception_is_handled?(exception) click to toggle source

Determines if the given exception has a custome handler.

    # File lib/cmd.rb, line 229
    def exception_is_handled?(exception)
      custom_exception_handler(exception)
    end

execute_command() click to toggle source

    # File lib/cmd.rb, line 197
    def execute_command
      unless ARGV.empty?
        stoploop
        execute_line(ARGV * ' ')
      else
        execute_line(display_prompt(prompt, true))
      end
    end

execute_line(command) click to toggle source

    # File lib/cmd.rb, line 214
    def execute_line(command)
      postcmd(run_command(precmd(command)))
    end

find_subcommand_in_args(subcommands, args) click to toggle source

Extracts a subcommand if there is one from the command line submitted. I guess this is a hack.

    # File lib/cmd.rb, line 491
    def find_subcommand_in_args(subcommands, args)
      (subcommands & (1..args.size).to_a.map {|num_elems| args.first(num_elems).join('_')}).max
    end

handle_all_remaining_exceptions(exception) click to toggle source

    # File lib/cmd.rb, line 206
    def handle_all_remaining_exceptions(exception)
      if exception_is_handled?(exception)
        run_custom_exception_handling(exception)
      else
        handle_exception(exception)
      end
    end

handle_exception(exception) click to toggle source

Exceptions in the cmdloop are caught and passed to `handle_exception`. Custom exception classes must inherit from StandardError to be passed to `handle_exception`.

    # File lib/cmd.rb, line 255
    def handle_exception(exception)
      raise exception
    end

has_shortcuts?(cmd) click to toggle source

Indicates if the passed in command has any registerd shortcuts.

    # File lib/cmd.rb, line 502
    def has_shortcuts?(cmd)
      command_shortcuts(cmd)
    end

has_subcommands?(command) click to toggle source

Indicates whether a given command has any subcommands.

    # File lib/cmd.rb, line 381
    def has_subcommands?(command)
      !subcommands(command).empty?
    end

interrupt() click to toggle source

A bit of a hack I'm afraid. Since subclasses will be potentially overriding user_interrupt we want to ensure that it returns true so that it can be called with 'and return'

    # File lib/cmd.rb, line 290
    def interrupt
      user_interrupt or true
    end

parse_line(line) click to toggle source

Receives the line as it was passed from the prompt (barring modification in precmd) and splits it into a command section and an args section. The args are by default set to nil if they are boolean false or empty then joined with spaces. The tokenize method can be used to further alter the args.

    # File lib/cmd.rb, line 474
    def parse_line(line)
      # line will be nil if ctr-D was pressed
      user_interrupt and return if line.nil?

      cmd, *args = line.split
      args = args.to_s.empty? ? nil : args * ' '
      if args and has_subcommands?(cmd)
        if cmd = find_subcommand_in_args(subcommands(cmd), line.split)
          # XXX Completion proc should be passed array of subcommands somewhere
          args = line.split.join('_').match(/^#{cmd}/).post_match.gsub('_', ' ').strip
          args = nil if args.empty?
        end
      end
      [cmd, args]
    end

postcmd(line) click to toggle source

Receives the returned value of the called command.

    # File lib/cmd.rb, line 331
    def postcmd(line)
      line
    end

postloop() click to toggle source

Call back executed at the end of the cmdloop.

    # File lib/cmd.rb, line 321
    def postloop
    end

precmd(line) click to toggle source

Receives line submitted at prompt and passes it along to the command being called.

    # File lib/cmd.rb, line 326
    def precmd(line)
      line
    end

preloop() click to toggle source

Call back executed at the start of the cmdloop.

    # File lib/cmd.rb, line 317
    def preloop
    end

print(*strings) click to toggle source

Writes out a message without newlines appended.

    # File lib/cmd.rb, line 437
    def print(*strings)
      strings.each {|string| @stdout.write string}
    end

print_help(cmd) click to toggle source

Displays the help for the passed in command.

    # File lib/cmd.rb, line 295
    def print_help(cmd)
      offset = docs.keys.longest_string_length
      write "#{cmd.ljust(offset)} -- #{docs[cmd]}"            +
      (has_shortcuts?(cmd) ? " #{display_shortcuts(cmd)}" : '')
    end

print_undocumented_commands() click to toggle source

    # File lib/cmd.rb, line 396
    def print_undocumented_commands
      return if undocumented_commands_hidden?
      # TODO perhaps do some fancy stuff so that if the number of undocumented
      # commands is greater than 80 cols or some such passed in number it
      # presents them in a columnar fashion much the way readline does by default
      write ' '
      write 'Undocumented commands'
      write '====================='
      write undocumented_commands.join(' ' * 4)
    end

puts(*strings) click to toggle source
Alias for: write
readline_supported?() click to toggle source

Indicates whether readline support is enabled

    # File lib/cmd.rb, line 223
    def readline_supported?
      @readline_supported = READLINE_SUPPORTED if @readline_supported.nil?
      @readline_supported
    end

run_command(line) click to toggle source

Takes care of collecting the current command and its arguments if any and dispatching the appropriate command.

    # File lib/cmd.rb, line 451
    def run_command(line)
      cmd, args = parse_line(line)
      sanitize_readline_history(line) if line
      unless cmd then empty_line; return end

      cmd = translate_shortcut(cmd)
      self.current_command = cmd
      set_completion_proc(complete_method(cmd)) if collect_complete.include?(complete_method(cmd))
      cmd_method = command(cmd)
      if self.respond_to?(cmd_method)
        # Perhaps just catch exceptions here (related to arity) and call a
        # method that reports a generic error like 'invalid arguments'
        self.method(cmd_method).arity.zero? ? self.send(cmd_method) : self.send(cmd_method, tokenize_args(args))
      else
        command_missing(current_command, tokenize_args(args))
      end
    end

run_custom_exception_handling(exception) click to toggle source

Runs the customized exception handler for the given exception.

    # File lib/cmd.rb, line 234
    def run_custom_exception_handling(exception)
      case handler = custom_exception_handler(exception)
      when String: write handler
      when Symbol: self.send(custom_exception_handler(exception))
      end
    end

sanitize_readline_history(line) click to toggle source

Cleans up the readline history buffer by performing tasks such as removing empty lines and piggy-backed duplicates. Only executed if running with readline support.

    # File lib/cmd.rb, line 519
    def sanitize_readline_history(line)
      return unless readline_supported?
      # Strip out empty lines
      Readline::HISTORY.pop if line.match(/^\s*$/)
      # Remove duplicates
      Readline::HISTORY.pop if Readline::HISTORY[-2] == line rescue IndexError
    end

set_completion_proc(cmd) click to toggle source

Readline completion uses a procedure that takes the current readline buffer and returns an array of possible matches against the current buffer. This method sets the current procedure to use. Commands can specify customized completion procs by defining a method following the naming convetion complet_{command_name}.

    # File lib/cmd.rb, line 532
    def set_completion_proc(cmd)
      return unless readline_supported?
      Readline.completion_proc = self.method(cmd)
    end

setup() click to toggle source

Called at object creation. This can be treated like 'initialize' for sub classes.

    # File lib/cmd.rb, line 249
    def setup
    end

stoploop() click to toggle source

    # File lib/cmd.rb, line 218
    def stoploop
      @stop = true
    end

subcommand_list() click to toggle source

List of all subcommands.

    # File lib/cmd.rb, line 370
    def subcommand_list
      with_underscore, without_underscore = collect_do.partition {|command| command.include?('_')}
      with_underscore.find_all {|do_method| without_underscore.include?(do_method[/^[^_]+/])}
    end

subcommands(command) click to toggle source

Lists all subcommands of a given command.

    # File lib/cmd.rb, line 376
    def subcommands(command)
      completion_grep(subcommand_list, translate_shortcut(command) + '_')
    end

tokenize_args(args) click to toggle source

Called on command arguments as they are passed into the command.

    # File lib/cmd.rb, line 512
    def tokenize_args(args)
      args
    end

translate_shortcut(cmd) click to toggle source

Looks up command shortcuts (e.g. '?' is a shortcut for 'help'). Short cuts can be added by using the shortcut class method.

    # File lib/cmd.rb, line 497
    def translate_shortcut(cmd)
      command_lookup_table[cmd] || cmd
    end

undocumented_commands() click to toggle source

Returns list of undocumented commands.

    # File lib/cmd.rb, line 408
    def undocumented_commands
      command_list - documented_commands
    end

undocumented_commands?() click to toggle source

Indicates if any commands are undocumeted.

    # File lib/cmd.rb, line 413
    def undocumented_commands?
      !undocumented_commands.empty?
    end

undocumented_commands_hidden?() click to toggle source

Indicates whether undocummented commands will be listed by the help command (they are listed by default).

    # File lib/cmd.rb, line 392
    def undocumented_commands_hidden?
      self.class.hide_undocumented_commands
    end

user_interrupt() click to toggle source

Called when the user hits ctrl-C or ctrl-D. Terminates execution by default.

    # File lib/cmd.rb, line 276
    def user_interrupt
      write 'Terminating' # XXX get rid of this
      stoploop
    end

write(*strings) click to toggle source

Writes out a message with newline.

    # File lib/cmd.rb, line 427
    def write(*strings)
      # We want newlines at the end of every line, so don't join with "\n"
      strings.each do |string|
        @stdout.write string
        @stdout.write "\n"
      end
    end

Also aliased as: puts

[Validate]

Generated with the Darkfish Rdoc Generator 2.