/usr/lib/ruby/1.8/rdoc/template.rb is in libruby1.8 1.8.7.352-2ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | # 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
#
# _stuff_ will only be included in the output if the corresponding
# key is set in the value hash.
#
# Usage: Given a set of templates <tt>T1, T2,</tt> 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 <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
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", '\\')
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 <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
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)
"<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")
}
line
rescue Exception => e
$stderr.puts "Error in template: #{e}"
$stderr.puts "Original line: #{line}"
exit
end
end
|