We put rdoc stuff in the RDoc module to avoid namespace clutter.
ToDo: This isn‘t universally true.
This package contains Rdoc and SimpleMarkup. Rdoc is an application that produces documentation for one or more Ruby source files. We work similarly to JavaDoc, parsing the source, and extracting the definition for classes, modules, and methods (along with includes and requires). We associate with these optional documentation contained in the immediately preceding comment block, and then render the result using a pluggable output formatter. (Currently, HTML is the only supported format. Markup is a library that converts plain text into various output formats. The Markup library is used to interpret the comment blocks that Rdoc uses to document methods, classes, and so on.
This library contains two packages, rdoc itself and a text markup library, ‘markup’.
Once installed, you can create documentation using the ‘rdoc’ command (the command is ‘rdoc.bat’ under Windows)
% rdoc [options] [names...]
Type "rdoc —help" for an up-to-date option summary.
A typical use might be to generate documentation for a package of Ruby source (such as rdoc itself).
% rdoc
This command generates documentation for all the Ruby and C source files in and below the current directory. These will be stored in a documentation tree starting in the subdirectory ‘doc’.
You can make this slightly more useful for your readers by having the index page contain the documentation for the primary file. In our case, we could type
% rdoc --main rdoc/rdoc.rb
You‘ll find information on the various formatting tricks you can use in comment blocks in the documentation this generates.
RDoc uses file extensions to determine how to process each file. File names ending .rb and .rbw are assumed to be Ruby source. Files ending .c are parsed as C files. All other files are assumed to contain just SimpleMarkup-style markup (with or without leading ’#’ comment markers). If directory names are passed to RDoc, they are scanned recursively for C and Ruby source files only.
RDoc is Copyright (c) 2001-2003 Dave Thomas, The Pragmatic Programmers. It is free software, and may be redistributed under the terms specified in the README file of the Ruby distribution.
RDoc is invoked from the command line using:
% rdoc <options> [name...]
Files are parsed, and the information they contain collected, before any output is produced. This allows cross references between all files to be resolved. If a name is a directory, it is traversed. If no names are specified, all Ruby files in the current directory (and subdirectories) are processed.
Options are:
db_opt :name, :age
will get parsed and displayed in the documentation. Each name may have an optional "=flagtext" appended, in which case the given flagtext will appear where (for example) the ‘rw’ appears for attr_accessor.
A typical small Ruby program commented using RDoc might be as follows. You can see the formatted result in EXAMPLE.rb and Anagram.
:include: EXAMPLE.rb
Comment blocks can be written fairly naturally, either using ’#’ on successive lines of the comment, or by including the comment in an =begin/=end block. If you use the latter form, the =begin line must be flagged with an RDoc tag:
=begin rdoc Documentation to be processed by RDoc. =end
Paragraphs are lines that share the left margin. Text indented past this margin are formatted verbatim.
For example, the input that produced the above paragraph looked like
1. Lists are typed as indented
paragraphs with:
* a '*' or '-' (for bullet lists)
* a digit followed by a period for
numbered lists
* an upper or lower case letter followed
by a period for alpha lists.
[cat] small domestic animal [+cat+] command to copy standard input
cat:: small domestic animal +cat+:: command to copy standard input
For both kinds of labeled lists, if the body text starts on the same line as the label, then the start of that text determines the block indent for the rest of the body. The text may also start on the line following the label, indented from the start of the label. This is often preferable if the label is long. Both the following are valid labeled list entries:
<tt>--output</tt> <i>name [, name]</i>::
specify the name of one or more output files. If multiple
files are present, the first is used as the index.
<tt>--quiet:</tt>:: do not output the names, sizes, byte counts,
index areas, or bit ratios of units as
they are processed.
= Level One Heading == Level Two Heading
and so on
| italic: | _word_ or <em>text</em> |
| bold: | *word* or <b>text</b> |
| typewriter: | +word+ or <tt>text</tt> |
The first form only works around ‘words’, where a word is a sequence of upper and lower case letters and underscores. Putting a backslash before inline markup stops it being interpreted, which is how I created the table above:
_italic_:: \_word_ or \<em>text</em> *bold*:: \*word* or \<b>text</b> +typewriter+:: \+word+ or \<tt>text</tt>
Hyperlinks can also be of the form label[url], in which case the label is used in the displayed text, and url is used as the target. If label contains multiple words, put it in braces: {multi word label}[url].
def fred
...
yield line, address
This will get documented as
fred() { |line, address| ... }
You can override this using a comment containing ’:yields: …’ immediately after the method definition
def fred # :yields: index, position
...
yield line, address
which will get documented as
fred() { |index, position| ... }
module SM #:nodoc:
class Input
end
end
module Markup #:nodoc: all
class Output
end
end
In the above code, only class SM::Input will be documented.
# Extract the age and calculate the
# date-of-birth.
#--
# FIXME: fails if the birthday falls on
# February 29th
#++
# The DOB is returned as a Time object.
def get_dob(person)
...
<tt>:section:</tt> is used as the section heading, and the
remainder of the comment containing the section is used as
introductory text. Subsequent methods, aliases, attributes,
and classes will be documented in this section. A :section:
comment block may have one or more lines before the :section:
directive. These will be removed, and any identical lines at
the end of the block are also removed. This allows you to add
visual cues such as
# ----------------------------------------
# :section: My Section
# This is the section that I wrote.
# See it glisten in the noon-day sun.
# ----------------------------------------
See also markup/simple_markup.rb.
| Author: | Dave Thomas <dave@pragmaticprogrammer.com> |
| Requires: | Ruby 1.8.1 or later |
| License: | Copyright (c) 2001-2003 Dave Thomas. Released under the same license as Ruby. |
This software is provided "as is" and without any express or implied warranties, including, without limitation, the implied warranties of merchantibility and fitness for a particular purpose.
| GENERAL_MODIFIERS | = | [ 'nodoc' ].freeze | ||
| CLASS_MODIFIERS | = | GENERAL_MODIFIERS | ||
| ATTR_MODIFIERS | = | GENERAL_MODIFIERS | ||
| CONSTANT_MODIFIERS | = | GENERAL_MODIFIERS | ||
| METHOD_MODIFIERS | = | GENERAL_MODIFIERS + [ 'arg', 'args', 'yield', 'yields', 'notnew', 'not-new', 'not_new', 'doc' ] | ||
| KNOWN_CLASSES | = | { "rb_cObject" => "Object", "rb_cArray" => "Array", "rb_cBignum" => "Bignum", "rb_cClass" => "Class", "rb_cDir" => "Dir", "rb_cData" => "Data", "rb_cFalseClass" => "FalseClass", "rb_cFile" => "File", "rb_cFixnum" => "Fixnum", "rb_cFloat" => "Float", "rb_cHash" => "Hash", "rb_cInteger" => "Integer", "rb_cIO" => "IO", "rb_cModule" => "Module", "rb_cNilClass" => "NilClass", "rb_cNumeric" => "Numeric", "rb_cProc" => "Proc", "rb_cRange" => "Range", "rb_cRegexp" => "Regexp", "rb_cString" => "String", "rb_cSymbol" => "Symbol", "rb_cThread" => "Thread", "rb_cTime" => "Time", "rb_cTrueClass" => "TrueClass", "rb_cStruct" => "Struct", "rb_eException" => "Exception", "rb_eStandardError" => "StandardError", "rb_eSystemExit" => "SystemExit", "rb_eInterrupt" => "Interrupt", "rb_eSignal" => "Signal", "rb_eFatal" => "Fatal", "rb_eArgError" => "ArgError", "rb_eEOFError" => "EOFError", "rb_eIndexError" => "IndexError", "rb_eRangeError" => "RangeError", "rb_eIOError" => "IOError", "rb_eRuntimeError" => "RuntimeError", "rb_eSecurityError" => "SecurityError", "rb_eSystemCallError" => "SystemCallError", "rb_eTypeError" => "TypeError", "rb_eZeroDivError" => "ZeroDivError", "rb_eNotImpError" => "NotImpError", "rb_eNoMemError" => "NoMemError", "rb_eFloatDomainError" => "FloatDomainError", "rb_eScriptError" => "ScriptError", "rb_eNameError" => "NameError", "rb_eSyntaxError" => "SyntaxError", "rb_eLoadError" => "LoadError", "rb_mKernel" => "Kernel", "rb_mComparable" => "Comparable", "rb_mEnumerable" => "Enumerable", "rb_mPrecision" => "Precision", "rb_mErrno" => "Errno", "rb_mFileTest" => "FileTest", "rb_mGC" => "GC", "rb_mMath" => "Math", "rb_mProcess" => "Process" | Ruby‘s built-in classes. | |
| DOT_DOC_FILENAME | = | ".document" | Name of the dotfile that contains the description of files to be processed in the current directory |
Display usage information from the comment at the top of the file. String arguments identify specific sections of the comment to display. An optional integer first argument specifies the exit status (defaults to 0)
# File lib/rdoc/usage.rb, line 81
81: def RDoc.usage(*args)
82: exit_code = 0
83:
84: if args.size > 0
85: status = args[0]
86: if status.respond_to?(:to_int)
87: exit_code = status.to_int
88: args.shift
89: end
90: end
91:
92: # display the usage and exit with the given code
93: usage_no_exit(*args)
94: exit(exit_code)
95: end
Display usage
# File lib/rdoc/usage.rb, line 98
98: def RDoc.usage_no_exit(*args)
99: main_program_file = caller[-1].sub(/:\d+$/, '')
100: comment = File.open(main_program_file) do |file|
101: find_comment(file)
102: end
103:
104: comment = comment.gsub(/^\s*#/, '')
105:
106: markup = SM::SimpleMarkup.new
107: flow_convertor = SM::ToFlow.new
108:
109: flow = markup.convert(comment, flow_convertor)
110:
111: format = "plain"
112:
113: unless args.empty?
114: flow = extract_sections(flow, args)
115: end
116:
117: options = RI::Options.instance
118: if args = ENV["RI"]
119: options.parse(args.split)
120: end
121: formatter = options.formatter.new(options, "")
122: formatter.display_flow(flow)
123: end
Given an array of flow items and an array of section names, extract those sections from the flow which have headings corresponding to a section name in the list. Return them in the order of names in the sections array.
# File lib/rdoc/usage.rb, line 165
165: def RDoc.extract_sections(flow, sections)
166: result = []
167: sections.each do |name|
168: name = name.downcase
169: copy_upto_level = nil
170:
171: flow.each do |item|
172: case item
173: when SM::Flow::H
174: if copy_upto_level && item.level >= copy_upto_level
175: copy_upto_level = nil
176: else
177: if item.text.downcase == name
178: result << item
179: copy_upto_level = item.level
180: end
181: end
182: else
183: if copy_upto_level
184: result << item
185: end
186: end
187: end
188: end
189: if result.empty?
190: puts "Note to developer: requested section(s) [#{sections.join(', ')}] " +
191: "not found"
192: result = flow
193: end
194: result
195: end
# File lib/rdoc/usage.rb, line 141
141: def RDoc.find_comment(file)
142: catch(:exit) do
143: # skip leading blank lines
144: 0 while (line = gets(file)) && (line =~ /^\s*$/)
145:
146: comment = []
147: while line && line =~ /^\s*#/
148: comment << line
149: line = gets(file)
150: end
151:
152: 0 while line && (line = gets(file))
153: return no_comment if comment.empty?
154: return comment.join
155: end
156: end
Find the first comment in the file (that isn‘t a shebang line) If the file doesn‘t start with a comment, report the fact and return empty string
# File lib/rdoc/usage.rb, line 133
133: def RDoc.gets(file)
134: if (line = file.gets) && (line =~ /^#!/) # shebang
135: throw :exit, find_comment(file)
136: else
137: line
138: end
139: end