Class TemplatePage
In: template.rb
Parent: Object

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)

Methods

Classes and Modules

Class TemplatePage::Context
Class TemplatePage::LineReader

Public Class methods

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

  <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

[Source]

     # File template.rb, line 132
132:   def initialize(*templates)
133:     result = "!INCLUDE!"
134:     templates.each do |content|
135:       result.sub!(/!INCLUDE!/, content)
136:     end
137:     @lines = LineReader.new(result.split($/))
138:   end

Public Instance methods

Given an individual line, we look for %xxx% constructs and HREF:ref:name: constructs, substituting for each.

[Source]

     # File template.rb, line 201
201:   def expand_line(line)
202:     # Generate a cross reference if a reference is given,
203:     # otherwise just fill in the name part
204: 
205:     line.gsub!(/HREF:(\w+?):(\w+?):/) {
206:       ref = @context.lookup($1)
207:       name = @context.find_scalar($2)
208: 
209:       if ref and !ref.kind_of?(Array)
210:         "<a href=\"#{ref}\">#{name}</a>"
211:       else
212:         name
213:       end
214:     }
215: 
216:     # Substitute in values for %xxx% constructs.  This is made complex
217:     # because the replacement string may contain characters that are
218:     # meaningful to the regexp (like \1)
219: 
220:     line = line.gsub(/%([a-zA-Z]\w*)%/) {
221:       val = @context.find_scalar($1) 
222:       val.tr('\\', "\000")
223:     }
224: 
225: 
226:     line
227:   rescue Exception => e
228:     $stderr.puts "Error in template: #{e}"
229:     $stderr.puts "Original line: #{line}"
230:     exit
231:   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

[Source]

     # File template.rb, line 161
161:   def substitute_into(lines, values)
162:     @context.push(values)
163:     skip_to = nil
164:     result = []
165: 
166:     while line = lines.read
167: 
168:       case line
169: 
170:       when /^IF:(\w+)/
171:         lines.read_up_to(/^ENDIF:#$1/) unless @context.lookup($1)
172: 
173:     when /^IFNOT:(\w+)/
174:         lines.read_up_to(/^ENDIF:#$1/) if @context.lookup($1)
175: 
176:       when /^ENDIF:/
177:         ;
178: 
179:       when /^START:(\w+)/
180:         tag = $1
181:         body = lines.read_up_to(/^END:#{tag}/)
182:         inner_values = @context.lookup(tag)
183:         raise "unknown tag: #{tag}" unless inner_values
184:         raise "not array: #{tag}"   unless inner_values.kind_of?(Array)
185:         inner_values.each do |vals|
186:           result << substitute_into(body.dup, vals)
187:         end
188:       else
189:         result << expand_line(line.dup)
190:       end
191:     end
192: 
193:     @context.pop
194: 
195:     result.join("\n")
196:   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)

[Source]

     # File template.rb, line 144
144:   def write_html_on(op, value_hash)
145:     @context = Context.new
146:     op << substitute_into(@lines, value_hash).tr("\000", '\\')
147:   end

[Validate]