XSLT and Rails! Huzzah!
I am only being 50% sarcastic. XSLT can actually be quite useful for some situations of view generation as long as you can keep yourself from burning out on its sheer ludicrousness and/or majesty.
Usage
Put it in your Gemfile, and then create an app/transforms
directory. Write an XSLT stylesheet in there; you can use Haml if you want, or just raw XML.
The name of the file is important; it describes what sort of stuff it transforms from and to. In the simplest case, to transform some XML with a root tag named report
into HTML, your transform might be named report.html.xslt
(or if it’s written in Haml, then report.html.xslt.haml
).
Here’s a simple example app/transforms/report.html.xslt.haml
:
!!! XML
%xsl:stylesheet(version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform")
%xsl:template(match="/env/context")
-# More about this in a moment, for now just make sure we don't output the
-# context tag.
%xsl:template(match="/env/report")
%h1 I am a report! Hey, check me out!
%ul
%xsl:apply-templates(select=".")
%xsl:template(match="report-item")
%li
%label
%xsl:value-of(select="@name")
%xsl:value-of(select=".")
You’ll get everything you need under an input tag named env
. Under that is whatever tag came in with the actual source document, in this case report
. In our example, report
presumably contains just a bunch of simple report-item
tags, each with a name
attribute. We want to turn that into a simple HTML list.
Now, here’s the view (also in Haml, ’cause I like Haml) that will be displaying all the lovely HTML, where the_report
is a Nokogiri document:
%p
Here is your report, O Mighty One:
= Morpheus.transform(@the_report, :tgt_format => "html")
The `Morpheus.transform` method will look for an appropriately named file in your `transforms` directory, run the input document through it, and return the transformed document, which is easy to display due to Nokogiri’s implementation of to_s
in appropriate classes.
What’s all this about a context?
That’s for when you need to give some additional information to your transformation that’s separate from your input document. This is similar to how your views have access to the instance variables of their controllers.
For example, you could call transform
like so:
Morpheus.transform(@some_doc, :tgt_format => "html", :context => self)
Now in your XSLT, you’ll have access to all your instance variables in /env/context
, as i.e. /env/context/foo
and /env/context/bar
. In the context, Morpheus understands numbers, strings, arrays (the elements will end up in a series of tags named val
), and hashes (with the keys as tag names), and also any object that responds to to_xml
.
Multi-stage transformations
Your transformations can depend on other transformations in turn. A transformation that accepts some other format besides so-called “plain” XML needs to have a via
part in its filename.
For example, if I wanted to generate some XSL-FO in order to generate a PDF of my report, then I might decide that it’s easier to go from HTML instead of the original XML. In that case, I’d write another stylesheet and keep it in app/transforms/report.fo.via-html.xslt.haml
. Now you can run something like this:
fo_data = Morpheus.transform(@the_report, :tgt_format => "fo")
Morpheus will run your report through the HTML transform and then through the FO transform.
By the way, if you’re talking about a different “type” of document rather than a different format (for example, if you’re converting from a <report> document to a <summary> document, but they’re both “XML”), then you’d use “from-whatever” instead of “via-whatever”.