Tweak RDoc to transclude a function into another function's documentation

P

Phlip

Ruboids:

A recent post here, by Robert Dober, showed how to tweak RDoc to add a raw
HTML directive, %html.

Below my sig is a monkey-patch, for Ruby 1.8's RDoc, which includes this
tweak and adds another; the ability to insert a method's contents into
another method's documentation. This allows us to do "literate programming".
We can compete with the likes of Knuth and Sedgewick by inserting
automatically tested source code into our verbiage.

To use the monkey patch, simply save it to a file, say rdoc_patch.rb and
then add require 'rdoc_patch' inside your Rakefile. If your Rakefile is
normal, it will run RDoc in-process, so that will get the patch.

I use the %html and %transclude tags like this:

# %html <a name='assert_json' />
# Example:
#
# %transclude AssertJavaScriptTest#test_assert_json
#
def assert_json(...)
...
end

The contents of test_assert_json will explain to us how to use assert_json.
And the %html <a name=...> gives us a direct, external link to the method:

http://assertxpath.rubyforge.org/classes/AssertJavaScript.html#assert_json

I'm putting the code here because I don't think it's good enough as a direct
patch to RDoc yet. The main reasons:

- the directives escape with %, where :x: is the RDoc standard.
- both these tweaks gleefully ignore the other documentation flavors
- future directives are no easier.

The ideal system would help users easily plug new directives in, without
stitching them into each element of the Visitor Pattern.

--
Phlip
http://www.oreilly.com/catalog/9780596510657/
"Test Driven Ajax (on Rails)"
assert_xpath, assert_javascript, & assert_ajax


##################################################################
## eek eek ook ook patch to add directives to RDocage

require 'rdoc/rdoc'
require 'rdoc/markup/simple_markup'
require 'rdoc/markup/simple_markup/lines'
require 'rdoc/markup/simple_markup/fragments'
require 'rdoc/markup/simple_markup/to_html'


def find_method_contents(file_info, modool, method)
file_info.each do |top|
if mod = top.find_module_named(modool) and
symbol = mod.find_local_symbol(method)
return symbol.token_stream
end
end
return nil
end


module SM

class Line
PURE_HTML = :pURE_HTML
TRANSCLUDE = :TRANSCLUDE
end

class PureHTML < Fragment
type_name Line::pURE_HTML
end

class Transclude < Fragment
type_name Line::TRANSCLUDE
end

class ToHtml
def accept_transclude(am, fragment)
if found = find_method_contents(@context.context.parent.in_files,
*fragment.txt.split('#'))
@res << '<pre>'
# to do: hilite syntax; make method name clickable
found.each_with_index do |tok, index|
next if 0 == index and tok.text =~ /^\#/
next if 1 == index and tok.text == "\n"
@res << tok.text
end
@res << '</pre>'
else
@res << fragment.txt
end
end

def accept_pure_html(am, fragment)
@res << fragment.txt
end
end

class LineCollection
def accept(am, visitor)
visitor.start_accepting

@fragments.each do |fragment|
case fragment
when Verbatim
visitor.accept_verbatim(am, fragment)
when PureHTML
visitor.accept_pure_html(am, fragment)
when Transclude
visitor.accept_transclude(am, fragment)
when Rule
visitor.accept_rule(am, fragment)
when ListStart
visitor.accept_list_start(am, fragment)
when ListEnd
visitor.accept_list_end(am, fragment)
when ListItem
visitor.accept_list_item(am, fragment)
when BlankLine
visitor.accept_blank_line(am, fragment)
when Heading
visitor.accept_heading(am, fragment)
when Paragraph
visitor.accept_paragraph(am, fragment)
end
end

visitor.end_accepting
end

end

class SimpleMarkup
private

def assign_types_to_lines(margin = 0, level = 0)

while line = @lines.next

if /^\s*%html/ === line.text then
line.text.sub!("%html","")
line.stamp( Line::pURE_HTML, level )
next
end

if /^\s*%transclude/ === line.text then
line.text.sub!("%transclude","")
line.stamp( Line::TRANSCLUDE, level )
next
end

if line.isBlank?
line.stamp(Line::BLANK, level)
next
end

# if a line contains non-blanks before the margin, then it must
belong
# to an outer level

text = line.text

for i in 0...margin
if text != SPACE
@lines.unget
return
end
end

active_line = text[margin..-1]

# Rules (horizontal lines) look like
#
# --- (three or more hyphens)
#
# The more hyphens, the thicker the rule
#

if /^(---+)\s*$/ =~ active_line
line.stamp(Line::RULE, level, $1.length-2)
next
end

# Then look for list entries. First the ones that have to have
# text following them (* xxx, - xxx, and dd. xxx)

if SIMPLE_LIST_RE =~ active_line

offset = margin + $1.length
prefix = $2
prefix_length = prefix.length

flag = case prefix
when "*","-" then ListBase::BULLET
when /^\d/ then ListBase::NUMBER
when /^[A-Z]/ then ListBase::UPPERALPHA
when /^[a-z]/ then ListBase::LOWERALPHA
else raise "Invalid List Type: #{self.inspect}"
end

line.stamp(Line::LIST, level+1, prefix, flag)
text[margin, prefix_length] = " " * prefix_length
assign_types_to_lines(offset, level + 1)
next
end


if LABEL_LIST_RE =~ active_line
offset = margin + $1.length
prefix = $2
prefix_length = prefix.length

next if handled_labeled_list(line, level, margin, offset, prefix)
end

# Headings look like
# = Main heading
# == Second level
# === Third
#
# Headings reset the level to 0

if active_line[0] == ?= and active_line =~ /^(=+)\s*(.*)/
prefix_length = $1.length
prefix_length = 6 if prefix_length > 6
line.stamp(Line::HEADING, 0, prefix_length)
line.strip_leading(margin + prefix_length)
next
end

# If the character's a space, then we have verbatim text,
# otherwise

if active_line[0] == SPACE
line.strip_leading(margin) if margin > 0
line.stamp(Line::VERBATIM, level)
else
line.stamp(Line::pARAGRAPH, level)
end
end
end
end
end

## eek eek ook ook patch to add directives to RDocage
##################################################################
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,990
Messages
2,570,211
Members
46,796
Latest member
SteveBreed

Latest Threads

Top