Monday, June 28, 2004

Reasons to Like Scripting Languages, #134

Being able to read the source of the executing program enables a neat ‘usage’ trick.

Well-behaved command line programs (remember the command line?) display a nice usage message when given invalid arguments. The message describes what they do and how they should be invoked. Well-documented command line programs also start with some kind of comment block. The comment describes what they do and how they should be invoked.

Hmmm, seems like a violation of the DRY principle, having all that information duplicated…

In Ruby (and most scripting languages, I suspect) it needn’t be. Let’s assume that the top-level source file in your application starts with a comment describing how it is used. We can then use the built-incaller method, which returns call stack. The last element in this array is the top level program. The entry is in the form filename:linenumber, so we can use a simple regexp to extract just the file name portion. We then open that source file and read in the comment block, writing it out as a help message after stripping the leading comment characters.

The show_usage method is pretty simple:

   def show_usage(msg=nil)
name = caller[-1].sub(/:\d+$/, '')
$stderr.puts "\nError: #{msg}" if msg
$stderr.puts do |f|
while line = f.readline and line.sub!(/^# ?/, '')
$stderr.puts line
exit 1

An example of this method in use is the start of a simple utility I wrote to record ad-hoc royalty payments to authors.

   # Pay an author a sum of money. We simply record the payment
# in the author_royalty_payment table: the statement code
# sorts it all out
# usage:
# ruby pay.rb "Author Name" 1234.56 <checkno>

require "common"

author = ARGV.shift || show_usage("Missing author name")
amount = ARGV.shift || show_usage("Missing amount")
amount = Money(amount) rescue show_usage("Invalid amount")
check = ARGV.shift || show_usage("Missing check")

Trivial, I know, but I like the simplicity.

No comments:

Post a Comment