A Quick Pas de Deux with Dancer
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.