require 'glue/flexob'
module Nitro
# 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 /, "; #{buffer} << %^")
text.gsub!(/<\?r(\s?)/, "^; ")
# Transform alternative code tags.
# (very useful in xsl stylesheets)
text.gsub!(/<\/ruby>/, "; #{buffer} << %^")
text.gsub!(//, "^; ")
# Also handle erb/asp/jsp style tags. Those tags
# *cannot* be used with an xslt stylesheet.
text.gsub!(/%>/, "; #{buffer} << %^")
text.gsub!(/<%/, "^; ")
# Alterative versions of interpolation.
# (very useful in xsl stylesheets)
# Example: #(my_val)
text.gsub!(/\#\((.*?)\)/, '#{\1}')
# Alternative for entities.
# (useful in xsl stylesheets)
# Examples: %nbsp;, %rquo;
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. Just use the #[] marker instead of #{}.
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
# * George Moschovitis