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-in caller 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
  File.open(name) do |f|
    while line = f.readline and line.sub!(/^# ?/, '')
      $stderr.puts line
    end
  end
  exit 1
end

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.

Please keep it clean, respectful, and relevant. I reserve the right to remove comments I don't feel belong.
  • NickName, E-Mail, and Website are optional. If you supply an e-mail, we'll notify you of activity on this thread.
  • You can use Markdown in your comment (and preview it using the magnifying glass icon in the bottom toolbar).