Wednesday, June 21, 2006

RDoc (from the Archive)

I originally wrote this material for IBM. It was previously published as part of an IBM DeveloperWorks Tutorial located here. IBM has been gracious enough to let me republish it here.


To find RDoc in use, you only have to look as far as the standard library. Abbrev.rb (the first library, in asciibetical order) includes a number of good examples. The block of comments shown in Listing 1 forms the documentation for the Abbrev module. Using RDoc, you can convert these comments into an HTML document fit for serving via a Web server or for browsing from the command line. Listing 2 shows the RDoc generation as viewed through ri. See the HTML output (notice that the HTML provides a link to the source code for the method(s) being described).


Listing 1. RDoc from abbrev.rb


# Calculate the set of unique abbreviations for a given set of strings.
#
#   require 'abbrev'
#   require 'pp'
#
#   pp Abbrev::abbrev(['ruby', 'rules']).sort
#
# <i>Generates:</i>
#
#   [["rub", "ruby"],
#    ["ruby", "ruby"],
#    ["rul", "rules"],
#    ["rule", "rules"],
#    ["rules", "rules"]]
#
# Also adds an +abbrev+ method to class +Array+.


Listing 2. ri output from Listing 1


---------------------------------------------------------- Class: Abbrev
     Calculate the set of unique abbreviations for a given set of
     strings.

       require 'abbrev'
       require 'pp'

       pp Abbrev::abbrev(['ruby', 'rules']).sort

     _Generates:_

       [["rub", "ruby"],
        ["ruby", "ruby"],
        ["rul", "rules"],
        ["rule", "rules"],
        ["rules", "rules"]]

     Also adds an +abbrev+ method to class +Array+.

------------------------------------------------------------------------


Instance methods:
-----------------
     abbrev

In Listing 1, you can see several kinds of markup. First, because this block of comments immediately precedes the line module Abbrev, it's treated as the documentation for the module as a whole. Second, the indented lines are treated as sample code and are displayed in a typewriter style font in HTML (no special treatment in ri, though). Third, <i>Generates</i> puts the word Generates in italics (at least, in HTML; in ri, it's shown with leading and trailing underscores). Last are the + symbols surrounding the terms abbrev and Array; these words are also treated as code. And because Array matches the format for a module or class, it's turned into a link to the Array method documentation for abbrev (in HTML).

RDoc isn't confined to comments in Ruby code; you can use it to comment C code, as well. Listings 3 and 4 show a portion of range.c and the ri output created from it. You can see more markup here: the :call-seq: keyword and the <code></code> tag. The tag is easy to understand. :call-seq: identifies the following lines (up to a blank line) as the calling sequence for the method.


Listing 3. RDoc from range.c


/*
 *  call-seq:
 *     rng === obj       =>  true or false
 *     rng.member?(val)  =>  true or false
 *     rng.include?(val) =>  true or false
 *
 *  Returns <code>true</code> if <i>obj</i> is an element of
 *  <i>rng</i>, <code>false</code> otherwise. Conveniently,
 *  <code>===</code> is the comparison operator used by
 *  <code>case</code> statements.
 *
 *     case 79
 *     when 1..50   then   print "low\n"
 *     when 51..75  then   print "medium\n"
 *     when 76..100 then   print "high\n"
 *     end
 *
 *  <em>produces:</em>

 *
 *     high
 */


Listing 4. ri output from Listing 3


-------------------------------------------------------------- Range#===
     rng === obj       =>  true or false
     rng.member?(val)  =>  true or false
     rng.include?(val) =>  true or false
------------------------------------------------------------------------
     Returns +true+ if _obj_ is an element of _rng_, +false+ otherwise.
     Conveniently, +===+ is the comparison operator used by +case+
     statements.

        case 79
        when 1..50   then   print "low\n"
        when 51..75  then   print "medium\n"
        when 76..100 then   print "high\n"
        end

     _produces:_

        high

In addition to using RDoc to comment your code, you can use it to mark up README files and the like. The markup is sparse enough that it doesn't hurt readability when unprocessed, while allowing the creation of HTML files that can be used with online documentation -- a method used by many Ruby libraries and applications, including r43 and Rake.

Putting RDoc into your code

If you want to document your Ruby code and use RDoc to do it, you need to know a few things. The following three sections discuss block commands, markup, and directives.

Block commands

You can create blocks of documentation in two ways: with # characters on successive lines of code, or with =begin and =end markers. You've seen the former already; the latter is shown in Listing 5. This listing also shows an additional pair of block commands: #++ and #--. These turn off RDoc processing and turn it back on again, respectively.


Listing 5. Block commands


# date.rb - date and time library
#
# Author: Tadayoshi Funaba 1998-2004
#
# Documentation: William Webber <william@williamwebber.com>
#
#--
# $Id: date.rb,v 2.12 2004-03-20 08:05:13+09 tadf Exp $
#++

Markup

You can use a number of symbols (they're not tags, for the most part) to mark up your documentation. You can put words into italics with _ or <em> (the former for single words, like _this_, and the latter for <em>groups of words</em>). Bold text is created with * or <b> and typewriter font with + or <tt> (all of which follow the same convention as the italic markup for single words vs. groups of words).

To create bulleted lists, begin indented lines with an * or a -. Indented lines starting with digits create numbered lists, and an indented line starting with a letter followed by a period creates an alphabetical list. You create dictionary lists, also known as description lists or labeled lists, by putting the label in square brackets or following it with a double colon. The definition (or description) can be placed on the same line as the label or on the following line.

Rules are created with three or more consecutive hyphens (---). Headings are created with one or more consecutive equals signs (=, ==, and so on) -- the more equals signs you use, the higher the level of the heading.

You can also create hyperlinks by adding the URL in square brackets after the term to use as the link: term[url]. If the link is more than one word long, it needs to be surrounded with curly braces: {multi word term}[url].

Directives

RDoc allows for a number of processing directives. Directives are words surrounded with colons, like this: :directive:. :call-seq: (seen in Listing 1) is a good example. Some of the most used are :yield:, :title:, :nodoc:, :include:, and :enddoc:.

:yield: is used to override the name of yielded objects. For example, Listing 6 shows two snippets of code. The first produces documentation like this: foo() {|first, second| ... }. The second produces this: foo() {|bar, baz| ... }.


Listing 6. The :yields: directive


def foo
 ...
 yield first, second


def foo # :yields: bar, baz
 ...
 yield first, second

:title: sets the title for the document. You use it like this: :title: The Document Title.

:nodoc: specifies that the current element won't be documented. You can give it the optional argument all, which turns off processing for all subordinate elements.

:include: inserts the contents of a file at this point in the documentation. Files are searched for in the directories listed in the --include option and in the current directory. You specify the filename like this: :include: file.

:enddoc: turns off processing for the remainder of the current element. Subordinate elements are still processed.

3 comments:

Chirantan Rajhans said...

Cool! Thanks...

Anonymous said...

Hey Pat


How do I make rdoc document comments inside a method? ...for e.g.

# Check for nodes
def check_nodes

#Wait for 5 mins
end

How can make rdoc document "Wait for 5 mins" comment... any idea?

Thanks
Rahul

Anonymous said...

Hey Pat


How do I make rdoc document comments inside a method? ...for e.g.

# Check for nodes
def check_nodes

#Wait for 5 mins
end

How can make rdoc document "Wait for 5 mins" comment... any idea?

Thanks
Rahul