require 'glue/flexob'
require 'glue/configuration'
module Glue
# 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|
itext = File.read("#{base_dir}/#$1")
itext.gsub!(/<\?xml.*\?>/, '')
itext.gsub!(/<\/?root(.*?)>/m, ' ');
itext
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!(/<(render|inject) href=["|'](.*?)["|'](.*)(.?)\/>/) 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
# Evaluate the template.
#
# [+ruby+]
# A String containing the compiled template
# code.
#
# [+binding+]
# The evaluation binding for the rendering.
def evaluate_template(ruby, the_binding = nil)
eval(ruby, the_binding)
end
# Compile and render the template.
def process_template(template, buffer = '@out', the_binding = nil)
evaluate_template(compile_template(template, buffer), the_binding)
end
end
# A helper class that provides access to the Template methods
# as singleton methods.
class Template
# The default root directory where template files reside.
if File.exist?('template')
default_root = 'template'
elsif File.exist?('src/template')
default_root = 'src/template'
else
default_root = 'public'
end
setting :root, :default => default_root, :doc => 'The default root directory where template files reside'
# The default template name.
setting :default, :default => 'index', :doc => 'The default template name'
# The default template file extension.
setting :extension, :default => 'xhtml', :doc => 'The default template file extension'
# Strip xml comments from templates?
setting :strip_xml_comments, :default => false, :doc => 'Strip xml comments from templates?'
class << self
include TemplateMixin
alias_method :compile, :compile_template
alias_method :transform, :compile_template
alias_method :evaluate, :evaluate_template
alias_method :process, :process_template
end
include TemplateMixin
# Helper.
def render(template)
str = ''
process_template(template, 'str', binding)
return str
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
evaluate_template(compiled, binding)
return __out__
end
end
end
# * George Moschovitis