[Templates] tt annoyances
Mark D. Anderson
mda@discerning.com
Thu, 09 Oct 2003 15:16:24 -0700
So now that I have another day or two of TT under my belt, it is also
getting under my skin, to mix metaphors.
Here are some annoyances I've run across so far, FYI.
Keep in mind this is with the best of intentions; I'm sure TT is the most
mature of the "mini-language" school of perl templating solutions.
1. Google isn't indexing www.template-toolkit.org. For example, try this search:
http://www.google.com/search?q=site%3Awww.template-toolkit.org+include
This is a problem because it undermines my ability to quickly look things up
and so possibly avoid embarrassing myself with emails like this one.
I've got no idea why this is; perhaps by now the google infrastructure has
evolved independent sentience.
2. There is no "typeof" or "instanceof" operator (or similar).
Perl has "ref" and "isa"; practically all real programming languages
have at least one of those operators.
(A lack of such an operator is one of the many subtle problems with XSLT, btw.)
TT allows case statements on exception types, but that is a restricted
solution for just that one situation. The fact that TT has so few generic
functions that apply across object types (see 4 and 5) aggravates this situation.
3. There is (effectively) no "defined" operator.
I refuse to run without undefined variable warnings (in TT, that
means DEBUG=undef). But when that is set, you can't actually use the
[% IF foobar.defined %] idiom, because it gives you a fatal error.
There is no variable that represents the unqualified namespace, so
that you could use local.exists('foobar'); the top-level "global"
variable does not include the local variables.
(Note that it is possible to test object membership because
obj.exists('memname') works even with DEBUG=undef.
Note that obj.memname.defined works only if for members that exist.)
There is also no "undef" builtin constant as in perl.
4. Common string functions are available in counter-intuitive ways.
The builtin scalar string supports: length, repeat, replace, match, search,
split, chunk. Now, suppose I want the equivalent of the perl functions substr,
uc, and index. Where do I find them? Well, substr is available by using the String
plugin, which has no relation to a builtin scalar string:
[% USE String %]
[% String.new(str).truncate(3) %]
This is awkward, not to mention that counter-intuitively "truncate" is a perl
builtin operation on files.
As for uc, we find that via a FILTER, and it is called "upper":
[% str FILTER upper %]
As for index, I don't see that anywhere, so I suppose that I'd have to define
it myself:
[% MACRO index(str,substr,pos) PERL %]
index($stash->get('str'),$stash->get('substr'),$stash->get('pos'))
[% END %]
(Note that a potential solution to this insanity would be similar to how the
POSIX module works in perl. One could perhaps allow:
[% USE Perl %]
[% Perl.substr(str,3) %]
or maybe some syntax like this:
[% ::substr(str, 3) %]
if an option like perl_global_namespace=true is set.)
5. The iterator objects/methods are not generic.
The FOREACH directive manages to treat lists, hashes, and Iterator
objects somewhat uniformly. However, such power
is not extended anywhere else. If I have some template variable "people",
then I have to know implementation details if I want to work with it.
If it came from DBI.query I have to treat it differently than if it is a
simple list. For example, for list objects, first() returns the first element,
while for Iterator objects, first() is a boolean!
If people is an Iterator, i need to do [% people.get_first().0 %], while if
it is a simple list I can do [% people.0 %] or [% people.first() %].
6. You can't call methods directly on literals, for example
[% 'asdf'.length %] or [% [1,3,2].sort %].
Certain common operators such as [% count++ %] and [% count += 1 %]
are not supported.
All these and others are of course a concomitant penalty for going with
the mini-language school of template systems ("We're hurting you
because we love you"...). Still, it hurts.
7. There is no way to comment out a whole block of template lines which includes
other directives. That is, you can't nest things like [%# [% %] %].
I end up having to create a separate emacs buffer to just hold fragments.
In perl this can be done with tricks like "=for IGNORE".
8. A bug: [% SET str = 'a'; str.repeat(0) %] prints 'a', not ''
9. The diagnostics stink. (This was a topic of an earlier email post.)
- You only get undefined variable warnings with DEBUG=undef, and then
it is fatal and gives no file or line information.
- There is no apparent way to trap perl-level warnings from "use strict",
let alone interpolate them into the template.
- There is no way to get warnings on interpolation of non-scalars.
- There is no warning on using "=" in a conditional.
- The errors on mistyped directives such as "[% ELSEIF" are obscure or
non-existent.
10. Boolean interpolation is non-obvious. [%1==2%] interpolates
as '', and [%2==2%] interpolates as '1'. There are also no builtin
constant values true and false.
-mda