In Files

Parent

Namespace

Included Modules

Class/Module Index [+]

Quicksearch

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