summaryrefslogtreecommitdiff
path: root/lib/rdoc/template.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rdoc/template.rb')
-rw-r--r--lib/rdoc/template.rb247
1 files changed, 38 insertions, 209 deletions
diff --git a/lib/rdoc/template.rb b/lib/rdoc/template.rb
index dcbccea30e..09fe1e8d24 100644
--- a/lib/rdoc/template.rb
+++ b/lib/rdoc/template.rb
@@ -1,233 +1,62 @@
-# Cheap-n-cheerful HTML page template system. You create a
-# template containing:
-#
-# * variable names between percent signs (<tt>%fred%</tt>)
-# * blocks of repeating stuff:
-#
-# START:key
-# ... stuff
-# END:key
-#
-# You feed the code a hash. For simple variables, the values
-# are resolved directly from the hash. For blocks, the hash entry
-# corresponding to +key+ will be an array of hashes. The block will
-# be generated once for each entry. Blocks can be nested arbitrarily
-# deeply.
-#
-# The template may also contain
-#
-# IF:key
-# ... stuff
-# ENDIF:key
+require 'erb'
+
+##
+# An ERB wrapper.
#
-# _stuff_ will only be included in the output if the corresponding
-# key is set in the value hash.
+# This TemplatePage operates similarly to RDoc 1.x's TemplatePage, but uses
+# ERB instead of a custom template language.
#
-# Usage: Given a set of templates <tt>T1, T2,</tt> etc
+# Converting from a RDoc 1.x template to an RDoc 2.x template is fairly easy.
#
-# values = { "name" => "Dave", state => "TX" }
+# * %blah% becomes <%= values["blah"] %>
+# * !INCLUDE! becomes <%= template_include %>
+# * HREF:aref:name becomes <%= href values["aref"], values["name"] %>
+# * IF:blah becomes <% if values["blah"] then %>
+# * IFNOT:blah becomes <% unless values["blah"] then %>
+# * ENDIF:blah becomes <% end %>
+# * START:blah becomes <% values["blah"].each do |blah| %>
+# * END:blah becomes <% end %>
#
-# t = TemplatePage.new(T1, T2, T3)
-# File.open(name, "w") {|f| t.write_html_on(f, values)}
-# or
-# res = ''
-# t.write_html_on(res, values)
+# To make nested loops easier to convert, start by converting START statements
+# to:
#
+# <% values["blah"].each do |blah| $stderr.puts blah.keys %>
#
+# So you can see what is being used inside which loop.
class RDoc::TemplatePage
- ##########
- # A context holds a stack of key/value pairs (like a symbol
- # table). When asked to resolve a key, it first searches the top of
- # the stack, then the next level, and so on until it finds a match
- # (or runs out of entries)
-
- class Context
- def initialize
- @stack = []
- end
-
- def push(hash)
- @stack.push(hash)
- end
-
- def pop
- @stack.pop
- end
-
- # Find a scalar value, throwing an exception if not found. This
- # method is used when substituting the %xxx% constructs
-
- def find_scalar(key)
- @stack.reverse_each do |level|
- if val = level[key]
- return val unless val.kind_of? Array
- end
- end
- raise "Template error: can't find variable '#{key}'"
- end
-
- # Lookup any key in the stack of hashes
-
- def lookup(key)
- @stack.reverse_each do |level|
- val = level[key]
- return val if val
- end
- nil
- end
- end
-
- #########
- # Simple class to read lines out of a string
-
- class LineReader
- # we're initialized with an array of lines
- def initialize(lines)
- @lines = lines
- end
-
- # read the next line
- def read
- @lines.shift
- end
-
- # Return a list of lines up to the line that matches
- # a pattern. That last line is discarded.
- def read_up_to(pattern)
- res = []
- while line = read
- if pattern.match(line)
- return LineReader.new(res)
- else
- res << line
- end
- end
- raise "Missing end tag in template: #{pattern.source}"
- end
-
- # Return a copy of ourselves that can be modified without
- # affecting us
- def dup
- LineReader.new(@lines.dup)
- end
- end
-
-
-
- # +templates+ is an array of strings containing the templates.
- # We start at the first, and substitute in subsequent ones
- # where the string <tt>!INCLUDE!</tt> occurs. For example,
- # we could have the overall page template containing
- #
- # <html><body>
- # <h1>Master</h1>
- # !INCLUDE!
- # </bost></html>
- #
- # and substitute subpages in to it by passing [master, sub_page].
- # This gives us a cheap way of framing pages
+ ##
+ # Create a new TemplatePage that will use +templates+.
def initialize(*templates)
- result = "!INCLUDE!"
- templates.each do |content|
- result.sub!(/!INCLUDE!/, content)
- end
- @lines = LineReader.new(result.split($/))
- end
-
- # Render the templates into HTML, storing the result on +op+
- # using the method <tt><<</tt>. The <tt>value_hash</tt> contains
- # key/value pairs used to drive the substitution (as described above)
-
- def write_html_on(op, value_hash)
- @context = Context.new
- op << substitute_into(@lines, value_hash).tr("\000", '\\')
+ @templates = templates
end
+ ##
+ # Returns "<a href=\"#{ref}\">#{name}</a>"
- # Substitute a set of key/value pairs into the given template.
- # Keys with scalar values have them substituted directly into
- # the page. Those with array values invoke <tt>substitute_array</tt>
- # (below), which examples a block of the template once for each
- # row in the array.
- #
- # This routine also copes with the <tt>IF:</tt>_key_ directive,
- # removing chunks of the template if the corresponding key
- # does not appear in the hash, and the START: directive, which
- # loops its contents for each value in an array
-
- def substitute_into(lines, values)
- @context.push(values)
- skip_to = nil
- result = []
-
- while line = lines.read
-
- case line
-
- when /^IF:(\w+)/
- lines.read_up_to(/^ENDIF:#$1/) unless @context.lookup($1)
-
- when /^IFNOT:(\w+)/
- lines.read_up_to(/^ENDIF:#$1/) if @context.lookup($1)
-
- when /^ENDIF:/
- ;
-
- when /^START:(\w+)/
- tag = $1
- body = lines.read_up_to(/^END:#{tag}/)
- inner_values = @context.lookup(tag)
- raise "unknown tag: #{tag}" unless inner_values
- raise "not array: #{tag}" unless inner_values.kind_of?(Array)
- inner_values.each do |vals|
- result << substitute_into(body.dup, vals)
- end
- else
- result << expand_line(line.dup)
- end
+ def href(ref, name)
+ if ref then
+ "<a href=\"#{ref}\">#{name}</a>"
+ else
+ name
end
-
- @context.pop
-
- result.join("\n")
end
- # Given an individual line, we look for %xxx% constructs and
- # HREF:ref:name: constructs, substituting for each.
-
- def expand_line(line)
- # Generate a cross reference if a reference is given,
- # otherwise just fill in the name part
+ ##
+ # Process the template using +values+, writing the result to +io+.
- line.gsub!(/HREF:(\w+?):(\w+?):/) {
- ref = @context.lookup($1)
- name = @context.find_scalar($2)
+ def write_html_on(io, values)
+ template_include = ""
- if ref and !ref.kind_of?(Array)
- "<a href=\"#{ref}\">#{name}</a>"
- else
- name
- end
- }
-
- # Substitute in values for %xxx% constructs. This is made complex
- # because the replacement string may contain characters that are
- # meaningful to the regexp (like \1)
-
- line = line.gsub(/%([a-zA-Z]\w*)%/) {
- val = @context.find_scalar($1)
- val.tr('\\', "\000")
- }
+ b = binding
+ @templates.reverse_each do |template|
+ template_include = ERB.new(template).result b
+ end
- line
- rescue Exception => e
- $stderr.puts "Error in template: #{e}"
- $stderr.puts "Original line: #{line}"
- exit
+ io.write template_include
end
end