Generating Template::Declare Code from a HTML Baseline

December 19th, 2010

Generating Template::Declare Code from a HTML Baseline

For the templating system of my web applications, I usually go for HTML::Mason. It’s a wonderful system but, as it mixes Perl code with HTML, no matter how hard I try to be well-behaved, my templates always seem to grow into giant miss-indented smorgasborgs.

Because of that, recently I’ve also been playing around with Template::Declare. As the templates of Template::Declare are pure Perl, not only do I have a fighting chance to keep my indentation consistent, but if I fail, Perl::Tidy is there to save my bacon.

Now, one thing with Template::Declare is that if I already have some HTML, writing the template from scratch is a little bit daunting. On the other hand, wouldn’t that conversion be a perfect occasion to showcase the latest development on my own XML templating system, XML::XSS? Well, yes, I believe it would. :-)

In the latest development of XML::XSS, we can not only create stylesheets as classes, but I’ve introduced a style keyword that makes the syntax much cleaner. Follow me, I’ll show you.

First, nothing too fancy. We just create a stylesheet called XML::XSS::Stylesheet::HTML2TD that inherits from XML::XSS.

package XML::XSS::Stylesheet::HTML2TD;

use Moose;
use XML::XSS;
use Perl::Tidy;

extends 'XML::XSS';

And then, we begin with the fun stuff. For all HTML elements, we want to morph

<div class="[..]"> [..] </div>

into something like

div { attr { class => "[..]" }; outs "[..]"; }

Since we want all HTML elements to be transformed, we use the catchall element of the stylesheet:

style '*' => (
    pre  => &pre_element,
    post => '};',

sub pre_element {
    my ( $self, $node, $args ) = @_;

    my $name = $node->nodeName;

    return "$name {" . pre_attrs( $node );

sub pre_attrs {
    my $node = shift;

    my @attr = $node->attributes or return '';

    my $output = 'attr { ';

    for ( @attr ) {
        my $value = $_->value;
        $value =~ s/'/&apos;/g;
        $output .= $_->nodeName . ' => ' . "'$value'" . ', ';

    $output .= '};';

    return $output;

For the text, we want to wrap it with calls to outs. Except for text nodes that are nothing but empty spaces, which we’ll gladly skip:

style '#text' => (
    process => sub { $_[1]->data =~ /\S/ },
    pre     => "outs '",
    post    => "';",
    filter  => sub { s/'/\\'/g; s/^\s+|\s+$//gm; $_ },

For the pièce de résistance, since we’ll eventually ask Perl::Tidy to clean up our code, why not do it directly as we are transforming the document?

style '#document' => (
    content => sub {
        my ( $self, $node, $args ) = @_;
        my $raw = $self->stylesheet->render( $node->childNodes );

        my $output;
        my $err;
        eval { 
                source       => $raw,
                destination  => $output,
                errorfile    => $err,

        # send the raw output if Tidy failed
        return $err ? $raw : $output;


And we are done. Now we can take a semi-badly formated HTML snippet like this one,

<div class="entry_info">

<div style="float: right">
    created: Sat, Dec 18 2010</div>
<div><p>Web applications typically have [..] </p>
<pre class="code" >


and pass it through XML::XSS very new xss command-line utility to get the corresponding Template::Declare code:

$ xss --stylesheet HTML2TD snippet.xml 
html {
    div {
        attr { class => 'entry_info', };
        div {
            attr { style => 'float: right', };
            outs 'created: Sat, Dec 18 2010';
    div {
        p { outs 'Web applications typically have [..]'; };
        pre { attr { class => 'brush: plain', }; outs '[..]'; };

There is still a lot to do, but I’m rrrreally liking the direction XML::XSS is taking.

Seen a typo or an error? Submit an edit on GitHub!