| 1 |
98 |
abw |
#======================================================================== |
| 2 |
|
|
# |
| 3 |
|
|
# TODO |
| 4 |
|
|
# |
| 5 |
|
|
# DESCRIPTION |
| 6 |
1167 |
abw |
# TODO list for the Template Toolkit version 2.20, containing |
| 7 |
98 |
abw |
# known bugs, limitations, planned enhancements, long term visions |
| 8 |
1167 |
abw |
# and a few whacky ideas. Development on TT2 has effectively |
| 9 |
|
|
# ceased for everything but bug fixes. All new features and general |
| 10 |
|
|
# enhancements are being saved for TT3. |
| 11 |
98 |
abw |
# |
| 12 |
|
|
# AUTHOR |
| 13 |
1167 |
abw |
# Andy Wardley <abw@wardley.org> |
| 14 |
98 |
abw |
# |
| 15 |
|
|
#======================================================================== |
| 16 |
26 |
abw |
|
| 17 |
1136 |
abw |
|
| 18 |
|
|
#------------------------------------------------------------------------ |
| 19 |
98 |
abw |
# Miscellaneous |
| 20 |
43 |
abw |
#------------------------------------------------------------------------ |
| 21 |
|
|
|
| 22 |
719 |
abw |
* The 'eval' filter leaks memory, as reported by Colin Johnson. The |
| 23 |
|
|
filter subroutine created contains a reference to the context and then |
| 24 |
|
|
gets cached in the FILTER_CACHE item of the context. Hey presto - |
| 25 |
|
|
circular references. The reset() method should probably clear the |
| 26 |
|
|
FILTER_CACHE. Also need to check the plugins cache for similar |
| 27 |
1167 |
abw |
problems. UPDATE: this may now have been fixed. |
| 28 |
719 |
abw |
|
| 29 |
124 |
abw |
* The handling of the DELIMITER parameter could be improved. At the |
| 30 |
|
|
moments it's hardcoded and hacked to Do The Right Thing for Win32 |
| 31 |
|
|
but I'd prefer it to Do The Good Thing. |
| 32 |
27 |
abw |
|
| 33 |
719 |
abw |
* If you use 'ttree' with a COMPILE_EXT or COMPILE_DIR option then |
| 34 |
|
|
templates in the 'lib' directories will be compiled, but those in |
| 35 |
|
|
the src directories will not. This is because ttree does a chdir() |
| 36 |
|
|
to the src directory and processes files as './myfile'. TT doesn't |
| 37 |
|
|
compile RELATIVE files by default. |
| 38 |
|
|
|
| 39 |
66 |
abw |
* No recursion checking is performed for BLOCKs, only |
| 40 |
27 |
abw |
Template::Document instances. This is probably the way it will stay |
| 41 |
|
|
(unless anyone shouts loudly enough) but it should be documented |
| 42 |
66 |
abw |
anyway. STOP PRESS: I had an idea that bare BLOCK subs should be |
| 43 |
|
|
blessed into Template::Document class to allow $template->process() |
| 44 |
|
|
to be called regardless. Template::Document methods would need to |
| 45 |
|
|
test $self for CODE/HASH and Do The Right Thing. This would then |
| 46 |
|
|
allow recursion testing for BLOCKs as well as Template::Document |
| 47 |
|
|
objects. |
| 48 |
27 |
abw |
|
| 49 |
131 |
abw |
* It would be nice if there was an option so that the files generated |
| 50 |
|
|
under the COMPILE_DIR are relative to the INCLUDE_PATH and not absolute. |
| 51 |
|
|
This could cause potential conflicts (e.g. if INCLUDE_PATH changes |
| 52 |
|
|
between sessions and the same files in different INCLUDE_PATH dirs |
| 53 |
|
|
maps to the samed compiled version) but is convenient for those times |
| 54 |
|
|
when you know that's not going to be a problem. |
| 55 |
121 |
abw |
|
| 56 |
1002 |
abw |
* Craig Barratt notes, in fixing the problem with NEXT not working |
| 57 |
138 |
abw |
inside SWITCH (see Changes v2.04): |
| 58 |
|
|
|
| 59 |
|
|
By the way, I came across another arcane bug: |
| 60 |
|
|
|
| 61 |
|
|
NEXT FOREACH k = [1]; |
| 62 |
|
|
|
| 63 |
|
|
is legal syntax but is an infinite loop, since $_[0]->{ INFOR } in |
| 64 |
|
|
Parser.yp is not set when the NEXT is parsed, so it generates a |
| 65 |
|
|
plain "next;" rather than calling $factor->next(). I don't see an |
| 66 |
|
|
easy, clean fix. |
| 67 |
|
|
|
| 68 |
|
|
|
| 69 |
98 |
abw |
#------------------------------------------------------------------------ |
| 70 |
|
|
# Documentation |
| 71 |
|
|
#------------------------------------------------------------------------ |
| 72 |
|
|
|
| 73 |
|
|
* Extend the FAQ. |
| 74 |
|
|
|
| 75 |
|
|
|
| 76 |
|
|
#------------------------------------------------------------------------ |
| 77 |
|
|
# Directives |
| 78 |
|
|
#------------------------------------------------------------------------ |
| 79 |
|
|
|
| 80 |
|
|
* A 'FOR', like 'FOREACH' but without using an iterator. You wouldn't get |
| 81 |
|
|
the 'loop' reference to test 'first', 'last', etc., against, but it would |
| 82 |
|
|
be faster for those cases when you didn't need it. This will likely |
| 83 |
|
|
be implemented as a facility feature (see later). |
| 84 |
|
|
|
| 85 |
|
|
* PRINT should be defined as a new directive, doing what the print() |
| 86 |
|
|
method of Template::View currently does (the Right Thing). |
| 87 |
|
|
|
| 88 |
|
|
[% PRINT node %] === [% tt.view.print(node) %] |
| 89 |
|
|
|
| 90 |
|
|
NOTE TO SELF: this is a Very Good Idea [tm]. PRINT becomes the way to |
| 91 |
|
|
display a data structure (e.g. hash, list, XML element, MyThingy, database |
| 92 |
|
|
record, etc.) in an "intelligent" fashion. Implemented underneath via |
| 93 |
|
|
the current default VIEW. |
| 94 |
|
|
|
| 95 |
|
|
* ARGS. There may be a requirement for reusable template components |
| 96 |
|
|
to define what variables they plan to use. This would allow some |
| 97 |
|
|
optimisation and also possibly help to avoid global variable clashes. |
| 98 |
|
|
Would also be a useful "comment" directive for human readers and maybe |
| 99 |
|
|
also help in debugging (WARNING: expected 'title' argument). |
| 100 |
|
|
|
| 101 |
|
|
[% ARGS title # no default |
| 102 |
|
|
bgcol='#ffffff' # default value |
| 103 |
|
|
%] |
| 104 |
|
|
|
| 105 |
|
|
|
| 106 |
|
|
#------------------------------------------------------------------------ |
| 107 |
|
|
# Parser |
| 108 |
|
|
#------------------------------------------------------------------------ |
| 109 |
|
|
|
| 110 |
1169 |
abw |
* Lists don't accept arbitrary expressions as elements, although |
| 111 |
|
|
function arguments now do. So you can do this: [% foo(bar + 1) %], |
| 112 |
|
|
but you can't do this: [% foo = [bar + 1] %]. This has been fixed in |
| 113 |
|
|
the v3 parser. |
| 114 |
98 |
abw |
|
| 115 |
73 |
abw |
* The parser isn't as intelligent as it could be about blocks of template |
| 116 |
|
|
code commented out en masse. The pre-scanner find the first terminating |
| 117 |
98 |
abw |
END_TAG after an opening tag, regardless of it being on a |
| 118 |
73 |
abw |
commented line or not. |
| 119 |
|
|
e.g. |
| 120 |
|
|
[%# |
| 121 |
|
|
# |
| 122 |
|
|
# [% INCLUDE blah %] <- directive ends here |
| 123 |
|
|
# foo <- this gets printed |
| 124 |
|
|
%] |
| 125 |
28 |
abw |
|
| 126 |
138 |
abw |
* Craig Barratt reports the following: |
| 127 |
98 |
abw |
|
| 128 |
138 |
abw |
I looked at Parse.yp to see how hard it would be to push FILTER |
| 129 |
|
|
evaluation down into the expr rule, so that you could put filters |
| 130 |
|
|
inside expressions (eg: using repeat() just like "x" in |
| 131 |
|
|
perl). More about that later. |
| 132 |
|
|
|
| 133 |
|
|
In browsing through Parser.yp I noticed several issues: |
| 134 |
|
|
|
| 135 |
|
|
- The operator precedence is very different to perl, C etc. |
| 136 |
|
|
For example, these expressions evaluate differently in |
| 137 |
|
|
TT2 versus perl, C etc: |
| 138 |
|
|
|
| 139 |
|
|
+ "1 || 0 && 0" evaluates to 0 in TT2 and 1 in perl or C. |
| 140 |
|
|
TT2 parses it as (1||0) && 0; in perl and C && is higher |
| 141 |
|
|
precedence than ||. |
| 142 |
|
|
|
| 143 |
|
|
+ "1 + !0 + 1" evaluates to 1 in TT2 and 3 in perl or C. |
| 144 |
|
|
TT2 parses it as 1 + !(0 + 1); in perl and C ! is higher |
| 145 |
|
|
precedence than +. |
| 146 |
|
|
|
| 147 |
|
|
+ Many other expressions parse incorrectly, but the effect |
| 148 |
|
|
is benign since most rules return flat text that perl |
| 149 |
|
|
correctly re-parses. Eg, 2 * 3 + 4 is incorrectly parsed |
| 150 |
|
|
as (2 * (3 + 4)), but happily just the string "2 * 3 + 4" |
| 151 |
|
|
is compiled by perl, which correctly evaluates it as |
| 152 |
|
|
(2 * 3) + 4. |
| 153 |
|
|
|
| 154 |
|
|
- There is no unary minus and the NUMBER token is signed. So you can |
| 155 |
|
|
write "x = -2;" but not "x = -y;". Moreover, "x = 1 -1;" is a syntax |
| 156 |
|
|
error (since "1 -1" returns just two tokens NUMBER, NUMBER). (As a |
| 157 |
|
|
workaround you can rewrite these as "x = 0-y;" and "x = 1 - 1".) |
| 158 |
|
|
|
| 159 |
|
|
- You cannot have expressions in lists ([..]) and function arguments. |
| 160 |
|
|
|
| 161 |
|
|
I have modified the Parser.pm (to make NUMBER unsigned) and modified |
| 162 |
|
|
Grammar.pm.skel and Parser.yp to fix most of these issues (improved |
| 163 |
|
|
operator precedence, unary minus and plus), and also to allow |
| 164 |
|
|
expressions in a few more places (eg: range). But the last item |
| 165 |
|
|
has me stuck. |
| 166 |
|
|
|
| 167 |
|
|
The parse rules for lists and function arguments make COMMA optional, |
| 168 |
|
|
so you can equivalently write [1 2 3 4] or [1,,,,,2 3 4] or [1,2,3,4]. |
| 169 |
|
|
This makes it very difficult to make each term an expression, because |
| 170 |
|
|
the resulting grammar has many ambiguities. For example, is [1 -1] |
| 171 |
|
|
two elements [1, -1] or a single element [0]? One partial solution is |
| 172 |
|
|
to move the bracketed expression rule '(' expr ')' to the term rule, |
| 173 |
|
|
allowing expressions to be included via parens. But there are also |
| 174 |
|
|
ambiguities, eg: does [foo (1+1)] have 2 elements or is it a function |
| 175 |
|
|
call to foo? |
| 176 |
|
|
|
| 177 |
|
|
Without allowing expressions in lists or function arguments, the unary |
| 178 |
|
|
minus change I've made means that the NUMBER token is unsigned, so with |
| 179 |
|
|
my changes you cannot write [-1, 2, 3]. Not a good thing. |
| 180 |
|
|
|
| 181 |
|
|
One solution is to change the grammar so that COMMAs are required in |
| 182 |
|
|
lists and arguments, but that would break several test cases and |
| 183 |
|
|
probably break lots of old templates. But this might be the only |
| 184 |
|
|
way to produce a grammar that is a lot more similar to perl. |
| 185 |
|
|
|
| 186 |
|
|
Another solution is to ignore these issues altogether and use temporary |
| 187 |
|
|
variables to precompute expressions that you need in lists or function |
| 188 |
|
|
arguments, or use explicit lvalue assignments, eg: |
| 189 |
|
|
|
| 190 |
|
|
foo(x + 2); becomes temp = x + 2; |
| 191 |
|
|
foo(temp); |
| 192 |
|
|
|
| 193 |
|
|
or |
| 194 |
|
|
|
| 195 |
|
|
List = [x+1,x+2,x+4]; becomes List = []; |
| 196 |
|
|
List.0 = x+1; |
| 197 |
|
|
List.1 = x+2; |
| 198 |
|
|
List.2 = x+4; |
| 199 |
|
|
|
| 200 |
|
|
Both of these look ugly to me. |
| 201 |
|
|
|
| 202 |
|
|
Back to the FILTER issues. Ultimately I'd like to be able to embed filters |
| 203 |
|
|
as low precedence operators in expressions, and write: |
| 204 |
|
|
|
| 205 |
|
|
List = [ |
| 206 |
|
|
"foo" | repeat(10), |
| 207 |
|
|
"bar" | repeat(10) |
| 208 |
|
|
]; |
| 209 |
|
|
|
| 210 |
|
|
but I doubt there is a non-ambiguous upward compatible grammar that |
| 211 |
|
|
supports this. |
| 212 |
|
|
|
| 213 |
|
|
Comments? |
| 214 |
|
|
|
| 215 |
|
|
|
| 216 |
78 |
abw |
#------------------------------------------------------------------------ |
| 217 |
98 |
abw |
# Plugins |
| 218 |
27 |
abw |
#------------------------------------------------------------------------ |
| 219 |
|
|
|
| 220 |
98 |
abw |
* We need a way to easily enable/disable certain plugins. This should |
| 221 |
131 |
abw |
be addressed by facility provision. Probably something for v3. |
| 222 |
98 |
abw |
|
| 223 |
|
|
* The Template::Plugin DBI iterator first/last() methods don't behave |
| 224 |
|
|
the same as list first/last(). Randal also reports that get_all() |
| 225 |
1002 |
abw |
doesn't work as it should - may be a conflict in code/docs? Again, |
| 226 |
|
|
this is a problem to solve in TT3. |
| 227 |
98 |
abw |
|
| 228 |
66 |
abw |
* PLUGINS could accept a reference to an object which is used as a |
| 229 |
98 |
abw |
singleton factory for a plugin. (NOTE: 2.01 includes PLUGIN_FACTORY |
| 230 |
|
|
to implement this, but currently undocumented because it's likely to |
| 231 |
|
|
change). |
| 232 |
66 |
abw |
|
| 233 |
131 |
abw |
* A more general solution for XML (e.g. DOM, XPath, etc) would be for |
| 234 |
|
|
TT to support a PerlSAX handler which generates the appropriate |
| 235 |
|
|
callbacks to the view. This should make it possible to easily |
| 236 |
|
|
display XML content from XML::DOM, XML::XPath, or any other SAX |
| 237 |
|
|
compliant source. |
| 238 |
66 |
abw |
|
| 239 |
98 |
abw |
Something like this: |
| 240 |
70 |
abw |
|
| 241 |
98 |
abw |
# define a view |
| 242 |
|
|
[% VIEW my_view |
| 243 |
|
|
prefix="my/xml/dom/path/" ; |
| 244 |
|
|
END |
| 245 |
|
|
%] |
| 246 |
70 |
abw |
|
| 247 |
98 |
abw |
# get some XML |
| 248 |
|
|
[% USE dom = XML.DOM %] |
| 249 |
|
|
[% doc = dom.parser(my.files.xmldata) %] |
| 250 |
|
|
|
| 251 |
|
|
# ask the view to print the data |
| 252 |
|
|
[% my_view.print(doc) %] |
| 253 |
27 |
abw |
|
| 254 |
98 |
abw |
The view print() method will call the relevant 2SAX method on the |
| 255 |
|
|
XML node, passing a SAX2TTView handler to make the relevant calls |
| 256 |
|
|
back to the view to display parts of the XML data model as SAX events |
| 257 |
|
|
are received. |
| 258 |
27 |
abw |
|
| 259 |
|
|
|
| 260 |
98 |
abw |
#------------------------------------------------------------------------ |
| 261 |
|
|
# Views |
| 262 |
|
|
#------------------------------------------------------------------------ |
| 263 |
66 |
abw |
|
| 264 |
98 |
abw |
The current implementation is there to get me (and anybody else who's |
| 265 |
|
|
interested) using it and trying to identify the problems, requirements |
| 266 |
|
|
and general issues involved. I've got a better idea now about what a |
| 267 |
|
|
VIEW should be in notional terms, but I'm still not quite sure about |
| 268 |
|
|
the syntax and API. |
| 269 |
11 |
abw |
|
| 270 |
98 |
abw |
General thoughts: |
| 271 |
|
|
|
| 272 |
|
|
* A view defines a set of templates. Things like prefix, suffix, |
| 273 |
|
|
default, etc., can be specified to customise template selection. |
| 274 |
|
|
In this sense, it is like a custom provider of those templates. |
| 275 |
|
|
It implements the template() method to fetch a template according |
| 276 |
|
|
to those rules. |
| 277 |
|
|
|
| 278 |
|
|
* It is also a custom processor of those templates. It implements the |
| 279 |
|
|
process() method. In this sense, it is like a custom context. |
| 280 |
|
|
|
| 281 |
|
|
* It also implements dispatch logic to apply the right template to the |
| 282 |
|
|
right kind of data. It does this via the print() method. It may |
| 283 |
|
|
have all kinds of custom dispatch logic. |
| 284 |
|
|
|
| 285 |
|
|
* A view takes responsiblity for things template related as opposed |
| 286 |
|
|
to anything data related (stash) or application logic related |
| 287 |
|
|
(plugins, runtime code, etc). It is the user interface facility |
| 288 |
|
|
within the engine. |
| 289 |
|
|
|