Be Helpful - Build Awesome Command-Line Applications in Ruby 2: Control Your Computer, Simplify Your Life (2013)

Build Awesome Command-Line Applications in Ruby 2: Control Your Computer, Simplify Your Life (2013)

Chapter 3. Be Helpful

In the previous chapter, we learned how to make an easy-to-use command-line interface. We learned the elements that make a well-formed command-line interface and how to design simple apps and command suites that accept arguments, flags, switches, and commands in an unsurprising[20] way. What we didn’t talk about was how a user finds out what options and commands such apps provide, what their options mean, and what arguments they accept or require. Without this information, our app might do the job expected of it, but it won’t be very helpful.

Fortunately for us, the standard Ruby library OptionParser and the open source GLI gem give us the power to make our app helpful without a lot of effort. In fact, you’ll see that it’s actually harder to make an unhelpful app using these tools. We’ll begin by exploring how you can add help and documentation to the pair of apps—db_backup.rb and todo—whose UI we developed in the previous chapter. We’ll also look at ways to create more detailed user documentation with an open source library that can bundle UNIX-style manual pages with our app. We’ll end the chapter with a look at some rules of thumb for making our documentation useful to both new users of our software and seasoned veterans.

3.1 Documenting a Command-Line Interface

An experienced command-line user will try one or two things on the command line to discover how to use an app: they will run it without arguments or give it a help switch, such as -h or --help (-help is also a possibility because many X-Windows apps respond to this for help). In each case, the user will expect to see a one-screen summary of the app’s usage, including what arguments the app accepts or requires and what options are available.

Because db_backup.rb uses OptionParser, we’re most of the way there already. Apps that use OptionParser respond to -h and --help in just the way our users expect. When OptionParser encounters either of these switches on the command line (assuming you haven’t overridden them), it will display basic help text that shows how to invoke the app and what options it accepts. Here’s what OptionParser displays when a user enters an -h or --help option for db_backup.rb:

$ db_backup.rb -h

Usage: db_backup [options]

-i, --iteration

-u USER

-p PASSWORD

$ db_backup.rb --help

Usage: db_backup [options]

-i, --iteration

-u USER

-p PASSWORD

While OptionParser nicely formats the help screen for us, what’s still missing is documentation to explain the meaning of each option. Even though the flags are somewhat self-documenting (e.g., a user will likely figure out that “PASSWORD” is the database password), they still bear further explanation. For example, because usernames are required to be in a certain format, the app should let users know that. The app also requires an argument—the name of the database to back up—and this should be documented in the help text as well.

Documenting Command-Line Options

Once we fill in the documentation, we’d like our help text to look like so:

$ db_backup.rb --help

Usage: db_backup [options]

-i, --iteration Indicate that this backup is an "iteration" backup

-u USER Database username, in first.last format

-p PASSWORD Database password

Now the user can see exactly what the options mean and what constraints are placed on them (e.g., the username’s format). Achieving this with OptionParser couldn’t be simpler. If you recall from Table 1, Overview of OptionParser parameters to on , any string given as a parameter to on that doesn’t match the format of an option will be treated as documentation.

So, all we need to do is add some strings to the end of our argument list to each of calls to on :

be_helpful/db_backup/bin/db_backup.rb

opts.on('-i','--iteration',

*

'Indicate that this backup is an "iteration" backup') do

options[:iteration] = true

end

opts.on('-u USER',

*

'Database username, in first.last format',

/^[^.]+\.[^.]+$/) do |user|

options[:user] = user

end

opts.on('-p PASSWORD',

*

'Database password') do |password|

options[:password] = password

end

That’s all there is to it—not bad for about thirty seconds of coding! Next, we need to document that our app takes the name of the database to back up as an argument.

Documenting Command-Line Arguments

OptionParser provides no way to explicitly document the arguments that a command-line app accepts or requires. You’ll note that OptionParser does, however, display an invocation template as its first line of help text (Usage: db_backup.rb [options]). This is called the banner and is the perfect place to document our app’s arguments. We’d like to append a description of our app’s argument to OptionParser’s banner so that our help screen looks like so:

$ bin/db_backup.rb -h

*

Usage: db_backup.rb [options] database_name

-i, --iteration Indicate that this backup is an "iteration" backup

-u USER Database username, in first.last format

-p PASSWORD Database password

Did you notice that the string database_name now appears in the highlighted line? This is just enough information to tell the user that we require an argument and that it should be the name of the database. OptionParser has a property, banner , that we can set to accomplish this. Since our app currently doesn’t set the banner, we get the default that we saw previously. Unfortunately, we cannot directly access this string and tack on database_name, so we’ll have to re-create it ourselves.

The other tricky bit is that we don’t want to hard-code the name of our app in the banner. If we did, we’d have to update our documentation if we chose to rename our app.

Fortunately, Ruby provides an answer. When an app runs, Ruby sets the global variable $PROGRAM_NAME to the full path name of the app’s executable , which is the name of the physical file on disk that the operating system uses to run our app. The filename (without the full path) is the name of our app and what the user will type on the command line to run it, so we want to show only that.

Ruby’s File class has a handy method named basename that will give us just the name of the file of our executable, without the path to it, which is exactly what we need to create our banner.

be_helpful/db_backup/bin/db_backup.rb

option_parser = OptionParser.new do |opts|

executable_name = File.basename($PROGRAM_NAME)

opts.banner = "Usage: #{executable_name} [options] database_name"

Now the user can easily see that our app requires one argument: the name of the database to back up. Note that we are using an underscore notation here; if we had written “database name” instead (using a space between the two words), a user might misinterpret the words as calling for two arguments, one called “database” and another called “name.”

It’s hard to think of adding one string to our app’s help text as “documentation,” but for apps as straightforward as ours, this is sufficient. The user knows that db_backup.rb backs up a database, and the string database_name is all the user needs in order to know that our argument is the name of the database to back up.

Some apps have more complex arguments, and we’ll see later how we can bundle more detailed documentation with our app to explain them.

The last thing we need to do is to provide a brief summary of the purpose of our app so that occasional users can get a quick reminder of what it does.

Adding a Brief Description for a Command-Line Application

A user who has just installed our app will certainly remember its purpose, but someone running it weeks or months from now might not. Although it’s not hard to guess that an app named db_backup backs up a database, occasional users might not recall that it’s only for backing up MySQL databases and won’t work on, say, an Oracle database. To be helpful to these users, db_backup.rb --help should include a brief summary of the app’s purpose. This should be the first thing the user sees when asking for help, like so:

$ bin/db_backup.rb -h

*

Backup one or more MySQL databases

Usage: db_backup.rb [options] database_name

-i, --iteration Indicate that this backup is an "iteration" backup

-u USER Database username, in first.last format

-p PASSWORD Database password

Like the usage statement, OptionParser doesn’t provide a place to explicitly document our app’s purpose, but we can add it to the banner, just like we did when we documented its arguments. Since the banner is going to be multiline, we can format it directly in our source using multiple lines (instead of putting control characters like \n in a single-line string) so that the banner text is easy to read and modify:

be_helpful/db_backup/bin/db_backup.rb

option_parser = OptionParser.new do |opts|

executable_name = File.basename($PROGRAM_NAME)

opts.banner = "Backup one or more MySQL databases

Usage: #{executable_name} [options] database_name

"

You might be tempted to add more documentation to the banner, but this is not what the banner is for. The banner should be brief and to the point, designed as reference. We’ll see later in this chapter how we can provide more detailed help and examples.

Now that we’ve fully documented what our app does, how to invoke it, and what options are available, db_backup.rb seems pretty darn helpful. There’s only one thing left to consider: what if the user executes db_backup.rb but omits the required argument, the database name?

We mentioned earlier that experienced command-line users might do this on purpose, as a way to get a help statement. The user could also do this by accident, forgetting to provide a database name. No matter what the user’s intent might be, our app behaves the same: unhelpfully. It will likely generate an exception or, worse, fail silently.

In cases like this, where you don’t know whether the user made a mistake or is just looking for help, you should cover both bases and provide an error message, followed by the help text. Let’s see how to do this by looking at db_backup.rb.

Ruby places all command-line arguments in an array called ARGV, which OptionParser modifies when parse! is called. OptionParser’s modification to ARGV is to remove all the options and arguments it knows about. What’s left in ARGV are the unparsed arguments, which you can safely treat as the arguments the user provided on the command line. Unrecognized switches and flags will cause OptionParser to print an error and exit your app, so you’ll never find them in ARGV.

All we need to do to detect this “request for help or erroneous invocation” situation is check that ARGV is empty after having OptionParser parse the command line, as shown in the following code:

be_helpful/db_backup/bin/db_backup.rb

option_parser.parse!

*

if ARGV.empty?

puts "error: you must supply a database_name"

puts

puts option_parser.help

else

database_name = ARGV[0]

# proceed as normal to backup database_name

end

Now db_backup.rb is as helpful as it can be:

$ db_backup.rb

*

error: you must supply a database name

Backup one or more MySQL databases

Usage: db_backup.rb [options] database_name

-i, --iteration Indicate that this backup is an "iteration" backup

-u USER Database username, in first.last format

-p PASSWORD Database password

We’ve seen how easy it is to make a helpful user interface for simple command-line apps using OptionParser, but what about command suites? It’s doubly important to provide a helpful user interface, because a command suite is naturally more complex. In the next section, we’ll see how to do that by enhancing our to-do list app todo.

3.2 Documenting a Command Suite

Since command suites like todo are more complex than simpler command-line apps like db_backup.rb, it’s important that we have documentation and that it’s easy to access. Users need to know not only what each option does and what the arguments mean but also what commands are available and what they do. The best way to provide this information is via a two-level help system. At the top “level,” we see the “banner”-type information, the global options, the list of commands, and what each command does. This information should be provided when the app is invoked with no arguments or when invoked with the command help, like so:

$ bin/todo help

NAME

todo -

SYNOPSIS

todo [global options] command [command options] [arguments...]

GLOBAL OPTIONS

-f, --filename=todo_file - Path to the todo file (default: ~/.todo.txt)

--help - Show this message

COMMANDS

done - Complete a task

help - Shows a list of commands or help for one command

list - List tasks

new - Create a new task in the task list

The second “level” is where help on a particular command is displayed. This type of help can include more detail about what the command does and should also document the command-specific options and arguments. Users should be able to access this using the command-suite’s help command, giving the command name as an argument, like so:

$ bin/todo help new

NAME

new - Create a new task in the task list

SYNOPSIS

todo [global options] new [command options] task_name

DESCRIPTION

A task has a name and a priority. By default, new tasks have

the lowest possible priority, though this can be overridden.

COMMAND OPTIONS

-f - put the new task first in the list

-p priority - set the priority of the new task, 1 being

the highest (default: none)

This may sound complex; however, open source libraries like GLI actually make this quite simple. Apps that use GLI, like todo, include a help command by default, which provides the two-level help system we just described. We can see this in action by running our todo app right now:

$ bin/todo help

NAME

todo -

SYNOPSIS

todo [global options] command [command options] [arguments...]

GLOBAL OPTIONS

-f, --filename=arg -

--help - Show this message

COMMANDS

done -

help - Shows a list of commands or help for one command

list -

new -

$ bin/todo help new

new

Like OptionParser, GLI provides the scaffolding and support for the help system and even formats everything for us; we just need to provide the help text for the global options, the commands, their options, and their arguments. This is done in GLI via three methods:

desc

Provides a short, one-line summary of a command or option

long_desc

Provides a more detailed explanation of a command or option (later, we’ll talk about the difference between this and the shorter summary you’d put in desc )

arg_name

Gives the argument to a command or flag a short, descriptive name

Once we fill in our app using these methods, our help system will look just like the one shown at the start of this section. Here’s what the new command’s implementation looks like when fully documented using these methods:

be_helpful/todo/bin/todo

*

desc 'Path to the todo file'

flag [:f,:filename]

*

desc 'Create a new task in the task list'

*

long_desc "

*

A task has a name and a priority. By default, new

*

tasks have the lowest possible priority, though

*

this can be overridden.

*

"

*

arg_name 'task_name'

command :new do |c|

*

c.desc 'set the priority of the new task, 1 being the highest'

*

c.arg_name 'priority'

c.flag :p

*

c.desc 'put the new task first in the list'

c.switch :f

c.action do |global_options,options,args|

end

end

As you can see, we call desc , long_desc , and arg_name before the element they document. This is exactly how Rake works (and also how we document our code; documentation comments appear before the code they document). This keeps our app’s code very readable and maintainable.

Now that we’ve filled this in, our app comes alive with an easy-to-use help system:

$ bin/todo help

NAME

todo -

SYNOPSIS

todo [global options] command [command options] [arguments...]

GLOBAL OPTIONS

-f, --filename=todo_file - Path to the todo file (default: ~/.todo.txt)

--help - Show this message

COMMANDS

done - Complete a task

help - Shows a list of commands or help for one command

list - List tasks

new - Create a new task in the task list

$ bin/todo help new

NAME

new - Create a new task in the task list

SYNOPSIS

todo [global options] new [command options] task_name

DESCRIPTION

A task has a name and a priority. By default, new tasks have

the lowest possible priority, though this can be overridden.

COMMAND OPTIONS

-f - put the new task first in the list

-p priority - set the priority of the new task, 1 being

the highest (default: none)

One last thing that’s worth pointing out is the documentation for the global flag, -f. You’ll note that our documentation string includes (default: ~/.todo.txt). We didn’t include that in the string given to desc; it’s an additional bit of documentation the GLI derives for us when we use the default_value method to indicate the default value for a flag.

be_helpful/todo/bin/todo

desc "Path to the todo file"

arg_name "todo_file"

*

default_value "~/.todo.txt"

flag [:f,:filename]

default_value isn’t actually for documentation; it allows us to specify the value for a flag when the user omits it from the command line; this means that the value of global_options[:f] will not be nil; it will be ~/.todo.txt if the user omits -f on the command line. GLI helpfully includes this in our help text, meaning our documentation and our code will always be consistent.

We’ve now learned how easy it is to provide help documentation for simple command-line apps and command suites. By adding a few extra strings to our code, our apps can easily help users understand what the apps do and how to use them. But not all apps are so simple. Command-line apps often provide sophisticated behavior that can’t be easily explained in the one or two lines of text available in the built-in help systems. How can we provide detailed documentation beyond simple help text?

3.3 Including a Man Page

As we’ve seen, it’s easy to document the options, arguments, and commands of a command-line app. This information, and the ability to access it from the app itself, is invaluable to repeat users of your app; they can quickly find out how to use your app the way they need to get their work done. What if we need more? Perhaps we’d like some longer examples for new users, or perhaps our app is sufficiently complex that we need more space to explain things.

Even a straightforward app like db_backup.rb can benefit from a few examples and some detailed documentation (such as an explanation of the “iteration backup” concept or why the username must be in first.last format). There isn’t enough space in the built-in help provided by OptionParser for this information. Furthermore, these are not details that a regular user will need. Frequent users will just want the usage statement and options reference via --help and won’t need tutorials, examples, or detailed documentation when they just need to get a list of options.

A traditional UNIX app provides this detailed information in a manual , or man , page, which users access via the man command. If you type man ls on the command line, you’ll see a nice, detailed explanation of the ls command. However, although you could bundle a man page with your Ruby command-line app, man wouldn’t be able to access it easily because of the way RubyGems installs apps (we’ll talk more about RubyGems in Chapter 7, Distribute Painlessly). Even if man could access your app’s files, creating a man page is no small feat; it requires using the nroff[21]format, which is cumbersome to use for writing documentation.

Fortunately, the Ruby ecosystem of open source libraries has us covered. gem-man,[22] a plug-in to RubyGems created by GitHub’s Chris Wanstrath, allows users to access man pages bundled inside a gem via the gem man command. ronn[23] is a Ruby app that allows us to create man pages in plain text, without having to learn nroff. We can use these two tools together to create a manual page that we can easily distribute with our app and that will be easily accessible to our users.

Once we’ve installed these tools, created our man page, and distributed our app to users, they’ll be able to read whatever detailed documentation we’ve provided like so:

$ gem man db_backup

DB_BACKUP.RB(1) DB_BACKUP.RB(1)

NAME

db_backup.rb - backup one or more MySQL databases

SYNOPSIS

db_backup.rb database_name

db_backup.rb -u username -p password database_name

db_backup.rb -i|--iteration database_name

etc....

Installing Man Page Tools

Installing gem-man and ronn is straightforward using RubyGems’ gem command:

$ gem install gem-man ronn

Successfully installed gem-man-0.2.0

Building native extensions. This could take a while...

Building native extensions. This could take a while...

Successfully installed hpricot-0.8.4

Successfully installed rdiscount-1.6.8

Successfully installed mustache-0.99.4

Successfully installed ronn-0.7.3

5 gems installed

The extra gems installed are gems needed by ronn (we’ll talk about runtime dependencies later in Chapter 7, Distribute Painlessly).

Now that we have our libraries and tools installed, we need to set up a location for our man page’s source to live in our project. By convention, this location is a directory called man, and our source file is named APP_NAME.1.ronn (where APP_NAME is the name of our app).

$ mkdir man

$ touch man/db_backup.1.ronn

Although the directory man is just a convention, the .1 in our filename is required. This number represents the “section” of the manual where our man page will live. The UNIX manual has several sections, and section 1 is for command-line executables.[24] The other part of the name (db_backup) is the name users will use to read our app’s manual page. Technically we could call it something else, like foobar, but then our users would need to run gem man foobar instead of gem man db_backup. So, we use the name of our app as the base of the filename.

Now that we have all the pieces in place, let’s create our man page.

Creating a Man Page with ronn

We said earlier that ronn allows us to write a man page in plain text, without having to use nroff. This is only partially true. What ronn really does is allow us to use the plain-text format Markdown[25] to write our man page.

Markdown text looks like plain text but actually follows some lightweight conventions for formatting lists, calling out sections, and creating hyperlinks. It’s much simpler than HTML and a lot easier to create than nroff. The ronn-format documentation[26] provides a comprehensive reference for the Markdown syntax relevant to a man page. Text formatted in Markdown is actually quite simple, so let’s take a look at some.

Here’s what a man page for db_backup.rb looks like:

be_helpful/db_backup/man/db_backup.1.ronn

db_backup.rb(1) -- backup one or more MySQL databases

=====================================================

## SYNOPSIS

`db_backup.rb` <database_name><br>

`db_backup.rb` `-u username` `-p password` <database_name><br>

`db_backup.rb` `-i`|`--iteration` <database_name>

## DESCRIPTION

**db_backup.rb** is a simple command-line tool for backing up a

MySQL database. It does so safely and quietly, using a sensible

name for the backup files, so it's perfect for use with cron as

a daily backup.

By default, `db_backup.rb` makes a daily backup and names the

resulting backup file with the date. `db_backup.rb` also

understands our development process, so if you specify the

`--iteration` flag, the backup will be named differently than

for a daily backup. This will allow you to easily keep one

backup per iteration, easily identifying it, and differentiate

it from daily backups.

By default, `db_backup.rb` will use your database credentials

in `~/.my.cnf`, however, you can override either the username

or password (or both) via the `-u` and `-p` flags, respectively.

Finally, `db_backup.rb` will add a sanity check on your username, to

make sure it fits with our corporate standard format of `first.last`.

## FILES

`~/.my.cnf` is used for authentication if `-u` or `-p` is omitted.

## OPTIONS

* `-i`, `--iteration`:

Indicate that this backup is an "end of iteration" backup.

* `-u USER`:

Database username, in first.last format

`~/my.cnf` is not correct

* `-p PASSWORD`:

Database password

## EXAMPLES

Backup the database "big_client"

$ db_backup.rb big_client

Backup the database "small_client", for which different credentials are required:

$ db_backup.rb -u dave -p d4v3 small_client

Make an iteration backup of the "big_client" database:

$ db_backup.rb -i big_client

The formatting reads very well just as plain text, but the Markdown format tells ronn things like this:

· ## marks the beginning of a new section.

· A string like **db_backup.rb** should be displayed in bold.

· Paragraphs preceded by asterisks are a bullet list.

Content-wise, we’ve replicated some of the information from our code to OptionParser, and we’ve expanded on a few topics so that a newcomer has a lot more information about how things work. We’ve also taken advantage of the standard sections that might appear in a man page so that experienced users can quickly jump to the section they are interested in. We’ll talk about what sections you might want to include on a man page in the final part of this chapter.

To actually generate our man page from the Markdown source, we use ronn as follows:

$ ronn man/db_backup.1.ronn

roff: man/db_backup.1

html: man/db_backup.1.html

ronn also generates an HTML version suitable for including on your app’s website. To preview our man page as command-line users will see it, we can use the UNIX man command on the nroff file generated by ronn:

$ man man/db_backup.1

DB_BACKUP.RB(1) DB_BACKUP.RB(1)

NAME

db_backup.rb - backup one or more MySQL databases

SYNOPSIS

db_backup.rb database_name

db_backup.rb -u username -p password database_name

db_backup.rb -i|--iteration database_name

We’ve omitted most of the man page content for brevity, but you can see that it’s nicely formatted like any other UNIX man page. To have this man page distributed with our app, we’ll need to learn more about RubyGems, which we’ll do later in Chapter 7, Distribute Painlessly. For now, we’ll just tell you that if you include this file in your gem and another user installs your app via RubyGems, users will be able to read your man page right from the command line.[27]

$ gem man db_backup

DB_BACKUP.RB(1) DB_BACKUP.RB(1)

NAME

db_backup.rb - backup one or more MySQL databases

SYNOPSIS

db_backup.rb database_name

db_backup.rb -u username -p password database_name

db_backup.rb -i|--iteration database_name

This, combined with the great built-in help that OptionParser or GLI gives you, will ensure that your app is helpful to all users, allowing them to easily and quickly understand how to use your app. You’ll be free to focus on what your app does instead of formatting and maintaining documentation.

We now know the nuts and bolts of creating help and documentation, but it’s worth having a brief discussion on style. There remain a few aspects of help that are “fuzzy” but nevertheless important, and knowledge of a few more documentation conventions will help you write great documentation without being too verbose.

3.4 Writing Good Help Text and Documentation

That your app has any help text at all is great and puts it, sadly, ahead of many apps in terms of ease of use and user friendliness. We don’t want to be merely great; we want to be awesome, so it’s important that our help text and documentation be clear, concise, accurate, and useful. We’re not going to get into the theory of written communication, but there are a few rules of thumb, as well as some formatting conventions, that will help elevate our help text and documentation.

In general, your in-app help documentation should serve as a concise reference. The only portion of the in-app help that needs to be instructive to a newcomer is the “banner,” that is, the one-sentence description of your program. Everything else should be geared toward allowing regular users of your program to remember what options there are and what they do.

Anything else should go into your man page and should include information useful to a newcomers (particularly the “DESCRIPTION” and “EXAMPLE” sections), examples, and more in-depth information for advanced users who want to dig deeper.

Let’s walk through each element of our app’s documentation and discuss how best to write it.

Documenting an App’s Description and Invocation Syntax

The first thing a user will expect to see is the banner, which, in the case of db_backup.rb, contains a one-line description of the app, along with its invocation syntax. This description should be one very short sentence that sums up what the app does. If it’s longer than sixty characters, it’s probably too long, and you should try to summarize it better. (The number sixty is based on a standard terminal width of eighty characters; the difference of twenty characters gives you plenty of breathing room for the name of the app and some whitespace, but in general it forces you to be concise, which is a good thing.)

The invocation syntax or “usage” should follow a fairly strict format. For non-command-suite apps, it will be as follows:

executable [options] arg_name1 arg_name2

where «executable» is the name of your executable and «arg_name1» and «arg_name2» are the names of your arguments. Note that [options] should be included only if your app takes options; omit it if it doesn’t.

For a command suite, the format is very similar; however, you need to account for the placement of the command and the differentiation of global and command-specific options. GLI’s behavior here is what you want:

executable [global options] command [command options] arg_name1 arg_name2

Here, «command» is the command being executed. Much like a simple command-line app, you should omit [global options] if your command suite doesn’t take global options and omit [command options] if the particular command doesn’t take command-specific options.

In other cases, the arguments’ names should be brief, with multiple words separated by underscores. If your app requires a lot of arguments, this may be an indicator that you have not designed your UI humanely; you should consider turning those arguments into flags.

If your app takes multiple arguments of the same type (such as a list of files on which to operate), use an ellipsis, like so: arg_name....

The ellipsis is a common indicator that one or more arguments of the same type may be passed, so, in the case of our database backup app, since we accept multiple database names, we should use database_name... to indicate this.

You also might be wondering why the options are documented using square brackets. This is because options are optional, and the UNIX convention is to show optional elements in square brackets. If you recall from Table 1, Overview of OptionParser parameters to on , a string like "-n [NAME]" indicates to OptionParser that the argument NAME to the flag -n is optional. The reason OptionParser uses square brackets is because of this convention.

It might be possible that your app requires certain options to always be set on the command line; this is discouraged and will be discussed in more detail in Chapter 5, Delight Casual Users.

You can use the square-bracket syntax for a command’s arguments as well. You should use [file_name] to indicate one file can be specified but that it isn’t required, or you can use [file_name...] to indicate zero or more files would be accepted.

Documenting Options

Like the summary description of an app, one brief summary sentence should be sufficient to document each of its flags and switches. Each sentence should be as clear and to the point as possible. Again, sixty characters is a good limit to set, though it might be harder to hit for more complex options.

An argument to a flag should have a short, one-word description (or, if using multiple words, each separated with an underscore). If a flag has a default value, provide that value in the help string as well (we saw this in the documentation of the global flag -f that todo uses to locate the task list file). As with arguments for your app, if the argument to your flag is optional, surround its name with square brackets.

OptionParser and GLI more or less handle the formatting part for you, so you mainly need to focus on keeping your descriptions brief and to the point.

Documenting Commands in a Command Suite

Commands for a command suite require two bits of documentation: a one-line summary for the first level of help (e.g., the help shown by a command such as your_app help) and a longer description for the second level of help (e.g., from your_app help command_name).

As you might be able to guess by now, the one-line summary should be a very short description of what that command does; you can elaborate in the longer description. The GLI desc and long_desc methods provide a place for this documentation.

Command-specific arguments should follow the same conventions as those we discussed for the app’s usage statement. The arg_name method in GLI provides a place to do this per-command.

Documenting Everything Else

An app’s man page (or other extra documentation) provides more detail about how the app works and how its various options and arguments affect its behavior. This document should be sorted into sections, which will help to keep it organized and navigable (experienced UNIX users will look for certain section names so they can quickly scan the documentation). Table 2, Common sections for your gem-man pages outlines the common sections you might need and what goes in them (though you aren’t limited to these sections; use your best judgment). Note that they are also listed in the order that they should appear in the man page and that you should include, at the very least, a “SYNOPSIS,” “DESCRIPTION,” and “EXAMPLE.”


Table 2. Common sections for your gem-man pages

Section

Meaning

SYNOPSIS

A brief synopsis of how to invoke your app on the command line. This should be similar to what the default banner is in OptionParser or what is output by GLI after the “Usage:” bit.

DESCRIPTION

A longer description of what your app does, why the user might use it, and any additional details. This section should be targeted at new users and written to help them understand how to use your app.

OPTIONS

A bullet list documenting each option. This is a chance to explain in more detail how the options work and what effect they have on your app’s behavior.

EXAMPLES

One or more examples of using the app, including brief text explaining each example.

FILES

A list of files on the filesystem that the app uses (for example, todo would document the default to-do list file’s location here).

ENVIRONMENT

A bullet list of any environment variables that affect the app’s behavior.

BUGS

Known bugs or odd behaviors.

LICENSE

The name of the license and a reference to the full license text (you do not need to reproduce the entire license here).

AUTHOR

The authors of your app and their email addresses.

COPYRIGHT

The copyright information for your app.

SEE ALSO

Links to other commands or places on the Web that are relevant for your app.


You should reuse documentation from your command-line interface here; typically your one-line summaries will be the first sentence of the paragraph that documents something. This saves you time, and it also helps connect the built-in help text to the more detailed documentation. Unfortunately, neither OptionParser nor GLI provide a way to autogenerate a man page so that your documentation can automatically be kept consistent. Perhaps you’ll be inspired and create such a system.

3.5 Moving On

You should now know everything you need to know to make a command-line application that’s helpful to newcomers as well as experts. All this is good news for your users; they’ll have an easy time using your apps, but it’s also good news for you as the developer; you can spend more time on your apps’ actual functionality and less on formatting help text and documentation.

Users aren’t the only entities who interact with your application, however. The system itself will be actually executing your application, and future developers may need to integrate your command-line apps into larger systems of automation (similar to how our database backup script integrates mysqldump). In the next chapter, we’ll talk about how to make your apps interoperate with the system and with other applications.

Footnotes

[20]

http://en.wikipedia.org/wiki/Principle_of_least_astonishment

[21]

http://en.wikipedia.org/wiki/Nroff

[22]

http://defunkt.io/gem-man/

[23]

http://rtomayko.github.com/ronn/

[24]

The Wikipedia entry for the UNIX man system ( http://en.wikipedia.org/wiki/Man_page#Manual_sections ) has a good overview of the other sections if you are interested.

[25]

http://daringfireball.net/projects/markdown/

[26]

http://rtomayko.github.com/ronn/ronn-format.7.html

[27]

Savvy users can alias man to be gem man -s, which tells gem-man to use the system manual for any command it doesn’t know, thus providing one unified interface to the system manual and the manual of installed Ruby command-line apps.