From 11dbedfaad4a9a9521ece2198a8dc491678b1902 Mon Sep 17 00:00:00 2001 From: shyouhei Date: Wed, 29 Aug 2007 04:06:12 +0000 Subject: add tag v1_8_6_5001 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/tags/v1_8_6_5001@13304 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ruby_1_8_6/lib/rdoc/template.rb | 234 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 ruby_1_8_6/lib/rdoc/template.rb (limited to 'ruby_1_8_6/lib/rdoc/template.rb') diff --git a/ruby_1_8_6/lib/rdoc/template.rb b/ruby_1_8_6/lib/rdoc/template.rb new file mode 100644 index 0000000000..469e10fb4b --- /dev/null +++ b/ruby_1_8_6/lib/rdoc/template.rb @@ -0,0 +1,234 @@ +# 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 +# +# _stuff_ will only be included in the output if the corresponding +# key is set in the value hash. +# +# Usage: Given a set of templates T1, T2, etc +# +# values = { "name" => "Dave", state => "TX" } +# +# 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) +# +# + +class 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 + + 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", '\\') + end + + + # 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 + 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 + + line.gsub!(/HREF:(\w+?):(\w+?):/) { + ref = @context.lookup($1) + name = @context.find_scalar($2) + + 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") + } + + + line + rescue Exception => e + $stderr.puts "Error in template: #{e}" + $stderr.puts "Original line: #{line}" + exit + end + +end + -- cgit v1.2.3