P O S T M O D E R N

How to add a man page to your Ruby project

kramdown, kramdown-man, man-page, markdown, ruby

If you have ever used the git command-line utility, you will be pleasantly surprised that if you run git clone --help it automatically displays the man page for git clone instead of the usual --help output.

This blog post will show you how to add the same functionality to your Ruby command-line utility.

Introducing kramdown-man

Man pages are written in the roff typesetting markup language, which uses macro tags that look like .PP and \fB. Needless to say writing roff by hand is not fun. Instead, we will use the kramdown-man gem to generate the roff man page from a similar looking pure markdown man page.

Step 1: Add kramdown-man

Add kramdown-man to your Gemfile and run bundle install:

gem 'kramdown-man', '~> 1.0'

Step 2: Write the markdown man page

First create the man/ directory in your project.

$ mkdir man

Then write the markdown man page, which should be named like man/mycli.1.md. The number in the file name indicates the man page Section number (Section 1 is for General Commands, Section 3 is for Library Functions).

Use kramdown-man’s own man page as an example for how to structure your markdown man page. It should look roughly like this:

# mycli 1 "2024-01-01" MyCLI "User Manuals"

## NAME

mycli - Does things and stuff

## SYNOPSIS

`mycli` [*options*] *ARG1* [*ARG2*]

## DESCRIPTION

The `mycli` utility does things and stuff. Bla bla bla bla.

## ARGUMENTS

*ARG1*
: This is a required argument.

*ARG2*
: This is an optional argument

## OPTIONS

`-f`, `--flag` *VALUE*
: This is an option flag that takes a *VALUE* argument.

`-h`, `--help`
: Prints the help information for the command.

## EXAMPLES

Example command description:

    $ mycli --flag file.txt

## AUTHOR

Your Name <you@example.com>

## SEE ALSO

[bash(1)](man:bash.1) [other-man-page](other-man-page.1.md)

To preview how the markdown man page will be rendered, use the kramdown-man command:

$ kramdown-man man/mycli.1.md

Markdown Man Page Layout Explained

The first line will be used for the man page’s header and footer lines. It has the following format:

# mycli 1 "2024-01-01" MyCLI "User Manuals"
  • # mycli - The command name.
  • 1 - The section number.
  • 2024-01-01 - The date the man page is being written, in the format YYYY-MM-DD.
  • MyCLI - The project’s name.
  • "User Manuals" - The man page section name.

Sections

Man pages typically have the follow main sections:

  • NAME - The command name and a short definition.
  • SYNOPSIS - The command’s usage, showing order of arguments.
  • DESCRIPTION - A more detailed description of the command.
  • ARGUMENTS - Defines the purpose of each argument.
  • OPTIONS - Defines the purpose of each option flag and it’s usage.
  • EXAMPLES - Show common examples of running the command.
  • AUTHORS - List the authors of the command.
  • SEE ALSO - Link to other man pages.

ARGUMENT / OPTION LISTS

Argument and option definitions must be defined using markdown definition lists (hence the : before the summary) for them to be properly indented.

*ARG1*
: Definition goes here.

*ARG2*
: Definition goes here.

  Multiple paragraphs may be given.

COMMAND USAGE SYNTAX

Codespans indicate a literal word:

`mycli`

Emphasis and all uppercase indicates a required argument:

*ARG*

Square brackets around an argument indicates an optional argument.

[*ARG*]

Curly-braces with pipe separates indicates one of the arguments is required:

{*ARG1* \| *ARG2* \| *ARG3*}

To link to other man pages in your project’s man/ directory, use a regular markdown link that links to the .1.md file:

[other-man-page](other-man-page.1.md)

This will also generate a bolded man page reference which will look like other-man-page(1) in the displayed man page.

To link to other man pages that are already installed on a system, use a regular markdown link, but use a man:page-name.1 URL with the man page name and section number:

[bash(1)](man:bash.1)

This will generate a bolded man page reference which will look like bash(1) in the displayed man page.

Note: Firefox on Linux will actually recognize man: URIs and open them using Gnome’s yelp help browser.

Step 3: Add the rake task

Now that we have written the markdown man page, we need to setup a rake task to generate the roff formatted man page from the markdown man page.

Add the following code to your Rakefile:

require 'kramdown/man/task'
Kramdown::Man::Task.new

This will define a man rake task and define file dependencies between the man/*.1 output files and the man/*.1.md input files.

Step 4: Generate the man pages

To generate all man pages in the man/ directory run:

$ rake man

You can then view the generated man pages using the man command:

man ./man/mycli.1

Step 5: Add the code

In order for our CLI to automatically display the man page when the --help option is given, we will need to add this bit of code to the OptionParser’s --help option:

# The path to the man/mycli.1 generated man page
MAN_PAGE = File.join(__dir__,'..','..','..','man','mycli.1')

...

opts.on('-h','--help','Prints this kruft') do
  if $stdout.tty?
    system('man',MAN_PAGE)
  else
    puts opts
  end
  exit
end

The if $stdout.tty? check tests whether stdout is a TTY or being redirected to a file or another command. If we are running in a real TTY terminal, then display the man page. If we are not running in a real TTY terminal, then print the usual --help output. This is a polite thing to do, as users might want to view the --help output through less or might dump it to a file using --help >mycli.txt.

If you don’t want to copy/paste the above code into all of your Ruby projects, you can use the command_kit gem, which provides a CommandKit::Help::Man module that adds the same functionality to a command class.

Step 6: Package your man page

Now that we have generated our roff man pages, we will want to add them to either git or the gemspec’s files list. This way the generated roff man page will be included in the packaged gem.

If you prefer to not add the generated roff man page to git, you can manually add it to the list of files in your .gemspec file:

gem.files << 'man/mycli.1'

Then build and install your gem:

$ rake gem
$ gem install ./pkg/mycli-0.1.0.gem

Step 7: Test It!

Now your command should display it’s own man page when --help is given:

$ mycli --help

You should see something that looks like this:

screenshot of the displayed man page