#/usr/bin/ruby require 'html5' unless ARGV.first =~ /.tmpl$/ and ARGV.length == 1 STDERR.puts "Usage: ruby #{__FILE__} index.html.tmpl" exit 86 end tmpl = open(ARGV.first) {|file| file.read} # convert variable names to xpath expressions xpath = proc do |name| case name when 'Channels' then 'planet:source' when 'Items' then 'atom:entry' when 'channel_link' then "atom:source/atom:link[@rel='alternate']/@href" when 'channel_title' then 'atom:source/atom:title' when 'channel_title_plain' then 'atom:title/text()' when 'date' then 'atom:updated/@planet:format' when 'feed' then "atom:link[@rel='self']/@href" when 'feedtype' then "atom:link[@rel='self']/@type" when 'generator' then 'atom:generator' when 'link' then "atom:link[@rel='alternate']/@href" when 'name' then 'atom:title' when 'owner_name' then 'atom:author/atom:name' when 'title' then 'atom:title' when 'url' then "atom:link[@rel='alternate']/@href" when /^channel_/ then "atom:source/planet:#{name.split('_',2).last}" when 'new_date' then "substring-before(atom:updated/@planet:format,', ')}, {" + "substring-before(substring-after(atom:updated/@planet:format,', '), ' ')" else STDERR.puts "Unknown variable encountered: #{name}" name end end # kill the DOCTYPE tmpl.sub! /\s*/, '' # enclose line comments in XML/HTML comments tmpl.gsub! /^((#.*\n)+)/, "\n" # special case: feed type already is formatted as a mime type tmpl.gsub! 'application/+xml', '' # template variables tmpl.gsub! //, '' tmpl.gsub! // do if $1 == 'content' '' else "{#{xpath.call($1)}}" end end # template if statements temporarily become pseudo-span elements tmpl.gsub! /<\/TMPL_IF>/, '' tmpl.gsub! // do if $1 == 'new_date' '' elsif $1 == 'new_channel' '' else "" end end # template loop statements temporarily become pseudo-span elements tmpl.gsub! /<\/TMPL_LOOP>/, '' tmpl.gsub! // do "" end # convert template to DOM doc = HTML5.parse(tmpl) # reparent conditional head elements doc.elements.each('html/body/span[meta or link]') {|span| doc.elements['html/head'] << span } doc.elements.each('//*') do |node| # pseudo span elements become xsl control elements if node.name == 'span' if node.attributes['select'] node.name = 'xsl:for-each' elsif node.attributes['test'] node.name = 'xsl:if' end end # choose between content and summary if node.name == 'choose' node.name = 'xsl:choose' child = node.add_element('xsl:when', 'test' => 'atom:content') child.add_element('xsl:apply-templates', 'select' => 'atom:content') child = node.add_element('xsl:when', 'test' => 'atom:summary') child.add_element('xsl:apply-templates', 'select' => 'atom:summary') end # convert scripts to CDATA if node.name == 'script' node.texts.each do |text| if text.value =~ /[&<]/ text.previous_sibling = REXML::CData.new(text.value) text.remove end end end # add namespace to html element if node.name == 'html' node.add_namespace 'http://www.w3.org/1999/xhtml' end # replace string interpretation in text nodes with xsl:value of elements node.texts.each do |text| if text.value =~ /\{.*?\}/ text.value.split(/\{(.*?)\}/).each_with_index do |value, index| if index % 2 == 1 text.previous_sibling = REXML::Element.new('xsl:value-of') text.previous_sibling.attributes['select'] = value elsif !value.empty? text.previous_sibling = REXML::Text.new(value) end end text.remove end end end # Egregious hack: convert all escaped single quotes in XPath # expressions to double quotes. tmpl = doc.to_s.gsub /\{.*?\}| (test|select)='.*?'/ do |xpath| xpath.gsub(''','"') end # wrapt the template in a stylesheet xslt = REXML::Document.new < #{tmpl.gsub(/\n\n\n+/,"\n\n")} content
EOF # output stylesheet open(ARGV.first.sub(/.tmpl$/,'.xslt'),'w') do |file| file.write xslt.to_s.gsub('"','"') end