Gavin Kistner
8/15/2006 5:28:00 PM
Because it's hard to find the information (you have to install the GEM
and then dive in and look at the README in the installed area) here's
the documentation from Usage so you can get an idea how it's used:
OVERVIEW
The usage library is a library that is allows command line scripts to
be written with
a minimum of memorization necessary to access the arguments. I wrote
it because I was
tired of copying code from other command line programs that used
GetOptLong. This was
started before there was a profusion of different command line
libraries. Even with this
profusion, there is still some memorization that goes along with how to
set up the
framework process the arguments.
LIMITATIONS
This library really intended for simplish command line programs. It is
not really
capable of handling complex command line requirements like commands
with sub-commands
and the like (like CVS or SVN). It is really intended for that quick
command that you
are whipping up to do a simple task. It can grow with you for a while
but at some
point it may not be up to the task.
USAGE ON USAGE
A note on terminology. When I mention "arguments" it means command line
arguments that
are not prefixed by a "-" option. When I mention options, I mean those
things following
the "-". Options themselves can have arguments as well. So we have:
program arguments - those non option arguments
options - those dash things
option arguments - those things that are after but associated with
options
1. Simple Usage
The only thing you have remember to use usage are how commands are
usually documented.
First you need to require the usage library:
require "Usage"
Then set up the usage string for the command:
usage = Usage.new "infile outfile"
The above would be a command with two require arguments: an input file
and an output file.
To access those arguments, you just need to use the usage variable that
was created and
send the .infile or .outfile message to them.
File.open(usage.infile) do |fi|
File.open(usage.outfile, "w") do |fo|
fo.write(fi.read)
end
end
If the user doesn't supply the correct number of arguments, the program
exits with an error
and the usage for the program (hence the libraries name).
PROGRAM: test.rb
ERROR: too few arguments 2 expected, 0 given
USAGE: test.rb infile outfile
2. Lists of files (...)
You can write a program that accepts a list of files by using elipses
appended to an
argument (the following program concatenates the input files into one
output file).
usage = Usage.new "outfile infiles..."
File.open(usage.outfile, "w") do |fo|
usage.infiles.each do |infile|
File.open(usage.infile) { |fi| fo.write(fi.read)}
end
end
3. Optional arguments
You can have optional arguments by surounding them in square brackets.
usage = Usage.new "[optional_arg] required_arg"
These are accessed in the standard way
usage.optional_arg # this is nil if it is not given by the user
usage.required_arg
4. Options
You can have dash options that are either required or optional. Options
can also have
arguments associated with them.
usage = Usage.new "[-y] [-x excluded_tags] (-z ztag) (-w warning_arg)
files..."
The options are accessed with "dash_" prefixing the option so that the
-y is accessed
via .dash_y. The -x can be accessed either with #dash_x (which would be
either nil or
true) or #excluded_tags (which would be either nil or the argument for
the -x option).
The -z option is required and has one argument, also the -w option is
also required.
They can appear in any order (-z option first or -w option first). The
optional arguments
can appear either before, interspersed with, or after the required
options.
5. Long Options
You can also have long options by including lines following the initial
usage line that
associates the short options with the long ones. Example below:
usage = Usage.new "-x files...", <<EOT
-x,--exclusive specifies exclusive access to the files
EOT
With this case, now #dash_x and #exclusive give the same result when
applied to the usage
variable.
6. Typed options
Starting with version 0.3, typed options are handled by a plugin
architecture. There
are also more types supported than pre 0.3. Typed options are invoked
by preceeding an
argument with a string of non-alpha chars.
The following are built into the library (some I took from BASIC which
I programmed in
long, long ago). Note: These can be replaced or added to.
% - Integer
$ - String (but this is unnecessary as this is default)
# - Float
@ - Date-Time
< - A file that will be read
> - A file that will be written to
<< - A file that will be read in by readlines
>> - A file that will be appended to
>? - A file that will be written to but the user is prompted if it
already exists
>>? - A file that will be appended to but the user is prompted if the
file doesn't exist
<@ - Either a local file or a URI that is http:// or ftp://. Also,
filenames that
start with www. are prepended automatically with http:// and
filenames that
start with ftp. are prepended automatically with ftp://
So when you send the argument message to the usage object, you will get
a value of that
type and if the user does not give that type, then they get an error
message.
usage = Usage.new "%num_times @on_date"
In this example, #num_times returns and Integer object and #on_date
returns a Time object.
6.1. Adding new types
You can add a new type parser by declaring a sub-class of
UsageMod::ArgumentParserPlugin or
one of the other sub-classes of Usage::ArgumentParserPlugin such as
UsageMod::IntegerArgumentPlugin.
You need to define two methods:
initialize(usage_ui, string)
- usage_ui is used to ask the user yes/no questions
- string is the argument as a string. If the parsing fails, then
raise an exception
that is a sub-class of UsageMod::Error
close
- use this to close any objects that need to be closed/released when
the usage block ends
6.2 Assigning them to a string of chars
To assign a plugin class to a character, you only need to call
UsageMod::Base.add_type_handler.
So if the class that you created is called SpanishArgumentPlugin, and
you would like to
assign it to the '^^' character sequence, then you would call:
UsageMod::Base.add_type_handler("^^", SpanishArgumentPlugin)
6.3 Example
#
# This example is taken from the built-in class for arguments that are
writable files
# that don't want to be overwritten
#
# first define the file exists exception class
class FileOutputExistsError < UsageMod::Error
attr_reader :filename
def initialize(filename)
@filename = filename
super("output file exists: '#{filename}'")
end
end
# next define the argument parser plugin
class FileOutputQueryPlugin < UsageMod::ArgumentParserPlugin
def initialize(usage_ui, str)
if FileTest.exist?(str) then
raise FileOutputExistsError.new(str) if
usage_ui.ask_yes_no(OVERWRITE_QUERY % str, NO_RESPONSE) == NO_RESPONSE
end
@value = File.open(str, "w")
end
def close
@value.close
end
end
# lastly attach that parser to the character sequence '>?'
UsageMod::Base.add_type_handler(">?", FileOutputQueryPlugin)
7. Choice options
You can have optional options that have a set of values which they can
be. The choices
are separated by pipe symbols. See below:
usage = Usage.new "[-a coffee|tea|milk]"
After this #dash_a will give the string coffee, tea, or milk. If the
value given isn't
one of the given choices, then the user is given an error message with
the
appropriate choices.
8. Usage blocks
Starting with version 0.3, you can run usage within a block. This
allows any objects that need
clean-up when the block exits (such as open files). To do this it looks
like the following:
Usage.new "<infile >outfile" do |usage|
usage.outfile.write(usage.infile.read)
end # here the file objects are closed
If you do it without a block, then you would have to do the following:
usage = Usage.new "<infile >outfile"
usage.outfile.write(usage.infile.read)
usage.infile.close
usage.outfile.close