# Scripts framework

**URL:** <https://www.ruby-forum.com/t/scripts-framework/64980>\
**Category:** Ruby\
**Created:** [August 15, 2006, 3:49pm UTC](https://www.ruby-forum.com/t/scripts-framework/64980 "2006-08-15T15:49:57Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [August 15, 2006, 3:49pm UTC](https://www.ruby-forum.com/t/scripts-framework/64980/1 "2006-08-15T15:49:57Z")

</div>

I’ve been writing a few unix scripts recently. I’m very much feeling the  
need for a framework that will let me build scripts and not worry about  
such common functionality as:

- providing parameters through the command line and config files, or  
falling back to program specified defaults.
- getting help on command usage (perhaps via embedded rdoc).

I would imagine there is a developed framework out there to do this kind  
of thing?

Cheers,  
Benjohn

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [August 15, 2006, 4:01pm UTC](https://www.ruby-forum.com/t/scripts-framework/64980/2 "2006-08-15T16:01:58Z")

</div>

On Aug 15, 2006, at 14:45, removed\_email\_address@domain.invalid wrote:

> I would imagine there is a developed framework out there to do this  
> kind  
> of thing?

The optparse library is distributed along with Ruby, and will  
probably do a large part of what you’re after. And don’t forget  
about ARGF.

[http://ruby-doc.org/stdlib/libdoc/optparse/rdoc/index.html](http://ruby-doc.org/stdlib/libdoc/optparse/rdoc/index.html)

matthew smillie.

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [August 15, 2006, 4:44pm UTC](https://www.ruby-forum.com/t/scripts-framework/64980/3 "2006-08-15T16:44:42Z")

</div>

> > falling back to program specified defaults.
> > 
> > - getting help on command usage (perhaps via embedded rdoc).
> > 
> > I would imagine there is a developed framework out there to do this  
> > kind  
> > of thing?
> 
> The optparse library is distributed along with Ruby, and will  
> probably do a large part of what you’re after. And don’t forget  
> about ARGF.

🙂 Thanks for the ARGF pointer. Optparse is the right kind of thing to  
be using, but I was hoping for more.

Cheers,  
Benjohn

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [August 15, 2006, 6:27pm UTC](https://www.ruby-forum.com/t/scripts-framework/64980/4 "2006-08-15T18:27:01Z")

</div>

On 8/15/06, Trans [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> > I would imagine there is a developed framework out there to do this kind  
> > of thing?

‘commandline’ and ‘choice’ are two gems that do what you want.  
‘commandline’  
is powerful, and based on the optparse library, but its documentation  
leaves  
a lot to be desired. The basic README gets you started but you have to  
deduce a lot for complex arguments.

‘choice’ is more constrained, and easier to use, but it doesn’t work too  
well when things get complex. Its great for simple sets of name-value  
pairs,  
though. The developer also seems responsive to bugs and feedback. At  
least,  
he fixed the one bug I sent in 🙂

Commandline: [http://rubyforge.org/projects/optionparser/](http://rubyforge.org/projects/optionparser/)  
Choice: [http://rubyforge.org/projects/choice/](http://rubyforge.org/projects/choice/)

Justin

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [August 15, 2006, 6:27pm UTC](https://www.ruby-forum.com/t/scripts-framework/64980/5 "2006-08-15T18:27:01Z")

</div>

I’ve been using Usage for its simplicity.  
[http://rubyforge.org/projects/usage/](http://rubyforge.org/projects/usage/)

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [August 15, 2006, 5:35pm UTC](https://www.ruby-forum.com/t/scripts-framework/64980/6 "2006-08-15T17:35:53Z")

</div>

removed\_email\_address@domain.invalid wrote:

> Cheers,  
> Benjohn

There are a number of alternative out there. A couple in particluar are  
nice. Can’t recall the name of it at the moment but one allows you to  
define the syntax via USAGE docs and it will parse the docs to setup  
the functionality for you. That’s kind of cool, albeit not my style.  
Anyone recall this lib?

Another way (my style) is Facets command.rb. It makes basic command  
line scripts as easy as silly putty.

require ‘facet/command’

class MyCommand \<\< Console::Command

```
# define some options

def __help
  puts "HELP!"
  exit 0
end

def __debug
  $DEBUG = true
end
alias :_d :__debug

def __file( name )
  @name = name
end

# define some subcommands

def sub1
  puts "This is a subcommand for #{@name}"
end

def sub2( req )
  puts "This is a subcommand with a required parameter #{req}"
end

```

end

MyCommand.execute

Running it:

% mycmd --help  
HELP!

% mycmd --file ‘afile’ sub1  
This is a subcommand for afile

&c.

It has some limitations in so far as sepcifying strict rules about  
which subcommand and options work together but often that proves  
unneccessary. Also, I haven’t finished the automatic help system yet,  
but I’ve started it and should be availbe soon.

Finally, yuo might be intersted in the project I’m working on right now  
called, Script Sake (as in the drink). It’s a build tool like Rake and  
my clone of it, Reap, but it moves away from the monolithic “Rakefile”  
idea and instead focuses on bolstering standard scripts. Should have a  
realese ready shortly.

T.

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [August 15, 2006, 7:33pm UTC](https://www.ruby-forum.com/t/scripts-framework/64980/7 "2006-08-15T19:33:12Z")

</div>

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

```

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

```

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

```

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

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

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

```

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

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

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [August 15, 2006, 10:30pm UTC](https://www.ruby-forum.com/t/scripts-framework/64980/8 "2006-08-15T22:30:47Z")

</div>

Hi phrogz, justin, trans and matthew, thank you for all the pointers,  
they look like they’ll be very useful.
