Hacking Thy Fearful Symmetry

Hacker, hacker coding bright

Showing Off Template::Caribou

February 22, 2013
perl template::caribou

Fair warning: I am fully aware that extolling the virtues of your all-new, all-shiny templating system has high changes to end up mixing the obnoxiousness of a new dad recklessly shoving pictures of his mini-Churchill down your field of vision ("isn't simply adooorable?") with the self-delusion typically reserved for inventors of perpetual motion engines ("Sure, there are other templating systems. But this one is... special."). It that regard, what follow ain't going to be pretty one bit.

So if self-indulgent ramblings aren't your thing you might want to close this browser tab already. But if you are here for the entertaining factor... make yourself comfortable, the delightful freakshow's about to start!

Recap

As template systems are all about being a coder-friendly layer between inner logic and final output, it's no wonder there are so many of them out there, for there is no two coders in existence sharing the exact same definition of what's "friendly'. Don't believe me? Just try to push your .vimrc to any of your colleague and behold the resulting tollé; you'll see what I mean.

In any cases, template-wise, I've always had a soft spot for Mason. I've also been attracted to Template::Declare, but it wasn't exactly what I wanted.

So, what was bound to happen happened, and on a cold, wintery night my very own Template::Caribou was born.

For the longest time, it remained a very rough prototype. But lately, I began to use it a little more seriously (mostly in my Newsmill and Galuga rewrite projects). And since today I finally managed to write some half-decent documentation (with heavy emphasis on the "half" part of it) and release it to CPAN, I thought it was the perfect time to hop on the soap box and count the ways this new antlered wonder might be poised to rock your socks.

Way no. 1 - It's a Role

... which means that it can be slapped on any regular Moose class that needs a HTML view. Which can be used for the obvious, or for the magnificently weird:


package ShowMethods;

use Moose::Role;
use Template::Caribou;
use Template::Caribou::Tags::HTML qw/ :all /;

with 'Template::Caribou';

template methods => sub {
    my $self = shift;

    ul {
        li { $_  } for $self->meta->get_method_list;
    }
};

# then later on
use MyClass;
use Moose::Util qw/ apply_all_roles /;

my $thingy = MyClass->new;

apply_all_roles( $thingy, 'ShowMethods' );

print $thingy->render('methods');

Way no. 2 - You Can Still Write Straight HTML If you Want To

It's nice that a template system offers you shortcuts and cleaner ways to write HTML, but sometimes you just want to pound raw HTML code to get you started, and clean up the (now working) mess afterward. Or you want to import already existing HTML and convert it afterward, as time allows. Happily, Caribou allows that:


template page => sub {
    print ::RAW <<'END';
<html>
    <head> ... </head>
    <body> ... </body>
</html>
END
};

Granted, it looks silly, but paves the way for subsequent rewrites that might look like


template page => sub {
    html {
        print ::RAW <<'END';
        <head> ... </head>
        <body> ... </body>
END
    }
};

then


template page => sub {
    html {
        show('head');
        show('body');
    }
};

template head => sub { print <<'END';
    <head> ... same ungodly mess as before, 
        but encapsulated in its own template </head>
END
};

and ultimately


template page => sub {
    html {
        show('head');
        show('body');
    }
};

template head => sub {
    head {
        title { "finally all Cariboutized" };
    }
};

Way no. 3 - Templates Can Go In Their Own Files

Thanks to the Template::Caribou::Files role, templates for a class can be defined in separate files so that you don't end up with a gigantic class. Using that pattern, a template class will look nice and clean:


package Greeter;

use Moose;
use Template::Caribou;

with 'Template::Caribou';

with 'Template::Caribou::Files' => {
    dirs        => [ 'views/greeter' ],
    auto_reload => 1,
};

has name => (
    is       => 'ro',
    isa      => 'Str',
    required => 1,
);

1;

and will be able to import .bou templates off the given directories, which will look like


#( $salutation = 'Howdie' )

body {
    h1 { join ' ', $salutation, $self->name };
};

The .bou templates are generated via Method::Signatures, so not only we don't have to worry about shifting our $self but we can also be fancy, like here, and give it a custom signature.

Way no. 4 - Auto-Refresh For Development Tinkering

Did you notice the auto_reload option in the previous example? As the name suggests, if that option is set to true, the class will rescan all template directories before any new render, which is awesome for development. Right now, for example, in the Dancer component of Newsmill, I have the action:


get qr{/edition/(\d+)} => sub {
    my( $nbr ) = splat;
    my $issue = rset('Edition')->find( $nbr )
        or return send_error "Issue '$nbr' not found", 404;

    return Newsmill::View::Issue->new(
        issue => $issue
    )->render('page');
};

Once that is there, I can fiddle with the .bou templates all I want without having to restart the application.

(and, yes, there is a Dancer::Template::Caribou lying somewhere in the near future. But let's not get ahead of ourselves, shall we?)

Way no. 5 - Templates Are Peopl-- No, Wait, It's Perl Code

One of the reason I prefer Mason over Template::Toolkit is the direct access the former gives us to Perl syntax. Unfortunately, because the resulting templates are a mix of Perl and HTML, even with the best of intentions they always end up looking like giant hairballs.

There is now Mason::Tidy to help with that, but as long as there is HTML involved, let's face it, it's always an uphill battle.

On the other hand, Caribou's templates are pure Perl, which means that a clean indentation of the code is always only a call to Perl::Tidy away.

It also means that the indentation in the source doesn't reflect on the output. So the nicely indented


html {
    head {
        title { "Hi" };
    }
}

will become the succinct


<html><head><title>Hi</title></head></html>

by default (with soon an option to pretty print it for debugging purposes).

Way no. 6 - Tags

This one is kinda of a given. But since all tags are just Perl functions, it's worth to point out that they therefore can be quite versatile.


# in page.bou
use Template::Caribou::Tags::HTML ':all';

html {
    body {
        div { attr class => 'left_column';
            show( 'widget' => $_ )
                for $self->all_widgets;
        };

        div { attr class => 'main';
            print ::RAW $self->body;
        };
    };
}

# in widget.bou
#( $widget )
div {
    attr id    => $widget->id,
         class => 'widget';
    print ::RAW $widget->content;
}

Way no. 7 - Semantic Tags

Look back at the example above. Isn't it silly that we mean 'the left column', and we have to write it


<div class="left_column"> ... </div>

? Wouldn't be nicer to take care of the mechanics once, and then use the high-level name forevermore, like so:


# in page.bou
use Template::Caribou::Tags::HTML ':all';

# define my custom divs
use Template::Caribou::Tags
    map { mytag => { -as => $_, class => $_ } }
        qw/ left_column widget main /;

html {
    body {
        left_column { 
            show( 'widget' => $_ )
                for $self->all_widgets;
        };

        main { print ::RAW $self->body; };
    };
}

# in widget.bou
#( $widget )
widget { attr id => $widget->id;
    print ::RAW $widget->content;
}

Way no. 8 - Extended Tags Just For HTML

A lot of templating systems try to stay generic. Caribou also does, as far as Template::Caribou::Tags::HTML is concerned. But then there is Template::Caribou::Tags::Extended, which acknowledges that we are hip-deep in HTML, and optimizes in function of that. Are you sick and tired to look up what is the right <link> invocation for stylesheets? Well, I am, and so Caribou's bag of extended tags contains


css_include "/my.css";

which takes care of all the nitty-gritty details. The shortcuts go from the simple, like


a { attr href => 'http://...'; "some link" }

that can become


anchor 'http://...' => "some link";

to the more... powerful. Like this:


head {
    # yes, this will convert a LESS stylesheet into classic 
    # CSS
    less q[
        #header {
        h1 {
            font-size: 26px;
            font-weight: bold;
        }
        p { font-size: 12px;
            a { text-decoration: none;
            &:hover { border-width: 1px }
            }
        }
        }
    ];
};
body {
    markdown q{
Because writing a paragraph using Markdown is
*much* easier on the eyes than using straight 
HTML.  
};
};

Way no. 9 - It's Pretty Easy To Create Tag Libraries For Anything

Case in point. Newsmill is built with Bootstrap. With basic tags, I could do things like


div { attr class => 'row-fluid'; 
    div { attr class => 'span8 offset2";
        ...;
    };
}

But it's much more legible if we go one level up the semantic ladder:


use Template::Caribou::Tags::Bootstrap 
    row  => { -as => 'body_row', fluid => 1 },
    span => { -as => 'body_span', offset => 2, span => 8 };

body_row {
    body_span {
        ...;
    };
}

What Lies Ahead

As mentioned earlier, as of today Caribou has some documentation, but it's still sparse and needs some serious love. The infrastructure is now in working order (or, at least, can be used for small projects), but needs some cleanup. The different tag libraries are there, but need to be thoroughly populate. A Dancer::Template::Caribou should eventually see the light of day to bring the whole solution together.

So yeah, there are still a lot of work to be done. But I must say, while I'm still half-convinced that the whole endeavor is nothing but an exercise in hubristic yak shaving, I kinda begin to think there might be something special in there. Something special, and adooooorable.

comments powered by Disqus

About the author

Yanick Champoux
Perl necrohacker , ACP writer, orchid lover. Slightly bonker all around. he/him/his