require 'ostruct' require 'facets/more/openobject' require 'glue/configuration' 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 # Set some pretty safe delimiters for templates. START_DELIM = "</, "") # 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} << #{START_DELIM}") text.gsub!(/<\?r(\s?)/, "#{END_DELIM}; ") # Transform alternative code tags. # (very useful in xsl stylesheets) text.gsub!(/<\/ruby>/, "; #{buffer} << #{START_DELIM}") text.gsub!(//, "#{END_DELIM}; ") # Also handle erb/asp/jsp style tags. Those tags # *cannot* be used with an xslt stylesheet. text.gsub!(/%>/, "; #{buffer} << #{START_DELIM}") text.gsub!(/<%/, "#{END_DELIM}; ") # 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!(/%(#\d+|\w+);/, '&\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} << #{START_DELIM}#{text}#{END_DELIM}" 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. #-- # gmosx: there is a problem with using OpenObject here!!! # the eval no longer works! #++ class FileTemplate < OpenStruct 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