Keith Devens .com |
Thursday, May 17, 2012 | ![]() |
| You all may go to Hell and I will go to Texas. – Davy Crockett | ||
|
| ← The Spirit of C | http://www.electoral-vote.com/ → |

Selkirk (http://www.procata.com/blog/) wrote:
Ian Bicking (http://blog.ianbicking.org) wrote:
This all seems to complicate things. Most templates are pull, in effect, though that depends on the capabilities of the underlying language to some degree. If you want "pull" in a Python-based template, you typically just need to implement an object with a _getitem_ method. Or in a template with expressions (Cheetah, ZPT, and many others) you just use those expressions to "pull" out values. Things like caching results can be done, as needed, outside of the template. There's lots of code out there to do generalized memoizing in Python.
Keith (http://keithdevens.com/) wrote:
Hey, awesome. I didn't know anyone else had thought of this before. Thanks for the pointers. The page makes a few points that I hadn't thought of, one of them being that the issue with option 2 I outlined above, that "you may wind up caching more data than you actually need for the request", is a problem that plagues push templating as well.
However, as I'm reading, it appears that the discussions of pull templating aren't covering what I consider to be pull templating. In other words, what I mean by "pull" is different than what they mean by "pull". The best way I can think to explain the difference is to highlight the difference between "retrieving" and "finding". The examples of pull templating on the TemplateView page as well as in the PDF linked at the bottom both have to do with retrieval, not discovery. As you can see in the example on the TemplateView page:
<?php
$Page =& new Template('/list.html');
$List =& $Page->findChild('ExampleList');
$List->setDataSet(NewRecordSet('SELECT Name, Description FROM phpmodules'));
$Page->display();
?>
They're still pushing the data into the template, it just happens to be retrieved on template execution rather than before. It's still "push" even though the data is a lazily-built iterator rather than a normal array.
Keith (http://keithdevens.com/) wrote:
Ian, the example with Cheetah you offered is the best example I can think of. As Cheetah's NameMapper goes through the SearchList, it'll request the variable reference in the template from any object in the SearchList. If an object in the list "knows" how to find any data that might be requested, that's the ideal implementation of "pull". Python, as usual, makes things easy. 
As I mentioned above, I was planning on implementing pull-style templates this way in Cheetah. I misspoke slightly above, however. I was planning on modifying Cheetah, but I remembered it wasn't to get pull-style templates, because that can be done without modifying Cheetah. I was planning on modifying it and submitting a patch to get rid of the horrible (IMO) errorCatcher implementation.
Harry Fuecks (http://www.phppatterns.com) wrote:
WACT uses a mix of push and pull. For that example you pointed out;
<?php
$Page =& new Template('/list.html');
$List =& $Page->findChild('ExampleList');
$List->setDataSet(NewRecordSet('SELECT Name, Description FROM phpmodules'));
$Page->display();
?>
The template 'list.html' might contain something like;
<body>
<h1>The results...</h1>
<list:LIST id='ExampleList'>
<ul>
<list:ITEM>
<li>{$Name}: {$Description}</li>
</list:ITEM>
</ul>
</list:LIST>
In other words in PHP you push a dataset to the template without identifying what the template should do with the variables contained in the dataset.
It's in the template itself you name the variables you want to pull out of the dataset for display.
WACT recently got a more advanced "data binding" (i.e. pull) language for the templates - more than just {$varname}. It's described a little here. This is getting applied in some of the "datatable*" templates here.
Enough already.
Clayton Scott wrote:
I think that Template::Toolkit=http://template-toolkit.org/ for Perl is a "Pull Template" according to your definition:
Your controller must provide some data to the template but
the template takes it from there requesting as much or as little as it needs, by being able to call methods on objects or dereference complex data structures.
In your cotroller:
my %data = ( navigation => $nav_object,
data => $some_deep_complex_datastruct,
ExampleList => NewRecordSet('SELECT Name, Description FROM phpmodules');
);
my $t = Template->new('path/to/template', \%data );
And your template:
<li>
[% FOREACH module IN ExampleList %]
<li>[% module.name %] : [% module.description %]
[% END %]
</li>
Keith (http://keithdevens.com/) wrote:
Nope, that's still not pull as I meant it. That's the same as WACT where you're still pushing, you're just giving an iterator instead of the full data set.
Harry Fuecks (http://www.phppatterns.com) wrote:
True the example pushes an iterator to the template but the names of the variables from each row in the result set only exist in the template - it pulls them out of each row.
Perhaps what the example doesn't make clear is it will also work if the SQL query is;
'SELECT * FROM phpmodules'
instead of naming columns in the query.
Only the template knows the names of the variables it wants from each row - the controller doesn't know this, as you were saying;
"the controller doesn't have to know what variables your template needs"
There are other examples in WACT where data can be loaded with the controller knowing absolutely nothing about it, using the core:import tag e.g.;
<body>
<h1 align="center">Import Example</h1>
<p>This is an example of using the IMPORT tag to import
values from a var file into the current template.</p>
<!-- Imports data from file at compile time -->
<core:IMPORT file="favorites.vars">
<p>Favorite Fruit: {$Fruit}</p>
<p>Favorite Vegatable: {$Vegatable}</p>
<p>Favorite Color: {$Color}</p>
</body>
The imported file looks like;
Color = Blue
Fruit = Apple
Vegatable = Carrot
Thats a specific instance though.
In general think a mix of push and pull works well. Pure push generally means excessive work on the controller side - the "Pinhole API" while pure pull requires, to be useful, will likely require a fairly complex template language for locating data (e.g. XSLT ish).
There is an example of a pure pull template engine, now I come to think of it, with eZ publish 3.x. Have a look here. Generally eZ publish 3.x wants to keep users completely away from controllers. The template language refers to a "content object" hierarchy (stored in a database).
henq wrote:
What you need for true pull are imho:
This means you dont need statements in the controller to push the vars-t-display into the template.
The solution is an dot-notation that maps to the datastructure of the app. Template Tooolkit (perl) does this. Tapestry too, with http://www.ognl.org.
This way you don't need special vars in the controller to catch the submitted form. (your objects may be form-handling aware by providing 'old' values of each bound property until the new values all validate).
So the template can call methods (i.e. to fetch data from a db) in a controlled way. (sql in the template is imo verboten). Using the dot notation and asuming the data is an graph of objects, we could just call a method from our template like this:
session.currentuser.logout
logout can be a method, if it returns a value we want to display, we can do that too.
http://jakarta.apache.org/tapestry/ is a web application framework that uses 'true pull' in the way I tried to outline above. (is at least my concusion, I am not familiar with it in detail).
Tapestry has another feature which is very sophisticated, I have to explain by an example. Say you display a number of rows of records from a table (a very common thing). On each row you have a link 'Delete' that deletes that record. The usual way to this is to fumble things in such a way that in the end behind each 'Delete' link there is an url with the record key encoded, like:
.....&id=328
In Tapestry this is not needed. When Tapestry displayes the rows (actally objects) it creates temporary id that identifie these objects by their position in the total object-graph. The 'Delete' link contains something like
....22.4.37.delete
where the numbers point to runtime generated objects. (Tapestry is Java based, so think only objects). So you get a lot of household chore done for you, but this way of doing has it drawbacks too, see http://jakarta.apache.org/tapestry/doc/DevelopersGuide/pages.stale-links.html (Directlink is similar to the &id=328 way of doing, Actionlink is pure OO).
Tapestry's template syntax has some nice touches too. <span> can be used, and the spanned data is replaced by the actual value, This means the template designer can put sample data between the tags, so the templates have a good preview at design time:
<span ...>Test text</span>
henq
Keith Gaughan (http://talideon.com/) wrote:
Keith, your StructuredText parser is after screwing up. In comment #5312, just after "Access to all data from within the template", Mozilla reports that it's expecting a </p> tag and refuses point blank to display the page.
I need to use IE to post this. Eww, I feel all dirty!
Keith (http://keithdevens.com/) wrote:
Fixed. Thanks. I gotta replace this code.
Comments closed.
Generated in about 0.215s.
(Used 8 db queries)
You may want to take a look at the Template View pattern page for the WACT project. This page talks about push versus pull templates. We have added many pull features to WACT. We have a "predefined property" feature, that lets an application define a property in terms of code that is called only if the property is referenced in the template. We also encourage assigning iterators and lazy loading objects into the template instead of building arrays up front. There is a link to an article at the bottom of that page that talks about push vs. pull templates and argues that pull templates are bad.