[pmwiki-users] pagelist template brainstorming

Patrick R. Michaud pmichaud at pobox.com
Sat Feb 17 23:46:26 CST 2007


Today I had an idea about improving pagelist templates, and
would like some comments and feedback.  As many of you know,
pagelist templates can be a bit slow, and I think I have
an idea that could speed them up as well as make them easier
to create.  (If you don't do much with pagelist templates,
feel free to skip this message.  :-)

For purposes of this message, consider the following template
to create a table of page names, authors, and modification dates,
with header rows at the top and colspan rows to mark the beginning 
of each group:

    [[#template]]
    (:if equal {<$Group}:)
    ||width=100%
    ||!Page         ||!Author            ||!Modified        ||
    (:if equal {<$Group} {=$Group}:)
    ||{=$Group}     ||||||
    (:if:)
    ||{=$FullName}  ||{=$LastModifiedBy} ||{=$LastModified} ||
    (:if equal {>$Group}:)
    {$$PageCount} pages.
    [[#templateend]]

Of course, the (:if:) lines are the canonical (and quite common)
constructs that people use to identify sections for

    the first page in the pagelist   (:if equal {<$Group} :)
    the first page of a wikigroup    (:if equal {<$Group} {=$Group}:)
    each individual page             (:if:)
    the last page of the pagelist    (:if equal {>$Group} :)

It works fine, but isn't all that desirable from a performance
perspective, because all of the sections are included and processed
for every page in the list.  In other words, if a pagelist has 
1000 pages over 10 wikigroups, then the above template produces 
9000 lines of markup to be processed, even though most of the lines
will eventually be discarded by the (:if:) statements.

Thus I'm wondering if we can improve things for both performance 
and readability by introducing special-purpose (:first:), (:each:),
and (:last:) directives inside of pagelist templates.

The (:first:) directive would identify markup prior to the first 
page in the pagelist, (:each:) would be markup used for each page 
in the list, and (:last:) would contain markup used after the 
last page in the list.  Thus with these markers, our above 
template would read:

    [[#template]]
    (:first:)
    ||width=100%
    ||!Page         ||!Author            ||!Modified        ||
    (:each:)
    (:if equal {<$Group} {=$Group}:)
    ||{=$Group}     ||||||
    (:if:)
    ||{=$FullName}  ||{=$LastModifiedBy} ||{=$LastModified} ||
    (:last:)
    {$$PageCount} pages.
    [[#templateend]]

Now the pagelist template formatter can be much smarter about
producing the markup.  Unlike the previous version where 1000
pages in the list produced 9000 lines of markup, this version 
would end up with only 2 + 1000*4 + 1 == 4003 lines of markup
to process -- a significant decrease.

Also, since it's very common for templates to produce "control breaks"
whenever a value changes (e.g., "the first page in a wikigroup"),
we could improve things even further by allowing (:each:) to take
a parameter that says "output this section whenever the specified
parameter changes value".  Thus:

    [[#template]]
    (:first:)
    ||width=100%
    ||!Page         ||!Author            ||!Modified        ||
    (:each {=$Group}:)
    ||{=$Group}     ||||||
    (:each:)
    ||{=$FullName}  ||{=$LastModifiedBy} ||{=$LastModified} ||
    (:last:)
    {$$PageCount} pages.
    [[#templateend]]

Here, (:each {=$Group}:) says to output the markup that follows
for every page where {=$Group} results in a value different from
the previous iteration.  With this formulation our 1000 page, 10 group 
list now produces 2 + 10*1 + 1000*1 + 1 == 1013 lines of markup, far 
less than 9000 lines of the original template.

I'm a bit concerned that some authors may try to put full fledged
conditionals into (:each:), as opposed to simply values to be
"watched".  So, perhaps (:each:) should take a conditional expression
instead of a value to be watched...

    (:each ! equal {=$Group} {<$Group} :)

But somehow I think it reads better if (:each:) simply watches
a value for control breaks, and that we tell authors to always
use (:if:) when a condtional needs to be evaluated.

Does this approach and markup make sense to others?

Note that all existing pagelist templates will continue to work
exactly as before.  Any template that doesn't contain (:first:), 
(:each:), or (:last:) directives will still act as though the 
entire template is included for each page in the list.  And,
pagelist templates that need to do more complex sorts of
operations can still use (:if:) in the templates as before.

While we're on the topic, another desirable feature we can introduce
with this approach would be the ability to set default pagelist
options from within a pagelist template itself.  For example,
to display a list of page titles in alphabetical order, we
currently have to remember to specify both "fmt=#title" and 
"order=title" in the pagelist directive.  It would be nice if 
"fmt=#title" automatically implied "order=title" as well.  
So, we could add an (:options:) directive to the template, as in

    [[#title]]
    (:options order=title:)
    * [[{=$FullName}|+]]
    [[#titleend]]

Then an author specifying "fmt=#title" in a pagelist command
would automatically get the pages ordered by title unless 
overridden by an explicit "order=" parameter.

So, again my questions are:  Would markups such as these make it
easier or more difficult for people creating pagelist templates?  
And is the (:first:)/(:each:)/(:last:) syntax appropriate, or 
would a different syntax be more desirable?

Thanks in advance for any comments and suggestions,

Pm



More information about the pmwiki-users mailing list