New Templates System
New templates system was introduces to the CppCMS framework. It is based on ideas of dynamic typed languages inside static typed C++.
The original template system had several problems:
- The each template variable was referenced by and integer key that was generated during compilation of templates.
- The rendering process required from the developer some kind of activity -- update content values according to requests from rendering engine.
- The values of the entries where limited to string, integer and boolean values.
In any case, the design of the first template system was just unacceptable, thus new template system was build.
It introduced following features:
- Dynamic typed variable values using boost::any. They allow assigning of any kind of objects to the variables and rendering them to the templates using custom engines.
- All the variables are references by their names.
- Content now has hierarchical structure when each variable can include list of items or callbacks that allow one step template rendering.
- The design of the engine is now much more modular.
Additional features I'm still working on them are:
- Support of different filters like "html escaping", "urlizing" etc.
- Support of custom filters, including filter chains.
- Support of localization and translation.
How Does This Works?
I'll introduce a simple example:
Template
<% template mails() %>
Mails from <% name %>
<% foreach mail in mails %>
The mails are:
<% separator %>, <% item %>
<a href="<% mail.url %>"><% mail.name %></a>
<% end %>
<% empty %>
No Mails
<% end %>
<% end %>
C++ Code
content c;
renderer rnd(tmpl_dat);
string out;
c["name"]=string("Yossi");
content::vector_t &data=c.vector("mails",links.size());
for(i=0.ptr=links.begin();ptr!=links.end();ptr++,i++)
{
data[i]["url"]=ptr->urlstr;
data[i]["name"]=ptr->name;
}
rnd.render(c,"mails",out);
In a very simple way I can introduce new type of data to the template system:
MyCoolClass my_data;
renderer rnd(tmpl_dat);
rnd.add_converter(typeid(MyCoolClass),
boost::bind(my_display,_1,_2));
content c;
c["somename"]=my_data;
And now, objects of type MyCoolClass will be rendered using "my_display" function);
As you can see the code is simple and clear end very easy to write and understand.
The templates system is actually an independent library and can be definitely used for any other project that require a creation of complex texts, not limiting to HTML or any other kind of web application.
As before, the template is compiled into simple bytecode that drives the renderer engine and thus do not require any kind of parsing and works very fast.
And how about performance?
For this blog it was about 6% slower then the original template system, and that is understandable because parameters lookup become little bit slower and more memory allocations required for managing boost::any type, but, it is significantly more powerful and easy to use then the previous system. Thus it worth it.
There are still many points that can be improved, especially when filters system will be complete and reduce the amount of memory that should be copied (and simplify the code).
Comments
I didn't understand what <% separator %> and <% item %> are in the template...
If you want to display a list of items sometimes you want to put a separator between them for example "," then you use
and you get "somethig1, something2, something3"
Or you can write
Than it will be "something something something"
hmm... the formatting went to hell. lets try again (any chance for a preview button and a markup tutorial link?)
let's try again. (we said markdown right?)
let's see if I get this straight: In the example above, if "mails" contains the following items: * ('bill', 'http://mail.com/from/bill'), * ('John', 'http://mail.com/from/john') and * ('samuel', 'http://mail.com/from/samuel')
then the output would be: The mails are: bill, john, samuel <!-- no comma at the end -->
is that correct?
I think that the syntax isn't intuitive. for example, i don't see what purpose does item have in the template and why separator doesn't have an "end" block.
what about something like this:
I thought about this. However foreach.last/foreach.first should be independent from the separator.
I mean there should be first/last but IMHO they are less intuitive then "separator" if you need separator only. It is much more error prone to add if last/first then simple to tell "put XYZ between them".
Also what happends if it is both firsts or last?
I have a support to add something before or after all items:
This foreach cover 99% of cases.
To be honest, I didn't add this because it would require from me to write much more complex state machine. It is simple in case of C++ vector or list, but in case of callback function I can't know if it is last one unless I call the function once more. So for each element I need to look "forward". That is not so hard but...
However the new template system is only at it's beginning. And I do plan to add "first/last" and many other features like template inheretence Django style, different filters like autoescaping, date filters, user defined filters etc...
Yes, you are right, I'll add it someday. This blog missing many other features it should have (for example editing comments by admin, but shhhhhh... don't tell anybody).
I just try to work much more on the framework itself and less on the blog. Because it is already quite useful and has 80% of features the blog should have.
even if you stick with the same capabilities, I still have a hard time with your syntax.
1) for starts, if I see a "foreach" block, I assume that everything inside that block is repeated for each item.
2) this template system is supposed to mimic html in a way, so I expect that each opening tag will have a closing tag ('empty' and 'seperator' do not have an ending tag) - BTW, how about <% /foreach %> ?
3) 'item' is a bad name, since it doesn't suggest what happens in that block. if you make the "foreach" block behave the same as programmatic foreach (1), 'item' wouldn't be required, but if you prefer it as it is, you should consider changing the name to something that suggests that this is the part being repeated.
how about: <% using names %>
Names list
<% foreach name in names %>- <% name %><% separator %>and also<% /separator %>
<% /foreach %>
<% /using %> <% else if empty %> <!-- or <% else_empty %> if this 'else' conflicts with the normal 'else' -->No names
<% /else %>this is basically only renaming of the above example, without adding complexity (unless, closing "using" takes you out of the scope of the current state machine)
think about the closing of tags in terms of brackets, the syntax you propose translates in terms to: if (a > 7) { printf(""a == %d\n",a); printf("larger than 7\n"); else { printf("smaller than 7\n"); }
there is only one closing bracket, so the bracket before the else is implied, but this is confusing for both html designers and programmers.
First of all, I'm not writing turing complete lenguage but only a simple way create HTML content, thus foreach look in templates system is different form any other foreach loop
You may see this as:
But "if not empty mails" should be replaced with "foreach", "for" with "item" and "else" with "empty".
Now I'll explain why:
Lets assume a programmer will write: <% if not empty mails %> There are mails <% else %> No mails <% end %>
But what happends? You make a long query that is actually creates all mails list (lets assume 10,000 mails in your box) instead of create one boolean variable with simple query that will only check existence of mails.
In my case, when you ask "foreach" you can not remove "item" -- you'll get syntax error, on the other hand. I mean, you can't ignore the content of the list.
ALso, there is not allways a possibility to check if list is empty unless I request an element -- when the list is actually a callback function.
IMHO this is much more simple or strightforward syntax then adding tens of if/elif/else/endif lists.
About end or /end or endfor or any other, it is a question of taste, and I don't mimic html. I just add tags that can't be valid html I mean <% XYZ %> can never be valid thus I use them. When some kind of {{ }} that are used in Django can be valid.
you're confusing syntax and implementation, the syntax I suggested can be implemented behind the scene the same way as you're syntax, especially if you convert it to that "assembly" language.
I understand the need to keep the state machine simple at this stage (I'm not talking about "foreach.last" anymore), so my only beef with your example is that I think the syntax is counter-intuitive since it gives a very different meaning to a common idiom ("foreach") and since it doesn't have a clear way of ending blocks - some blocks need "end" while other "end"s are implicitly defined ("foreach" and "separator" for example)
A bit late but I'll agree with ido, part of the example is not very intuitive...
Alternative approach to your example:
More or less like you would find it in most template languages like smarty and so on, see eZ Components template doc for more examples:
http://ezcomponents.org/docs/tutorials/Template#comments
Add Comment:
You must enable JavaScript in order to post comments.