require 'nitro/template' require 'nitro/element' module Nitro # A shader defines a transformation-pipeline that tansforms # the .xhtml files to actual ruby code ready for evaluation # (compilation) by the engine. # # Shaders employ a folded-filter design. #-- # TODO: pipeline stage mixin to be reused in filters too. #++ class Shader # the next stage in the Shader pipeline. attr :next_stage def initialize(next_stage = nil) @next_stage = next_stage end # Process the text and optionally update the hash. # The hash is a short, unique representation of the # input text, typically used as a caching key. def process(hash, text) process_next(hash, text) end # Set the next stage of the pipeline. def << (next_stage = nil) @next_stage = next_stage return self end # Process the next stage of the pipeline. def process_next(hash, text) if @next_stage return @next_stage.process(hash, text) else return hash, text end end end # Convert the xhtml script to actual Ruby code, ready # to be evaluated. class RubyShader < Shader include TemplateMixin # Convert the xhtml script to actual Ruby code, ready # to be evaluated. #-- # Investigate: # perhaps xl:href should be used to be XLink compatible? #++ def process(hash, text) text = compile_template(text) process_next(hash, text) end # Loads and statically includes a file. def load_statically_included(filename) Logger.debug "Statically including '#{filename}'" if $DBG text = File.read(filename) text.gsub!(/<\?xml.*\?>/, '') text.gsub!(/<\/?root(.*?)>/m, ' '); return text end end # Apply an XSL transformation to the script code. # There is no need to keep post xsl. I can reuse the # same xsl by calling transform again. class XSLTShader < Shader # The name attr :name # The xslt filename. attr :xsl_filename # The xslt transformer. attr :xslt # Last modified time of the xslt. attr :mtime def initialize(xsl_filename, next_stage = nil) # leave this require here. only inlcude the xslt # library if the project needs it. require "xml/xslt" @name = File.basename(xsl_filename, '.*') @xsl_filename = xsl_filename @xslt = XML::XSLT.new @next_stage = next_stage end # Transform the given text. def process(hash, text) parse_xsl() @xslt.xml = text hash += @name Logger.debug "Applying xsl '#{@xsl_filename}' to template" if $DBG process_next(hash, xslt.serve) end private # Parse the xsl. def parse_xsl Logger.debug "Parsing xsl '#{@xsl_filename}'" if $DBG @mtime = File.mtime(@xsl_filename) @xslt.xsl = File.read(@xsl_filename) end end # Compress the inline xhtml. Does not touch the ruby code. class CompressShader < Shader # Compress the inline xhtml. Does not touch the ruby code. def process(hash, text) text.gsub!(/\@out \<\< \%\^(.*?)\^/m) do |match| c = $1.gsub(/^(\s*)/m, '').squeeze(" \t").tr("\n", '').tr("\t", ' ') "@out << %{#{c}}" end process_next(hash, text) end end # MorphingShader class MorphingShader < Shader def process(hash, text) # .. # .. text.gsub!(/<(\w*?)([^>]*?)(if|unless)=["|'](.*?)["|'](.*?)>(.*?)<\/\1>/m) do |match| %{ <#$1#$2#$5>#$6 } end # .. text.gsub!(/<(\w*?)([^>]*?)times=["|'](.*?)["|'](.*?)>(.*?)<\/\1>/m) do |match| %{ <#$1#$2#$4>#$5 } end # .. text.gsub!(/<(\w*?)([^>]*?)each=["|'](.*?)["|'](.*?)>(.*?)<\/\1>/m) do |match| %{ <#$1#$2#$4>#$5 } end process_next(hash, text) end end # ElementsShader class ElementsShader < Shader def process(hash, text) text = ElementProcessor.render(text) process_next(hash, text) end end end # * George Moschovitis