RedZone is a lightweight C++ template engine with Jinja2-like syntax support. Inspired by microtemplates. This is the starting point for other languanges extensions (e.g. Python). Data exchange is provided via JSON protocol.
To build RedZone library you should have installed GCC 4.9 as well as CMake tool. Execute this shell script:
$ git clone https://github.com/jcfromsiberia/RedZone && cd RedZone && mkdir build && cd build && cmake .. && make
You'll need the same tools with Python dev package and Cython.
Use -DBUILD_LANG_EXTENSION=python
flag for cmake command:
$ cmake -DBUILD_LANG_EXTENSION=python .. && make
You'll find built RedZone.so python module in the build dir.
There are equivalent examples for C++ and Python.
Link RedZone library to your application or library and use these following classes inside:
This is an abstract class for output. It has only two pure virtual methods
- void write( std::string const & data )
- void flush()
There are obly two classes which implement this abstract class:
Writes string data to file on disk. Constructor accepts a path to output file.
RedZone::FileWriter writer( "/tmp/out.html" );
Writes string data to in-memory string. Constructor accepts a reference to certain string.
std::string out;
RedZone::FileWriter writer( out );
There is only one working class RedZone::FileTemplate
to load a template from filesystem by path for the time being.
RedZone::FileTemplate tpl( "/tmp/test.tpl" );
This code creates reads file contents from the given path (e.g. /tmp/test.tpl
), parses it and then creates a new RedZone::Template
instance.
RedZone uses JSON protocol for storing the rendering context. To implement JSON protocol, I have used json11.
json11::Json json( json11::Json::parse( R"(
{
"items": [
{ "text": "Hello World!", "active": true },
{ "text": "Foo", "active": true },
{ "text": "Bar", "active": false }
],
"numbers": {
"first": 5,
"second": 11,
"third": true
}
}
)", err ) );
Rendering context accepts json object-type instances only. You can not pass arrays or something else.
RedZone::Template
classes have render
method to render the template with user context into a stream
represented as pointer to Writer
isntance.
RedZone::Context context( json );
tpl.renderToStream( &writer, &context);
context.setJson( anotherJson );
Also they have render
method, which returns complete rendered output. It uses StringWriter
inside.
std::string rendered = tpl.render( &context );
RedZone supports a primitive code evaluation support. It supports main expression operations like:
- math expressions;
- function calls;
- string operations;
- comparison operations.
You can use them in variable blocks; as bool condition in 'if' block; as container expression in for block.
So, having this markup in /tmp/test.tpl
:
{% for item in items %}
{% if item.active && length(item.text) == 3 %}
<div class="active">{{ item.text }}</div>
{% else %}
<div class="inactive">{{ item.text }}</div>
{% endif %}
{% endfor %}
// Testing expression parser
{{ "f" * 8 + "u" * 8 + "~" }} should be ffffffffuuuuuuuu~
{{upper("f"*8+"u"*8+"~")}} should be FFFFFFFFUUUUUUUU~
{{ sin( 0 )}} should be 0
{{ cos( 0 ) }} should be 1
{{ length( [] ) }} should be 0
{{ length( [ 2, 4, { "s": 55 } ] ) }} should be 3
{{ length( { "key1": "value1", "key2": "value2" } ) }} should be 2
{{ 16 / 4 }} should be 4
{{ get( { "key1": "value1", "key2": "value2" }, "key2" ) }} should be value2
{{ get( numbers, "first" ) }} should be 5
{{ get( numbers, "second" ) }} should be 11
{{ get( numbers, "third" ) }} should be true
{{ get( numbers, "foo" ) }} should be null
{{ ( 2 + 2 ) * (2 + 2) }} should be 16
{% cache 5000 "TestCache" items %}{{ random(20, 100500) }}{% endcache %} should be {% cache 5000 "TestCache" items %}{{ random(20, 100500) }}{% endcache %} cached
and the context which is represented above , we will get this output:
<div class="inactive">Hello World!</div>
<div class="active">Foo</div>
<div class="inactive">Bar</div>
// Testing expression parser
ffffffffuuuuuuuu~ should be ffffffffuuuuuuuu~
FFFFFFFFUUUUUUUU~ should be FFFFFFFFUUUUUUUU~
0 should be 0
1 should be 1
0 should be 0
3 should be 3
2 should be 2
4 should be 4
value2 should be value2
5 should be 5
11 should be 11
true should be true
null should be null
16 should be 16
95199 should be 95199 cached
Function name | Arguments | Return value |
---|---|---|
sin | val numeric |
(numeric) sine of val radians |
cos | val numeric |
(numeric) cosine of val radians |
length | val string or array or object |
(numeric) length value of val Json object |
not | val bool |
(bool) not val |
get | val string or array or objectkey string or numeric |
(any value or null) child item of val by key |
lower | val string |
(string) lowered val |
upper | val string |
(string) uppered val |
random | a numericb numeric |
(numeric) random number value in range [a, b] |
Operator | Left value | Right value | Return value |
---|---|---|---|
+ | lhs numeric |
rhs numeric |
(numeric) addition of lhs and rhs |
+ | lhs string |
rhs string |
(string) concatenated string of lhs and rhs |
- | lhs numeric |
rhs numeric |
(numeric) subtraction of rhs from lhs |
* | lhs numeric |
rhs numeric |
(numeric) multiplication of lhs and rhs |
* | lhs string |
rhs numeric |
(string) lhs string repeated rhs times |
/ | lhs numeric |
rhs numeric |
(numeric) division of lhs by rhs |
> | lhs any object |
rhs any object |
(bool) comparison of lhs > rhs . Calls json11::Json approperiate operator |
< | lhs any object |
rhs any object |
(bool) comparison of lhs < rhs . Calls json11::Json approperiate operator |
== | lhs any object |
rhs any object |
(bool) comparison of lhs == rhs . Calls json11::Json approperiate operator |
!= | lhs any object |
rhs any object |
(bool) comparison of lhs != rhs . Calls json11::Json approperiate operator |
>= | lhs any object |
rhs any object |
(bool) comparison of lhs >= rhs . Calls json11::Json approperiate operator |
<= | lhs any object |
rhs any object |
(bool) comparison of lhs <= rhs . Calls json11::Json approperiate operator |
&& | lhs any object |
rhs any object |
(bool) logical AND of lhs and rhs |
|| | lhs any object |
rhs any object |
(bool) logical OR of lhs and rhs |
All operators are being executed in this order:
- *, /
- +, -
-
, <, ==, !=, <=, >=
- &&, ||
Having built RedZone python module, you can use the above classes in your own Python scripts, excepting there is no Json class -- Python dict replaces it completely.
from RedZone import *
context = Context({
"items": [
{ "text": "Hello World!", "active": True },
{ "text": "Foo", "active": True },
{ "text": "Bar", "active": False }
],
"numbers": {
"first": 5,
"second": 11,
"third": True
}
})
tpl = FileTemplate('test.tpl')
print tpl.render(context)
You get the same output.
extend template syntax with template inheritancedonemake Cython extension to use RedZone from Pythondone- add install/setup scripts
- add an ability to add custom functions in Python
- add an ability to add custom tags in Python