Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

ChrisDolan (2855)

ChrisDolan
  (email not shown publicly)
http://www.chrisdolan.net/

Journal of ChrisDolan (2855)

Monday March 12, 2007
01:32 AM

Template::Toolkit wantarray gotcha

[ #32654 ]

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:

  <ul>
  [% FOR p=c.model('Products').search({color='red'}).search({size='large'}) %]
   <li>[% p.title %]</li>
  [% END %]
  </ul>

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

  <ul>
  [% FOR p=c.model('Products').search({color='red'}) %]
   <li>[% p.title %]</li>
  [% END %]
  </ul>

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:

  <ul>
  [% FOR p=c.model('Products').search_rs({color='red'}).search({size='large'}) %]
   <li>[% p.title %]</li>
  [% END %]
  </ul>

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.

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.
  • ...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 :)

    --
    Ordinary morality is for ordinary people. -- Aleister Crowley