A Quick Pas de Deux with Dancer

January 24th, 2012

A Quick Pas de Deux with Dancer

This is going to be a short one, but potentially useful for anybody writing a Dancer template module, or just plain curious about Dancer’s guts. So here goes:

A few weeks ago, it came to my attention that Dancer’s Dancer::Template::Abstract, the base class for its template modules, added a test to verify that the template it receives as an argument is really a file. Yay. Sanity tests are awesome. Except… what happens when a templating system is not file-based? A lot of exceptions and a very sad web application, that’s what happen.

If you haven’t guessed yet, yes, there is at least one non-file-based Dancer::Template::* module out there: Dancer::Template::TemplateDeclare. And who is the maintainer of that module? … aw, come on. Surely you can infer that one.

So, well, yeah, Dancer::Template::Abstract was suddenly a killjoy for poor D::T::TD. And since the Dancer team was, and still is, very busy bringing Dancer 2 into this world (YAY!) and are up their earlobes in placental duties, I knew it was a thing I would have to resolve by myself. Dirty Harry style.

As it turned out, once I peeked, proded and understood how D::T::A works under the hood, a decent(ish) solution wasn’t too hard to come by. What I elected to go for was a classical “show the guards what they want to see while you keep your stash under the mattress” manoeuver.

To do the stashing, we augment the method apply_renderer() to do our leger de main before any rendering shenanigan begin:

sub apply_renderer {
    my ( $self, $view, $tokens ) = @_;

    $tokens->{template} = $view;

    return $self->SUPER::apply_renderer( $view, $tokens );

Now that our template is safely tucked away, we can get busy and pull the wool over the object’s eyes. This is made easy by the method view(), which is supposed to take in the template name and return the corresponding file. Instead, we’ll make it return something else that will always exist:

sub view { $FindBin::Bin }

I know what you’re going to say. $FindBin::Bin is a directory, not a file. As luck would have it, the test in T::D::Abstract is -e and not -f, so we are okay. If it hadn’t been, we would just had to use __FILE__ or something else.

And that’s pretty much it. There is still the business of retrieving the stashed template in render(), but that’s easily done:

sub render {
    my ($self, $template, $tokens) = @_;

    # just in case render() is called directly
    $template = $tokens->{template} || $template;

    return Template::Declare->show( $template => $tokens );

With that, everything is back to normal. Now, all I need is a little bit of time to cook up a patch to make Dancer2 a little more forgiving of T:D::TD and its file-less kin.