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#$1>
}
end
# ..
text.gsub!(/<(\w*?)([^>]*?)times=["|'](.*?)["|'](.*?)>(.*?)<\/\1>/m) do |match|
%{
<#$1#$2#$4>#$5#$1>
}
end
# ..
text.gsub!(/<(\w*?)([^>]*?)each=["|'](.*?)["|'](.*?)>(.*?)<\/\1>/m) do |match|
%{
<#$1#$2#$4>#$5#$1>
}
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