Tweak RDoc to transclude a function into another function's

Ruboids:

A recent post here, by Robert D., 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

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 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 << ‘


# 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 << ‘

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[i] != 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

##################################################################