Going forward in my Ruby practices and as a continuation of
my previous post here is the code of a shell-like command line interpreter which makes use of
catch/throw and exceptions for handle the user input. The idea of this exercise came after read the
ex 42 of the series that Michael Larsen is doing following the
Learning Ruby the Hard Way book.
class Shell
def initialize()
@arguments = []
end
def run()
nextOrder = prompt
while (nextOrder)
catch (:callCommand) do
throw :callCommand if nextOrder.length == 0
command = nextOrder.shift()
@arguments = nextOrder
begin
currentCommand = method(command)
rescue NameError
puts "Command not found"
throw :callCommand
end
begin
currentCommand.call()
rescue ArgumentError
puts "Argument error"
rescue SystemExit
puts "... bye!"
Process.exit!(true)
rescue Exception => unknownError
puts "Unknown Error: #{unknownError.message}"
end
end
nextOrder = prompt
end
end
def prompt()
print "\n>"
gets.chomp().split(' ')
end
def quit()
raise SystemExit
end
def command1()
puts "Executing command1"
end
def command2()
raise ArgumentError if @arguments.length == 0
puts "Executing command2"
end
def help()
puts <<HELPTEXT
command1 executes the command1
command2 [ARGUMENT] executes the command2
help Shows this help.
quit/exit Exit the shell.
HELPTEXT
end
end
Shell.new.run
As you can see the shell-like program is implemented by a class whose methods are the available "commands" the user can execute over this shell. Additionally the class includes methods for quit the shell, display help to the user, print out the shell prompt, and the
run method that is responsible for start up the shell. The shell only allows the execution of
command1 and
command2 but it can be easily extended for allow the execution of more commands. Just add those commands as methods of the Shell class.
When the user types a command the
prompt method returns an array which is handle by the
catch block labeled as
callCommand. Here, it extracts the first element off the array and assumes it is the command the user wants to execute and takes the remaining array as the arguments of the command.
A few additional comments about the code. The first
throw is called when the user types the
Return key without insert any command, then the program jumps out to the end of the
catch block and displays again the program prompt. The first
exception block detects if the user types unknown command, in those cases it displays an error message and jumps out to the prompt method skipping the second
exception block, which is responsible of call the methods (commands) and handle their errors. I make use of the
rescue statement as a way for exit the program. I think more elegant than the the use of quit, die, or exit. Finally, for avoid the program stops its execution if an unexpected error happend, the last
rescue block invokes the base Exception class in Ruby.