[Rg 99] [PATCH] XSLT template support
Stephan Maka
stephan at spaceboyz.net
Fri Sep 21 19:10:48 EDT 2007
Hi,
this is preliminary support for XSLT templates, along with specs and an
example. The extFunctions code is rather complicated and I could switch
to Facets' Functor pattern, if that dependency is allowed.
Please comment,
Stephan
-------------- next part --------------
New patches:
[Ramaze support for XSLT templates, example
stephan at spaceboyz.net**20070920011044] {
addfile ./examples/templates/template/external.xsl
hunk ./examples/templates/template/external.xsl 1
+<?xml version="1.0" encoding="utf-8"?>
+<xsl:stylesheet version="1.0"
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ exclude-result-prefixes="xsl">
+
+<xsl:output method="xml"
+ version="1.0"
+ encoding="utf-8"
+ doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
+ doctype-system="DTD/xhtml1-strict.dtd"
+ indent="yes"/>
+
+ <xsl:template match="/page">
+ <html>
+ <head>
+ <title>
+ <xsl:value-of select="@title"/>
+ </title>
+ </head>
+ <body>
+ <xsl:apply-templates/>
+ </body>
+ </html>
+ </xsl:template>
+
+ <xsl:template match="heading">
+ <h1>
+ <xsl:apply-templates/>
+ </h1>
+ </xsl:template>
+
+ <xsl:template match="list">
+ <ul>
+ <xsl:apply-templates/>
+ </ul>
+ </xsl:template>
+
+ <xsl:template match="list/item">
+ <li>
+ <xsl:apply-templates/>
+ </li>
+ </xsl:template>
+
+ <xsl:template match="link">
+ <a href="{@href}">
+ <xsl:apply-templates/>
+ </a>
+ </xsl:template>
+
+ <xsl:template match="text">
+ <p>
+ <xsl:apply-templates/>
+ </p>
+ </xsl:template>
+
+</xsl:stylesheet>
addfile ./examples/templates/template_xslt.rb
hunk ./examples/templates/template_xslt.rb 1
+# Copyright (c) 2006 Michael Fellinger m.fellinger at gmail.com
+# All files in this distribution are subject to the terms of the Ruby license.
+
+require 'ramaze'
+require 'ramaze/gestalt'
+
+include Ramaze
+
+class MainController < Controller
+ template_root __DIR__/:template
+ trait :engine => Template::XSLT
+
+ def index
+ redirect R(:external)
+ end
+
+ def external *args
+ r = lambda { |*a| R(*a) }
+
+ #options = {:place => :internal, :action => 'internal',
+ # :args => args, :request => request, :this => self}
+ Ramaze::Gestalt.build do
+ page(:title=>"Template::XSLT") do
+ heading "The external Template for XSLT"
+ text "Here you can pass some stuff if you like, parameters are just passed like this:"
+ list do
+ item {
+ link(:href => r.call(@this, :external, :one)) { "external/one" }
+ }
+ item {
+ link(:href => r.call(@this, :external, :one, :two, :three)) { "external/one/two/three" }
+ }
+ item {
+ link(:href => r.call(@this, :external, :one, :foo => :bar)) { "external/one?foo=bar" }
+ }
+ end
+ text "The arguments you have passed to this action are:"
+ if args.empty?
+ text "none"
+ else
+ list {
+ args.each do |arg|
+ item arg
+ end
+ }
+ end
+ end
+ end
+ end
+end
+
+Ramaze.start
hunk ./lib/ramaze/template.rb 19
- %w[ Amrita2 Erubis Haml Liquid Markaby Remarkably Sass ].each do |const|
+ %w[ Amrita2 Erubis Haml Liquid Markaby Remarkably Sass XSLT ].each do |const|
addfile ./lib/ramaze/template/xslt.rb
hunk ./lib/ramaze/template/xslt.rb 1
+require 'xml/libxml'
+require 'xml/xslt'
+
+module Ramaze
+
+ # Use the Gestalt helper to put your controller result
+ # into proper XML form
+ #
+ # TODO:
+ # * Error handling
+ # * Support for XML::XSLT::extFunction
+ # * Non-fatal failure when missing Ruby-XSLT
+ module Template
+ class XSLT < Template
+ ENGINES[self] = %w[ xsl ]
+
+ class << self
+
+ # Entry point for Action#render
+
+ def transform action
+ result, file = result_and_file(action)
+
+ xslt = XML::XSLT.new
+ xslt.xsl = action.template
+ xslt.xml = result
+ xslt.serve
+ end
+
+ end
+ end
+ end
+end
}
[XSLT templates: add extFunctions capability
stephan at spaceboyz.net**20070921115648] {
adddir ./spec/ramaze/template/xslt
hunk ./lib/ramaze/template/xslt.rb 3
+require 'thread'
hunk ./lib/ramaze/template/xslt.rb 11
+ # * Complex extFunction return values
hunk ./lib/ramaze/template/xslt.rb 13
- # * Support for XML::XSLT::extFunction
- # * Non-fatal failure when missing Ruby-XSLT
+ # * Maybe prevent extFunction to be called by HTTP
hunk ./lib/ramaze/template/xslt.rb 18
+ XSLT_EXT_FUNCTIONS_LOCK = Mutex.new
+
hunk ./lib/ramaze/template/xslt.rb 25
+
+ if options = action.instance.ancestral_trait[:xslt_options] and
+ fun_xmlns = options[:fun_xmlns]
+ # If a controller uses extFunctions, lock the whole
+ # transform action with a Mutex to prevent mixing
+ # extFunction binding and callback of two controller
+ # instances with one fun_xmlns.
+ ext_functions_synchronize do
+ register_ext_functions action.instance, fun_xmlns
+ do_transform action
+ end
+
+ else
+ do_transform action
+ end
+
+ end
+
+ private
+
+ def do_transform(action)
hunk ./lib/ramaze/template/xslt.rb 53
+
+ def ext_functions_synchronize &block
+ Inform.debug "Locking extFunctions Mutex #{XSLT_EXT_FUNCTIONS_LOCK.inspect}"
+ XSLT_EXT_FUNCTIONS_LOCK.synchronize &block
+ end
+
+ def register_ext_functions instance, fun_xmlns
+
+ instance.methods.each do |method|
+ if method =~ /^xslt_.+/
+ method_name = method[5..-1]
+
+ proxy_instance = make_functor(method_name.intern) { |*a|
+ instance.send method.intern, *a
+ }
+
+ XML::XSLT.extFunction method_name.gsub('_', '-'), fun_xmlns, proxy_instance
+ end
+ end
+
+ end
+
+ def make_functor(m, &block)
+ # Create anonymous class and instantiate;
+ # anonymous because only this one object with this
+ # particular custom method should call &block
+ o = Class.new.new
+
+ class << o
+ def create_method(name, &block)
+ self.class.send(:define_method, name, &block)
+ end
+ end
+
+ o.create_method(m, &block)
+ o
+ end
hunk ./spec/ramaze/gestalt.rb 9
+ # This is useful for any controller using Gestalt,
+ # should be made a MixIn somewhen.
addfile ./spec/ramaze/template/xslt.rb
hunk ./spec/ramaze/template/xslt.rb 1
+# Copyright (c) 2006 Michael Fellinger m.fellinger at gmail.com
+# All files in this distribution are subject to the terms of the Ruby license.
+
+require 'spec/helper'
+
+testcase_requires 'xml/xslt'
+testcase_requires 'ramaze/gestalt'
+
+class TCTemplateXSLTController < Ramaze::Controller
+ template_root 'spec/ramaze/template/xslt/'
+ trait :engine => Ramaze::Template::XSLT
+ trait :xslt_options => { :fun_xmlns => 'urn:test' }
+
+ def index
+ gestalt {
+ hi 'tobi'
+ }
+ end
+
+ def ruby_version
+ @version = RUBY_VERSION
+
+ gestalt {
+ document
+ }
+ end
+
+ def xslt_get_ruby_version
+ @version
+ end
+
+ private
+
+ def gestalt &block
+ Ramaze::Gestalt.new(&block).to_s
+ end
+
+end
+
+describe "XSLT" do
+ ramaze(:mapping => {'/' => TCTemplateXSLTController})
+
+ it "index" do
+ get('/').body.should == "hi tobi"
+ end
+
+ it "ruby_version through external functions" do
+ get('/ruby_version').body.should == RUBY_VERSION
+ end
+end
+
addfile ./spec/ramaze/template/xslt/index.xsl
hunk ./spec/ramaze/template/xslt/index.xsl 1
+<?xml version="1.0" encoding="utf-8"?>
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+ <xsl:output method="text"
+ encoding="utf-8"/>
+
+ <xsl:template match="/*">
+ <xsl:value-of select="name(.)"/>
+ <xsl:text> </xsl:text>
+ <xsl:apply-templates/>
+ </xsl:template>
+
+</xsl:stylesheet>
addfile ./spec/ramaze/template/xslt/ruby_version.xsl
hunk ./spec/ramaze/template/xslt/ruby_version.xsl 1
+<?xml version="1.0" encoding="utf-8"?>
+<xsl:stylesheet version="1.0"
+ xmlns:test="urn:test"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ exclude-result-prefixes="xsl">
+
+ <xsl:output method="text"
+ encoding="utf-8"/>
+
+ <xsl:template match="/document">
+ <ruby-version><xsl:value-of select="test:get-ruby-version()"/></ruby-version>
+ </xsl:template>
+
+</xsl:stylesheet>
}
[XSLT template spec: two more examples
stephan at spaceboyz.net**20070921130029] {
hunk ./spec/ramaze/template/xslt.rb 8
+testcase_requires 'rexml/document'
hunk ./spec/ramaze/template/xslt.rb 33
+ def products
+ gestalt {
+ order {
+ first
+ items
+ }
+ }
+ end
+
+ def xslt_get_products
+ REXML::Document.new \
+ gestalt {
+ list {
+ %w[Onion Bacon].each { |product|
+ item product
+ }
+ }
+ }
+ end
+
+ def concat_words
+ gestalt {
+ document
+ }
+ end
+
+ def xslt_concat(*args)
+ args.to_s
+ end
+
hunk ./spec/ramaze/template/xslt.rb 81
+
+ it "external functions returning XML data" do
+ get('/products').body.
+ gsub(/<\?.+\?>/, '').strip.
+ should == '<result><first>Onion</first><item>Onion</item><item>Bacon</item></result>'
+ end
+
+ it "parameters" do
+ get('/concat_words').body.should == 'oneonetwoonetwothree'
+ end
addfile ./spec/ramaze/template/xslt/concat_words.xsl
hunk ./spec/ramaze/template/xslt/concat_words.xsl 1
+<?xml version="1.0" encoding="utf-8"?>
+<xsl:stylesheet version="1.0"
+ xmlns:test="urn:test"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ exclude-result-prefixes="test xsl">
+
+ <xsl:output method="text"
+ encoding="utf-8"/>
+
+ <xsl:template match="/document">
+ <xsl:value-of select="test:concat('one')"/>
+ <xsl:value-of select="test:concat('one', 'two')"/>
+ <xsl:value-of select="test:concat('one', 'two', 'three')"/>
+ </xsl:template>
+
+</xsl:stylesheet>
addfile ./spec/ramaze/template/xslt/products.xsl
hunk ./spec/ramaze/template/xslt/products.xsl 1
+<?xml version="1.0" encoding="utf-8"?>
+<xsl:stylesheet version="1.0"
+ xmlns:test="urn:test"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ exclude-result-prefixes="test xsl">
+
+ <xsl:output method="xml"
+ encoding="utf-8"/>
+
+ <xsl:template match="order">
+ <result>
+ <xsl:apply-templates/>
+ </result>
+ </xsl:template>
+
+ <xsl:template match="first">
+ <first>
+ <xsl:value-of select="test:get-products()/item[1]"/>
+ </first>
+ </xsl:template>
+
+ <xsl:template match="items">
+ <xsl:apply-templates select="test:get-products()" mode="list"/>
+ </xsl:template>
+
+ <xsl:template match="item" mode="list">
+ <item>
+ <xsl:apply-templates mode="list"/>
+ </item>
+ </xsl:template>
+
+</xsl:stylesheet>
hunk ./spec/ramaze/template/xslt/ruby_version.xsl 5
- exclude-result-prefixes="xsl">
+ exclude-result-prefixes="test xsl">
}
[XSLT template example: add Content-Type
stephan at spaceboyz.net**20070921225525] {
hunk ./examples/templates/template_xslt.rb 2
+# Copyright (c) 2007 Stephan Maka stephan at spaceboyz.net
hunk ./examples/templates/template_xslt.rb 20
+ response['Content-Type'] = 'application/xhtml+xml'
hunk ./lib/ramaze/template/xslt.rb 1
+# Copyright (c) 2007 Stephan Maka stephan at spaceboyz.net
+# All files in this distribution are subject to the terms of the Ruby license.
+
hunk ./lib/ramaze/template/xslt.rb 14
- # * Complex extFunction return values
hunk ./lib/ramaze/template/xslt.rb 57
- Inform.debug "Locking extFunctions Mutex #{XSLT_EXT_FUNCTIONS_LOCK.inspect}"
hunk ./lib/ramaze/template/xslt.rb 76
+ # Can be replaced by Ruby Facets' Functor pattern
}
Context:
[Slight 'beautifcation' for specwrapper
Michael Fellinger <m.fellinger at gmail.com>**20070921081739]
[SourceReload#reload_glob is now SourceReload.trait[:reload_glob] for ultimate control.
Michael Fellinger <m.fellinger at gmail.com>**20070921080937]
[This improves logging a bit by introducing the :dev tag and so lowering the overall output in default mode.
Michael Fellinger <m.fellinger at gmail.com>**20070921064236]
[alias Ramaze.contrib to Ramaze::Contrib.load
Aman Gupta <ramaze at tmm1.net>**20070919153655]
[Gestalt: allow text in arguments, properly escape this text and attributes
stephan at spaceboyz.net**20070920153337]
[Add $0 to files being sourcereloaded.
Michael Fellinger <m.fellinger at gmail.com>**20070919062417]
[Implement Global.ignore and spec it.
Michael Fellinger <m.fellinger at gmail.com>**20070919062143]
[Add Global.cache_alternative so you can specify a different cache-class to use for only certain caches. For example: Ramaze::Global.cache_alternative[:sessions] = Ramaze::MemcachedCache
Michael Fellinger <m.fellinger at gmail.com>**20070918104659]
[Small improvment to the sequel/fill contrib
Michael Fellinger <m.fellinger at gmail.com>**20070918054112]
[Add contrib/sequel/fill
Michael Fellinger <m.fellinger at gmail.com>**20070918053330]
[alias redirect_referer to redirect_referrer in RedirectHelper
Michael Fellinger <m.fellinger at gmail.com>**20070918045439]
[Allow custom /helper directory in apps, will be searched before ramazes helpers.
Michael Fellinger <m.fellinger at gmail.com>**20070918014843]
[Add the wikore example and fix spec for wiktacular a little.
Michael Fellinger <m.fellinger at gmail.com>**20070917114503]
[Improve output of spec wrapper a bit.
Michael Fellinger <m.fellinger at gmail.com>**20070917114458]
[Adding path for OSX to tool/tidy and improve readability of the spec for it a bit.
Michael Fellinger <m.fellinger at gmail.com>**20070912125255]
[Fixing dependence on the debug.rb implementation of ruby, since this may vary between different versions/implementations, use gestalt.rb instead, the oldest and most stable file we have.
Michael Fellinger <m.fellinger at gmail.com>**20070912090809]
[Small beautification/speedup for the mocked http
Michael Fellinger <m.fellinger at gmail.com>**20070911084731]
[Clean up the controller/resolve part a bit, implement raise_no_filter which throws a NoFilter error now and fix a minor bug that would result in a faulty response if an element of Cache.resolved was no valid action. Added docs for all methods in Controller.
Michael Fellinger <m.fellinger at gmail.com>**20070911162955]
[Restructuring of how contribs are handled, introducing the Ramaze::Contrib namespace, adding Global.contribs so we can add a unified shutdown in future, fixing routing so it won't try to match an already resolved route again and thereby avoiding recursion.
Michael Fellinger <m.fellinger at gmail.com>**20070911144843]
[Adding snippet for Array#put_within/put_before/put_after plus specs. docs missing.
Michael Fellinger <m.fellinger at gmail.com>**20070911054158]
[Updated spec for route
Aman Gupta <ramaze at tmm1.net>**20070910063242]
[This introduces the first contrib for routes, slight restructuring of Controller::resolve to allow filtering based on Controller::FILTER like we know it from Dispatcher. Added dictionary.rb from facets to allow sorted but hash-like routes-adding. Spec for routes added as small showcase.
Michael Fellinger <m.fellinger at gmail.com>**20070910044521]
[Add basic Ramaze::contrib as future helping instance for contributed things.
Michael Fellinger <m.fellinger at gmail.com>**20070910044506]
[make snippets/struct/values_at behaviour compatible with standard ruby (orig. by riffraff)
Michael Fellinger <m.fellinger at gmail.com>**20070907083216]
[Fix for directory-listing, always sort files/dirs shown
Michael Fellinger <m.fellinger at gmail.com>**20070907083158]
[TAG 0.1.4
Michael Fellinger <m.fellinger at gmail.com>**20070906135219]
Patch bundle hash:
057156156e12aba1ef2d9329f3cc99a57d736775
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
Url : http://rubyforge.org/pipermail/ramaze-general/attachments/20070922/28660443/attachment.bin
More information about the Ramaze-general
mailing list