From 466d40ed15bc057ff786e73fde58a1ee48effabe Mon Sep 17 00:00:00 2001 From: drbrain Date: Mon, 7 Jan 2008 10:40:50 +0000 Subject: Use ERB instead of custom template language for RDoc. Remove old_html template. Convert all templates to ERB. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@14935 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/rdoc/template.rb | 247 ++++++++------------------------------------------- 1 file changed, 38 insertions(+), 209 deletions(-) (limited to 'lib/rdoc/template.rb') 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 (%fred%) -# * 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 T1, T2, 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 !INCLUDE! occurs. For example, - # we could have the overall page template containing - # - # - #

Master

- # !INCLUDE! - # - # - # 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 <<. The value_hash 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 "#{name}" - # 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 substitute_array - # (below), which examples a block of the template once for each - # row in the array. - # - # This routine also copes with the IF:_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 + "#{name}" + 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) - "#{name}" - 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 -- cgit v1.2.3