Template::Toolkit wantarray gotcha

ChrisDolan on 2007-03-12T06:32:59

I wanted to experiment with some DBIx::Class syntax tonight in my Catalyst app tonight. Normally, I'd add the code to a .pm file, restart my app server, refresh the browser and look at the result.

Well, as any Template::Toolkit user knows, the TT backing code gets recompiled any time you change the .tt file. So an easy way to test some quick code is to write it in the .tt file and refresh the browser, saving the server restart time.

My code involved some multi-step DBIx::Class searches. DBIx::Class::ResultSet has a very cool feature that the search is performed as late as possible, so you can further constrain your search by calling search() again. A silly example:

  my @products = $c->model('Products')->search({color => 'red'})->search({size => 'Large'});


The search() method is smart in that it returns a ResultSet instance in scalar context, but in list context it performs the search and returns the database rows as DBIx::Class::Row instances.

So, I translated that code into TT syntax for a quick test:

  
    [% FOR p=c.model('Products').search({color='red'}).search({size='large'}) %]
  • [% p.title %]
  • [% END %]


That showed no results. Funny, I see some large, red things in the database. So, I removed the second search:

  
    [% FOR p=c.model('Products').search({color='red'}) %]
  • [% p.title %]
  • [% END %]


That worked. Then I tried .search({color='red'}).all() to force list context. Suddenly, the query stopped returning results, but no errors. That made a light turn on.

I realized that Template::Toolkit is evaluating each method call individually in list context and then calling the next method.

So, c.model('Products').search({color='red'}) was returning an array of products, then TT tried to call .search({size='large'}) on that array. The solution was to turn off the wantarray smarts of the resultset instance via the search_rs() method which forces search to return a ResultSet:

  
    [% FOR p=c.model('Products').search_rs({color='red'}).search({size='large'}) %]
  • [% p.title %]
  • [% END %]


So, the lesson is that while Template::Toolkit feels like Perl sometimes, it most certainly is not Perl. And that's a good thing! My hackery aside, it's a bad thing to code too much in your template.


Well...

phaylon on 2007-03-12T16:09:50

...I actually don't consider chained method calls as "too much Perl in the view," and it would be nice if Perl's specialities would be respected int hat regard, but for me TT is still the sanest templating out there. So all's forgiven :)