DTL::Fast - Perl implementation of Django templating language.
Version 1.603
Complie and render template from code:
use DTL::Fast; my $tpl = DTL::Fast::Template->new('Hello, {{ username }}!'); print $tpl->render({ username => 'Alex'});
Or create a file: template.txt in /home/alex/templates with contents:
Hello, {{ username }}!
And load and render it:
use DTL::Fast qw( get_template ); my $tpl = get_template( 'template.txt', ['/home/alex/templates'] ); print $tpl->render({ username => 'Alex'});
This module is a Perl and stand-alone templating system, cloned from Django templating sytem, described in here.
Goals of this implementation are:
Speed in mod_perl/FCGI environment
Possibility to cache using files/memcached
Maximum compatibility with original Django templates
Current release implements almost all tags and filters documented on Django site.
There are no speed optimizations done yet.
Internationalization, localization and caching are not yet implemented.
You may get template object using three ways.
Using DTL::Fast::Template constructor:
use DTL::Fast; my $tpl = DTL::Fast::Template->new( $template_text, # template itself 'dirs' => [ $dir1, $dir2, ... ], # optional, directories list to look for parent templates and includes 'ssi_dirs' => [ $ssi_dir1, $ssi_dir1, ...] # optional, directories list to look for files included with ssi tag 'url_source' => \&uri_getter # optional, reference to a function, that can return url template by model name (necessary for url tag) );
use DTL::Fast qw(get_template); my $tpl = get_template( $template_path, # path to the template, relative to directories from second argument 'dirs' => [ $dir1, $dir2, ... ], # mandatory, directories list to look for parent templates and includes 'ssi_dirs' => [ $ssi_dir1, $ssi_dir1, ...] # optional, directories list to look for files included with ssi tag 'url_source' => \&uri_getter # optional, reference to a function, that can return url template by model name (necessary for url tag) );
when you are using get_template helper function, framework will try to find template in following files: $dir1/$template_path, $dir2/$template_path ... Searching stops on first occurance.
get_template
$dir1/$template_path, $dir2/$template_path ...
use DTL::Fast qw(select_template); my $tpl = select_template( [ $template_path1, $template_path2, ...], # paths to templates, relative to directories from second argument 'dirs' => [ $dir1, $dir2, ... ], # mandatory, directories list to look for parent templates and includes 'ssi_dirs' => [ $ssi_dir1, $ssi_dir1, ...] # optional, directories list to look for files included with ssi tag 'url_source' => \&uri_getter # optional, reference to a function, that can return url template by model name (necessary for url tag) );
when you are using select_template helper function, framework will try to find template in following files: $dir1/$template_path1, $dir1/$template_path2 ... Searching stops on first occurance.
select_template
$dir1/$template_path1, $dir1/$template_path2 ...
After parsing template using one of the methods above, you may render it using context. Context is basically a hash of values, that will be substituted into template. Hash may contains scalars, hashes, arrays, objects and methods. Into render method you may pass a Context object or just a hashref (in which case Context object will be created automatically).
render
use DTL::Fast qw(get_template); my $tpl = get_template( 'hello_template.txt', 'dirs' => [ '/srv/wwww/templates/' ] ); print $tpl->render({ name => 'Alex' }); print $tpl->render({ name => 'Ivan' }); print $tpl->render({ name => 'Sergey' });
or
use DTL::Fast qw(get_template); my $tpl = get_template( 'hello_template.txt', 'dirs' => [ '/srv/wwww/templates/' ] ); my $context = DTL::Fast::Context->new({ 'name' => 'Alex' }); print $tpl->render($context); $context->set('name' => 'Ivan'); print $tpl->render($context); $context->set('name' => 'Sergey'); print $tpl->render($context);
To do...
This module supports almost all built-in tags documented on official Django site. Don't forget to read incompatibilities and extensions sections.
New tag, that works like firstof tag, but checks if value is defined (not true)
firstof
url tag works a different way. Because there is no framework around, we can't obtain model's path the same way. But you may pass url_source parameter into template constructor or get_template/select_template function. This parameter MUST be a reference to a function, that will return to templating engine url template by some 'model path' (first parameter of url tag). Second parameter passed to the url_source handler will be a reference to array of argument values (in case of positional arguments) or reference to a hash of arguments (in case of named ones). Url source handler may just return a regexp template by model path and templating engine will try to restore it with specified arguments. Or, you may restore it yourself, alter replacement arguments or do whatever you want.
url
url_source
This module supports all built-in filters documented on official Django site. Don't forget to read incompatibilities and extensions sections.
ssi tag in Django uses absolute paths and ALLOWED_INCLUDE_ROOTS configuration option. This library works separately and may be used with different frameworks. So, ssi tag uses relative paths and you MUST specify additional template constructor parameter: ssi_dirs which should be an array reference with list of dirs to search in.
ssi
ALLOWED_INCLUDE_ROOTS
ssi_dirs
csrf_token tag is not implemented, too well connected with Django.
csrf_token
_dtl_* variable names in context are reserved for internal system purposes. Don't use them.
_dtl_*
output from following tags: cycle, firstof, firstofdefined are being escaped by default (like in later versions of Django)
cycle
firstofdefined
escapejs filter works other way. It's not translating every non-ASCII character to the codepoint, but just escaping single and double quotes and \n \r \t \0. Utf-8 symbols are pretty valid for javascript/json.
escapejs
\n \r \t \0
fix_ampersands filter is not implemented, because it's marked as depricated and will beremoved in Django 1.8
fix_ampersands
pprint filter is not implemented.
pprint
iriencode filter works like urlencode for the moment.
iriencode
urlencode
urlize filter takes well-formatted url and makes link with this url and text generated by urldecoding and than escaping url link.
urlize
wherever filter in Django returns True/False values, DTL::Fast returns 1/0.
True/False
DTL::Fast
1/0
May be some of this features implemented in Django itself. Let me know about it.
filters may accept several arguments, and context variables can be used in them, like {{ var|filter1:var2:var3:...:varn }}
numberformat - new filter. Formats number like 12 345 678.9999999
numberformat
12 345 678.9999999
strftime - new filter. Formats time using Date::Format module, which is using C functions strftime and ctime.
strftime
Date::Format
ctime
firstofdefined - new tag, that works like firstof tag, but checks if value is defined (not true)
defined logical operator. In logical constructions you may use defined operator, which works exactly like perl's defined
defined
alternatively, in logical expresisons you may compare (==,!=) value to undef or None which are synonims
undef
None
slice filter works with ARRAYs and HASHes. Arrays slicing supports Python's indexing rules and Perl's indexing rules (but Perl's one has no possibility to index from the end of the list). Hash slicing options should be a comma-separated keys.
slice
You may use brackets in logical expressions to override natural precedence
forloop context hash inside a for block tag contains additional fields: odd, odd0, even and even0
forloop
for
odd
odd0
even
even0
variables rendering: if any code reference encountered due variable traversing, is being invoked with context argument. Like:
{{ var1.key1.0.func.var2 }}
is being rendered like:
$context->{'var1'}->{'key1'}->[0]->func($context)->{'var2'}
you may use filters with static variables. Like:
{{ "text > test"|safe }}
objects behaviour methods. You may extend your objects, stored in context to make them work properly with some tags and operations:
as_bool - returns logical representation of object
as_bool
and(operand) - makes logical `and` between object and operand
and(operand)
or(operand) - makes logical `or` between object and operand
or(operand)
div(operand) - divides object by operand
div(operand)
equal(operand) - checks if object is equal with operand
equal(operand)
compare(operand) - compares object with operand, returns -1, 0, 1 on less than, equal or greater than respectively
compare(operand)
in(operand) - checks if object is in operand
in(operand)
contains(operand) - checks if object contains operand
contains(operand)
minus(operand) - substitutes operand from object
minus(operand)
plus(operand) - adds operand to object
plus(operand)
mod(operand) - returns reminder from object division to operand
mod(operand)
mul(operand) - multiplicates object by operand
mul(operand)
pow(operand) - returns object powered by operand
pow(operand)
not() - returns object inversion
not()
reverse() - returns reversed object
reverse()
as_array() - returns array representation of object
as_array()
as_hash() - returns hash representation of object
as_hash()
I've compared module speed with previous abandoned implementation: Dotiac::DTL in both modes: FCGI and CGI. Test template and scripts are in /timethese directory. Django templating in Python with cache works about 80% slower than DTL::Fast.
Dotiac::DTL
Template parsing permormance with software cache wiping on each iteration:
Benchmark: timing 5000 iterations of DTL::Fast , Dotiac::DTL... DTL::Fast : 2 wallclock secs ( 1.31 usr + 0.75 sys = 2.06 CPU) @ 2428.36/s (n=5000) Dotiac::DTL: 26 wallclock secs (11.40 usr + 13.01 sys = 24.41 CPU) @ 204.80/s (n=5000)
DTL::Fast parsing templates almost 12 times faster, than Dotiac::DTL.
To run this test, you need to alter Dotiac::DTL module and change declaration of my %cache; to our %cache;.
my %cache;
our %cache;
Rendering of pre-compiled template (software cache):
Benchmark: timing 3000 iterations of DTL::Fast , Dotiac::DTL... DTL::Fast : 7 wallclock secs ( 7.29 usr + 0.00 sys = 7.29 CPU) @ 411.81/s (n=3000) Dotiac::DTL: 13 wallclock secs (12.15 usr + 0.00 sys = 12.15 CPU) @ 246.85/s (n=3000)
Tests shows, that DTL::Fast works 67% faster, than Dotiac::DTL in persistent environment.
This test rendered test template many times by external script, invoked via system call:
system
Benchmark: timing 500 iterations of Dotiac render , Fast render ... Dotiac render : 57 wallclock secs ( 0.16 usr + 0.44 sys = 0.59 CPU) @ 843.17/s (n=500) Fast render : 62 wallclock secs ( 0.22 usr + 0.53 sys = 0.75 CPU) @ 668.45/s (n=500)
Tests shows, that DTL::Fast works 26% slower, than Dotiac::DTL in CGI environment.
1 Cache key : 0 wallclock secs ( 0.19 usr + 0.00 sys = 0.19 CPU) @ 534759.36/s (n=100000) 2 Decompress : 0 wallclock secs ( 0.27 usr + 0.00 sys = 0.27 CPU) @ 377358.49/s (n=100000) 3 Serialize : 4 wallclock secs ( 3.73 usr + 0.00 sys = 3.73 CPU) @ 26824.03/s (n=100000) 4 Deserialize: 5 wallclock secs ( 4.26 usr + 0.00 sys = 4.26 CPU) @ 23479.69/s (n=100000) 5 Compress : 10 wallclock secs (10.50 usr + 0.00 sys = 10.50 CPU) @ 9524.72/s (n=100000) 6 Validate : 11 wallclock secs ( 3.12 usr + 8.05 sys = 11.17 CPU) @ 8952.55/s (n=100000) 7 Parse : 1 wallclock secs ( 0.44 usr + 0.23 sys = 0.67 CPU) @ 1492.54/s (n=1000) 8 Render : 11 wallclock secs ( 9.30 usr + 1.14 sys = 10.44 CPU) @ 95.82/s (n=1000)
Main project repository and bugtracker: https://github.com/hurricup/DTL-Fast
CPAN Testers reports: http://www.cpantesters.org/distro/D/DTL-Fast.html
Testers matrix: http://matrix.cpantesters.org/?dist=DTL-Fast
AnnoCPAN, Annotated CPAN documentation: http://annocpan.org/dist/DTL-Fast
CPAN Ratings: http://cpanratings.perl.org/d/DTL-Fast
Original Django templating documentation: https://docs.djangoproject.com/en/1.7/topics/templates/
Other implementaion: http://search.cpan.org/~maluku/Dotiac-0.8/lib/Dotiac/DTL.pm
This module is published under the terms of the MIT license, which basically means "Do with it whatever you want". For more information, see the LICENSE file that should be enclosed with this distributions. A copy of the license is (at the time of writing) also available at http://www.opensource.org/licenses/mit-license.php.
Copyright (C) 2014-2015 by Alexandr Evstigneev (hurricup@evstigneev.com)
To install DTL::Fast, copy and paste the appropriate command in to your terminal.
cpanm
cpanm DTL::Fast
CPAN shell
perl -MCPAN -e shell install DTL::Fast
For more information on module installation, please visit the detailed CPAN module installation guide.