# * George Moschovitis # (c) 2004-2005 Navel, all rights reserved. # $Id: template.rb 332 2005-03-29 11:31:52Z gmosx $ require 'glue/flexob' module N # A template is a text file with embeded Ruby code. The template # processor converts the original text file to ruby code and # then evaluates this code to produce the result of the # template transformation. module TemplateMixin # Convert a template to actual Ruby code, ready to be # evaluated. # # [+template+] # The template as a String. # # [+buffer+] # The variable to act as a buffer where the ruby code # for this template will be generated. Passed as a| # String. # # [+base_dir+] # The base directory where the templates reside. def compile_template(template, buffer = '@out', base_dir = Dir.pwd) text = template.dup # Strip the xml header! (interracts with the following gsub!) text.gsub!(/<\?xml.*\?>/, "") # Statically include sub-template files. # The target file is included at compile time. # # gmosx: must be xformed before the text.gsub!(/<\?include href="(.*?)"(.*)\?>/) do |match| text = File.read("#{base_dir}/#$1") text.gsub!(/<\?xml.*\?>/, '') text.gsub!(/<\/?root(.*?)>/m, ' '); text end # Transform include instructions # must be transformed before the processinc instructions. # Useful to include fragments cached on disk # # gmosx, FIXME: NOT TESTED! test and add caching. # add load_statically_included fixes. text.gsub!(//) do |match| "" end # xform render/inject instructions # must be transformed before the processinc instructions. text.gsub!(//) do |match| "" end text.gsub!(//) do |match| "" end # Remove elements. typically removed by xslt but lets # play it safe. The element is typically added to # template files to make them XHTML valid. text.gsub!(/<(\/)?root>/, '') # Transform the processing instructions, use /, "\n#{buffer} << %^") text.gsub!(/<\?r(\s?)/, "^\n") # Transform alternative code tags. # (very useful in xsl stylesheets) text.gsub!(/<\/ruby>/, "\n#{buffer} << %^") text.gsub!(//, "^\n") # Also handle erb/asp/jsp style tags. Those tags # *cannot* be used with an xslt stylesheet. text.gsub!(/%>/, "\n#{buffer} << %^") text.gsub!(/<%/, "^\n") # Alterative versions of interpolation. # (very useful in xsl stylesheets) text.gsub!(/\#\((.*?)\)/, '#{\1}') # Alternative for entities. # (useful in xsl stylesheets) text.gsub!(/%(\S*?);/, '&\1;') # Compile time ruby code. This code is evaluated when # compiling the template and the result injected directly # into the result. Usefull for example to prevaluate # localization. text.gsub!(/\#\[(.*?)\]/) do |match| eval($1) end text = "#{buffer} << %^" + text + "^" return text end # Render the template. # # [+ruby+] # A String containing the compiled template # code. # # [+binding+] # The evaluation binding for the rendering. def render_template(ruby, the_binding = nil) eval(ruby, the_binding) end # Compile and render the template. def process_template(template, buffer = '@out', the_binding = nil) render_template(compile_template(template, buffer), the_binding) end end # A helper class that provides access to the Template methods # as singleton methods. class Template class << self include TemplateMixin alias_method :compile, :compile_template alias_method :render, :render_template alias_method :process, :process_template end end # A Template that reads from files and also # provides a simple but effective caching scheme. # An intuitive binding mechanism provides the # expansion environment. class FileTemplate < Flexob include TemplateMixin @@compiled_template_cache = {} attr_accessor :template_filename def initialize(filename = nil) super @template_filename = filename end def process __out__ = '' unless compiled = @@compiled_template_cache[@template_filename] template = File.read(@template_filename) compiled = compile_template(template, '__out__') @@compiled_template_cache[@template_filename] = compiled end render_template(compiled, binding) return __out__ end end end