Tenjin-1.000001000755001750001750 012716673333 11706 5ustar00idoido000000000000README100644001750001750 3367512716673333 12705 0ustar00idoido000000000000Tenjin-1.000001NAME Tenjin - Fast templating engine with support for embedded Perl. SYNOPSIS use Tenjin; $Tenjin::USE_STRICT = 1; # use strict in the embedded Perl inside # your templates. Recommended, but not used # by default. $Tenjin::ENCODING = "UTF-8"; # set the encoding of your template files # to UTF-8. This is the default encoding used # so there's no need to do this if your # templates really are UTF-8. my $engine = Tenjin->new(\%options); my $context = { title => 'Tenjin Example', items => [qw/AAA BBB CCC/] }; my $filename = 'file.html'; my $output = $engine->render($filename, $context); print $output; DESCRIPTION Tenjin is a very fast and full-featured templating engine, implemented in several programming languages, among them Perl. The Perl version of Tenjin supports embedded Perl code, nestable layout template, inclusion of other templates inside a template, capturing parts of or the entire template output, file and memory caching, template arguments and preprocessing. The original version of Tenjin is developed by Makoto Kuwata. This CPAN version is developed by Ido Perlmuter and differs from the original in a few key aspects: * Code is entirely revised, packages are separated into modules, with a smaller number of packages than the original version. In particular, the Tenjin::Engine module no longer exists, and is now instead just the Tenjin module (i.e. this one). * Support for rendering templates from non-file sources (such as a database) is added. * Ability to set the encoding of your templates is added (Tenjin will decode template files according to this encoding; by default, Tenjin will decode * HTML is encoded and decoded using the HTML::Entities module, instead of internally. * The "pltenjin" script is not provided, at least for now. To make it clear, the CPAN version of Tenjin might find itself diverting a bit in the future from the original Tenjin's roadmap. Although my aim is to be as compatible as possible (and this version is always updated with features and changes from the original), I cannot guarantee it (but I'll do my best). Please note that version 0.05 (and above) of this module is NOT backwards compatible with previous versions. A NOTE ABOUT ENCODING When Tenjin opens template files, it will automatically decode their contents according to the selected encoding (UTF-8 by default), so make sure your template files are properly encoded. Tenjin also writes cache files of compiled template structure. These will be automatically encoded according to the selected encoding. When it comes to UTF-8, it might interest you to know how Tenjin behaves: 1. "UTF-8" is the default encoding used. If for some reason, either before running "Tenjin->new()" or during, you provide an alternate spelling (such as "utf8" or "UTF8"), Tenjin will convert it to UTF-8. 2. When reading files, Tenjin uses "<:encoding(UTF-8)", while when writing files, Tenjin uses ">:utf8", as recommended by this article . METHODS new( \%options ) This creates a new instant of Tenjin. "\%options" is a hash-ref containing Tenjin's configuration options: * path - Array-ref of filesystem paths where templates will be searched * prefix - A string that will be automatically prepended to template names when searching for them in the path. Empty by default. * postfix - The default extension to be automtically appended to template names when searching for them in the path. Don't forget to include the dot, such as '.html'. Empty by default. * cache - If set to 1 (the default), compiled templates will be cached on the filesystem (this means the template's code will be cached, not the completed rendered output). * preprocess - Enable template preprocessing (turned off by default). Only use if you're actually using any preprocessed Perl code in your templates. * layout - Name of a layout template that can be optionally used. If set, templates will be automatically inserted into the layout template, in the location where you use "[== $_content ==]". * strict - Another way to make Tenjin use strict on embedded Perl code (turned off by default). * encoding - Another way to set the encoding of your template files (set to "UTF-8" by default). render( $tmpl_name, [\%_context, $use_layout] ) Renders a template whose name is identified by $tmpl_name. Remember that a prefix and a postfix might be added if they where set when creating the Tenjin instance. $_context is a hash-ref containing the variables that will be available for usage inside the templates. So, for example, if your "\%_context" is "{ message => 'Hi there' }", then you can use $message inside your templates. $use_layout is a flag denoting whether or not to render this template into a layout template (when doing so, the template will be rendered, then the rendered output will be added to the context hash-ref as '_content', and finally the layout template will be rendered with the revised context and returned. If $use_layout is 1 (which is the default in case it is undefined), then Tenjin will use the layout template that was set when creating the Tenjin instance (via the 'layout' configuration option). If you want to use a different layout template (or if you haven't defined a layout template when creating the Tenjin instance), then you must add the layout template's name to the context as '_layout'. You can also just pass the layout template's name as $use_layout, but "$_context->{_layout}" has precedence. If $use_layout is 0, then a layout template will not be used, even if "$_context->{_layout}" is defined. Note that you can nest layout templates as much as you like, but the only way to do so is by setting the layout template for each template in the nesting chain with "$_context->{_layout}". Please note that by default file templates are cached on disk (with a '.cache') extension. Tenjin automatically deprecates these cache files every 10 seconds. If you find this value is too low, you can override the $Tenjin::TIMESTAMP_INTERVAL variable with your preferred value. register_template( $template_name, $template ) Receives the name of a template and its Tenjin::Template object and stores it in memory for usage by the engine. This is useful if you need to use templates that are not stored on the file system, for example from a database. Note, however, that you need to pass a template object who's already been converted and compiled into Perl code, so if you have a template with a certain name and certain text, these are the steps you will need to perform: # create a Tenjin instance my $tenjin = Tenjin->new(\%options); # create an empty template object my $template = Tenjin::Template->new(); # compile template content into Perl code $template->convert($tmpl_content); $template->compile(); # register the template with the Tenjin instance $tenjin->register_template($tmpl_name, $template); INTERNAL METHODS get_template( $template_name, $_context ) Receives the name of a template and the context object and tries to find that template in the engine's memory. If it's not there, it will try to find it in the file system (the cache file might be loaded, if present). Returns the template's Tenjin::Template object. to_filename( $template_name ) Receives a template name and returns the proper file name to be searched in the file system, which will only be different than $template_name if it begins with ':', in which case the prefix and postfix configuration options will be appended and prepended to the template name (minus the ':'), respectively. find_template_file( $filename ) Receives a template filename and searches for it in the path defined in the configuration options (or, if a path was not set, in the current working directory). Returns the absolute path to the file. read_template_file( $template, $filename, $_context ) Receives a template object and its absolute file path and reads that file. If preprocessing is on, preprocessing will take place using the provided context object. cachename( $filename ) Receives a template filename and returns its standard cache filename (which will simply be $filename with '.cache' appended to it. store_cachefile( $cachename, $template ) Receives the name of a template cache file and the corresponding template object, and creates the cache file on disk. load_cachefile( $cachename, $template ) Receives the name of a template cache file and the corresponding template object, reads the cache file and stores it in the template object (as 'script'). create_template( $filename, $_context ) Receives an absolute path to a template file and the context object, reads the file, processes it (which may involve loading the template's cache file or creating the template's cache file), compiles it and returns the template object. SEE ALSO The original Tenjin website is located at . In there check out for detailed usage guide, for examples, and for frequently asked questions. Note that the Perl version of Tenjin is referred to as plTenjin on the Tenjin website, and that, as opposed to this module, the website suggests using a .plhtml extension for the templates instead of .html (this is entirely your choice). Tenjin::Template, Catalyst::View::Tenjin, Dancer::Template::Tenjin. CHANGES Version 0.05 of this module broke backwards compatibility with previous versions. In particular, the Tenjin::Engine module does not exist any more and is instead integrated into this one. Templates are also rendered entirely different (as per changes in the original tenjin) which provides much faster rendering. Upon upgrading to versions 0.05 and above, you MUST perform the following changes for your applications (or, if you're using Catalyst, you must also upgrade Catalyst::View::Tenjin): * "use Tenjin" as your normally would, but to get an instance of Tenjin you must call "Tenjin->new()" instead of the old method of calling "Tenjin::Engine->new()". * Remove all your templates cache files (they are the '.cache' files in your template directories), they are not compatible with the new templates structure and WILL cause your application to fail if present. Version 0.06 (this version) restored the layout template feature which was accidentally missing in version 0.05, and the ability to call the utility methods of Tenjin::Util natively inside templates. You will want to remove your templates' .cache files when upgrading to 0.6 too. AUTHOR Ido Perlmuter Forked from plTenjin 0.0.2 by Makoto Kuwata (). ACKNOWLEDGEMENTS I would like to thank the following people for their contributions: * Makoto Kuwata The original developer of Tenjin. * John Beppu For introducing me to Tenjin and helping me understand the way it's designed. * Pedro Melo For helping me understand the logic behind some of the original Tenjin aspects and helping me fix bugs and create tests. BUGS Please report any bugs or feature requests on the L. SUPPORT You can find documentation for this module with the perldoc command. perldoc Tenjin You can also read the documentation online on metacpan . LICENSE AND COPYRIGHT Tenjin is licensed under the MIT license. Copyright (c) 2007-2016 the aforementioned authors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. See http://dev.perl.org/licenses/ for more information. Changes100644001750001750 622312716673333 13265 0ustar00idoido000000000000Tenjin-1.000001Revision history for Perl extension Tenjin 1.000001 2016-05-17 22:54:00+03:00 Asia/Jerusalem - Small typo fix in Tenjin::Template 1.000000 2016-05-14 20:18:01+03:00 Asia/Jerusalem - Avoid using $& since it leads to performance penalty for all regexp matches in code using this library (Mark Hindess) - Fixes for performance bug and off-by-one error in delete newline syntax (Mark Hindess) - Small documentation fixes - Move bug tracker to GitHub - Bump version to 1.0.0 0.070001 2011-03-29 21:38:04 Asia/Jerusalem - Added version numbers to all modules (not just Tenjin.pm) as the CPAN indexer failed. 0.070 2011-03-29 21:31:14 Asia/Jerusalem - Tenjin now properly decodes template input and encodes template output according to the user's selected encoding - Default encoding now spelled UTF-8 instead of utf8 - Added t/encoding.t to test template encoding 0.062 2010-08-06 20:49:28 Asia/Jerusalem - Tenjin now croaks instead of dies, and only inside the context class (the evaluate and to_func methods). - The _render internal method was removed as it's not needed anymore - Every template object now has the template's name, not just the file name. This is good for non-file templates and allows croaking with a message that gives more info about the location of the error. - Added a new test that simply attempts to catch a croak in an intentionally "faulty" template. - Removed the Encode dependency from Tenjin::Util as it wasn't even used. - Migrated distribution to use Dist::Zilla 0.061 2010-02-24 17:00:00 - Restored the ability to nest layout templates - Restored the layou templates precedence to the same used in plTenjin - Renamed the context variable back to $_context - Added some real CPAN tests to the distro - Moved from Module::Install to ExtUtils::MakeMaker for the Makefile 0.06 2010-02-24 12:30:00 - Restored the layout template feature which went MIA on 0.05 - Broadened the documentation for the layout template feature and macro functions - Made the utility functions available in templates natively again - Added documentation for all modules and methods - Fixed bug when preprocessing templates - Made a test script (not yet an actual CPAN test) - Cleaned distro files 0.052 2010-02-19 01:06:00 - Pointless update to remove git files from the release 0.051 2010-02-18 18:51:00 - Fixed 'use of uninitialized value' warnings caused by $lspace and $rspace - Removed references to pltenjin which caused the Makefile to fail 0.05 2010-02-18 12:35:00 - Revised code structure, Tenjin::Engine is now just Tenjin - Much faster template compilation - Not backwards compatible with previous versions! 0.04 2009-08-01 18:00:00 - Fixed some typos in the documentation - Added the ability to set the encoding of templates - USE_STRICT can now also be set by Tenjin::Engine->new() - Added a little more documentation 0.031 2009-07-27 23:30:00 - Fixed bad links in all the modules. - Added the command line application to the distribution. - Added the MIT license file to the distribution. 0.03 2009-07-26 20:20:00 - Initial release adapted from plTenjin's original 0.0.2 source. LICENSE100644001750001750 221012716673333 12767 0ustar00idoido000000000000Tenjin-1.000001This software is Copyright (c) 2016 by Ido Perlmuter. This is free software, licensed under: The MIT (X11) License The MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. INSTALL100644001750001750 161412716673333 13022 0ustar00idoido000000000000Tenjin-1.000001 This is the Perl distribution Tenjin. Installing Tenjin is straightforward. ## Installation with cpanm If you have cpanm, you only need one line: % cpanm Tenjin If you are installing into a system-wide directory, you may need to pass the "-S" flag to cpanm, which uses sudo to install the module: % cpanm -S Tenjin ## Installing with the CPAN shell Alternatively, if your CPAN shell is set up, you should just be able to do: % cpan Tenjin ## Manual installation As a last resort, you can manually install it. Download the tarball, untar it, then build it: % perl Makefile.PL % make && make test Then install it: % make install If you are installing into a system-wide directory, you may need to run: % sudo make install ## Documentation Tenjin documentation is available as POD. You can run perldoc from a shell to read the documentation: % perldoc Tenjin dist.ini100644001750001750 16512716673333 13415 0ustar00idoido000000000000Tenjin-1.000001name = Tenjin author = Ido Perlmuter license = MIT copyright_holder = Ido Perlmuter [@IDOPEREL] MANIFEST100644001750001750 210712716673333 13120 0ustar00idoido000000000000Tenjin-1.000001# This file was automatically generated by Dist::Zilla::Plugin::Manifest v5.031. Changes INSTALL LICENSE MANIFEST MANIFEST.SKIP META.json Makefile.PL README SIGNATURE dist.ini lib/Tenjin.pm lib/Tenjin/Context.pm lib/Tenjin/Preprocessor.pm lib/Tenjin/Template.pm lib/Tenjin/Util.pm t/00-load.t t/01-nested_layouts.t t/02-layout_precedence.t t/03-capture_placeholder.t t/04-utils.t t/05-fail.t t/06-encoding.t t/07-delete-newline.t t/data/capture_placeholder/capture.html t/data/capture_placeholder/placeholder.html t/data/delete-newline/delete-newline.txt t/data/delete-newline/delete-no-newline.txt t/data/encoding/chinese.html t/data/encoding/hebrew.html t/data/fail/fail.html t/data/fail/no_fail.html t/data/layout_precedence/content.html t/data/layout_precedence/context_layout.html t/data/layout_precedence/instance_layout.html t/data/layout_precedence/render_layout.html t/data/nested_layouts/inside_layout.html t/data/nested_layouts/outside_layout.html t/data/nested_layouts/top.html t/data/utils/encode_url.html t/data/utils/escape_xml.html t/data/utils/pP.html t/release-dist-manifest.t META.json100644001750001750 222312716673333 13407 0ustar00idoido000000000000Tenjin-1.000001{ "abstract" : "Fast templating engine with support for embedded Perl.", "author" : [ "Ido Perlmuter " ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 5.031, CPAN::Meta::Converter version 2.143240", "license" : [ "mit" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Tenjin", "prereqs" : { "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "Carp" : "0", "Fcntl" : "0", "HTML::Entities" : "0", "perl" : "5.008", "strict" : "0", "warnings" : "0" } }, "test" : { "requires" : { "Test::More" : "0", "Try::Tiny" : "0", "utf8" : "0" } } }, "release_status" : "stable", "resources" : { "repository" : { "type" : "git", "url" : "git://github.com//Tenjin.git", "web" : "https://github.com//Tenjin" } }, "version" : "1.000001" } SIGNATURE100644001750001750 706712716673333 13265 0ustar00idoido000000000000Tenjin-1.000001This file contains message digests of all files listed in MANIFEST, signed via the Module::Signature module, version 0.73. To verify the content in this distribution, first make sure you have Module::Signature installed, then type: % cpansign -v It will check each file's integrity, as well as the signature's validity. If "==> Signature verified OK! <==" is not displayed, the distribution may already have been compromised, and you should not run its Makefile.PL or Build.PL. -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 SHA1 1dff8959edb922cf7135380f73ee0189b41064dc Changes SHA1 35814390224523dbc9fe9716d68108e716efe4ba INSTALL SHA1 3bbac59114d652f292a6f8e6e6c14a601d1f9add LICENSE SHA1 d77668482142afb9ce7b3547e0a9be2e68155d9c MANIFEST SHA1 cb5399c0c2ab1d0a342bd8d13c958b383d33cd1b MANIFEST.SKIP SHA1 fb4dceb2f77d3c9b9b39d9383da9e076b9cf5ebf META.json SHA1 c290959144f96438203aeff95dc6611b152119cc Makefile.PL SHA1 f93b895bb2ed789db32e0b8e94077e767e545c10 README SHA1 fae71f7642e90cf94e33e4e32ae0ca8bf6174b4e dist.ini SHA1 b3711989ed8f0361a17d62eb442b5ee47087c221 lib/Tenjin.pm SHA1 c50965ab1ee729bd6076600c554fad0032050afb lib/Tenjin/Context.pm SHA1 55b5617434ed74da238f40dcbca2b7930d1d271a lib/Tenjin/Preprocessor.pm SHA1 3fac118f9dec97d7ba4446fefff8996d7325a6f8 lib/Tenjin/Template.pm SHA1 03abb9e15b6289e5f4a736fec5cac751677ef6cc lib/Tenjin/Util.pm SHA1 495a30673bfbbce86c789ed72c31b16f19897a06 t/00-load.t SHA1 8caa16aeb9f7fb1ad2cd946ecd1d4f4ceb35a310 t/01-nested_layouts.t SHA1 c63cdc538dd9b0d6341931d5ad318310c1d7e319 t/02-layout_precedence.t SHA1 e5f6c287a673c15f2a83f4c306d61759d141ee34 t/03-capture_placeholder.t SHA1 6705a60b891d5ab41ef23642c6d34b6bcccbca2e t/04-utils.t SHA1 c29c857ffc3fb9b58aac7c7a8ae554929e5a75b6 t/05-fail.t SHA1 e844ee1fd4b2adda1a798945c3580429f07bf820 t/06-encoding.t SHA1 fc3c48ffe5f05f2f70e5a83d1d1d9b8f4acee3e5 t/07-delete-newline.t SHA1 95e39906fd0d19d69b2ab77df798e92f4714df60 t/data/capture_placeholder/capture.html SHA1 73b8a2539512809a674467c1c28212668c682975 t/data/capture_placeholder/placeholder.html SHA1 0f872656f686eb117a065f8f01f5ac2602776549 t/data/delete-newline/delete-newline.txt SHA1 a2ea61bb62ad2db68e2e657c911a9eded9560c07 t/data/delete-newline/delete-no-newline.txt SHA1 be3174ec87b22b9908459ed9578db8e3e94a05d5 t/data/encoding/chinese.html SHA1 4dc53f13601fa18b648ef169a9a0cd5ebe1703de t/data/encoding/hebrew.html SHA1 b92b90fe0759237c737a068c50efc47e71ddf8f2 t/data/fail/fail.html SHA1 356a192b7913b04c54574d18c28d46e6395428ab t/data/fail/no_fail.html SHA1 664e8a0b85a27c0b9dfa86e6fd84ce27ff3a235b t/data/layout_precedence/content.html SHA1 d754f3840b84ac364f72cbecf3b2209e7443ed1c t/data/layout_precedence/context_layout.html SHA1 6679123c114cf2bd6e16c9c0e65afbbc3450dadb t/data/layout_precedence/instance_layout.html SHA1 acfe367f998dbce265b5c3343ce2620dba83d5ae t/data/layout_precedence/render_layout.html SHA1 c30a4b043ab481a0a27b9c00848ff6d1d4607cb0 t/data/nested_layouts/inside_layout.html SHA1 d6d3520668636fbf14112e7297247daee9fc7c78 t/data/nested_layouts/outside_layout.html SHA1 d8c67fe4bea6ba790066ab65e23c65e1f4ddb151 t/data/nested_layouts/top.html SHA1 6789b494c6797133bb0d38985426b0be0a9a1cec t/data/utils/encode_url.html SHA1 0d6662d870015860fd4fd70cae07d7a6c8dc46eb t/data/utils/escape_xml.html SHA1 569f290287f6a3360ccec467fae190775b905ca6 t/data/utils/pP.html SHA1 87768d177f8c2e2df9c591cde869e7c8a245555e t/release-dist-manifest.t -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iF4EAREIAAYFAlc7dtsACgkQa4OHQAFM10d1rAD9FW1LB9TSwNdX0ovGWNKlnF0x LwT7QOfy5ZG27n0akqIA/RNpMm6GmPCcPQi10b3rgp1b498NdiS4r71BAy87NQBu =zTF4 -----END PGP SIGNATURE----- t000755001750001750 012716673333 12072 5ustar00idoido000000000000Tenjin-1.00000105-fail.t100644001750001750 62212716673333 13534 0ustar00idoido000000000000Tenjin-1.000001/t#!perl -T use strict; use warnings; use Test::More tests => 3; use Tenjin; use Try::Tiny; my $t = Tenjin->new({ path => ['t/data/fail'] }); ok($t, 'Got a proper Tenjin instance'); my $output = try { $t->render('fail.html') } catch { 'failed'; }; is( $output, 'failed', 'failure caught' ); my $output2 = try { $t->render('no_fail.html') } catch { 'failed'; }; is( $output2, 1, 'all okay' ); 00-load.t100644001750001750 23112716673333 13527 0ustar00idoido000000000000Tenjin-1.000001/t#!perl -T use Test::More tests => 1; BEGIN { use_ok( 'Tenjin' ) || print "Bail out! "; } diag( "Testing Tenjin $Tenjin::VERSION, Perl $], $^X" ); Makefile.PL100644001750001750 245312716673333 13745 0ustar00idoido000000000000Tenjin-1.000001 # This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v5.031. use strict; use warnings; use 5.008; use ExtUtils::MakeMaker; my %WriteMakefileArgs = ( "ABSTRACT" => "Fast templating engine with support for embedded Perl.", "AUTHOR" => "Ido Perlmuter ", "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => 0 }, "DISTNAME" => "Tenjin", "EXE_FILES" => [], "LICENSE" => "mit", "MIN_PERL_VERSION" => "5.008", "NAME" => "Tenjin", "PREREQ_PM" => { "Carp" => 0, "Fcntl" => 0, "HTML::Entities" => 0, "strict" => 0, "warnings" => 0 }, "TEST_REQUIRES" => { "Test::More" => 0, "Try::Tiny" => 0, "utf8" => 0 }, "VERSION" => "1.000001", "test" => { "TESTS" => "t/*.t" } ); my %FallbackPrereqs = ( "Carp" => 0, "ExtUtils::MakeMaker" => 0, "Fcntl" => 0, "HTML::Entities" => 0, "Test::More" => 0, "Try::Tiny" => 0, "strict" => 0, "utf8" => 0, "warnings" => 0 ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { delete $WriteMakefileArgs{TEST_REQUIRES}; delete $WriteMakefileArgs{BUILD_REQUIRES}; $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); 04-utils.t100644001750001750 202712716673333 14001 0ustar00idoido000000000000Tenjin-1.000001/t#!perl -T use strict; use warnings; use Test::More; use Tenjin; my $t = Tenjin->new({ path => ['t/data/utils'] }); ok($t, 'Got a proper Tenjin instance'); # the encode_url method is( $t->render('encode_url.html', { url => "http://www.google.com/search?q=tenjin&ie=utf-8&oe=utf-8&aq=t" }), "http%3A//www.google.com/search%3Fq%3Dtenjin%26ie%3Dutf-8%26oe%3Dutf-8%26aq%3Dt", 'encode_url() works' ); # the escape_xml() and unescape_xml() methods is( $t->render('escape_xml.html', { escape => "test\"test", unescape => "test"test" }), "<a href="http://localhost:3000/?key=value&value=key">test"test</a>\ntest\"test", '(un)escape_xml() works' ); # the p() and P() methods is( $t->render('pP.html', { p => "whaddup?", P => "encode & me" }), "<`#whaddup?#`>\n<`\$encode & me\$`>", 'p() and P() work' ); done_testing(); MANIFEST.SKIP100644001750001750 34212716673333 13644 0ustar00idoido000000000000Tenjin-1.000001^\.gitignore$ ^blib/.*$ ^inc/.*$ ^Makefile$ ^Makefile\.old$ ^pm_to_blib$ ^Build$ ^Build\.bat$ ^_build\.*$ ^pm_to_blib.+$ ^.+\.tar\.gz$ ^\.lwpcookies$ ^cover_db$ ^pod2htm.*\.tmp$ ^Tenjin-.*$ ^\.build.*$ ^.+\.cache$ ^MYMETA\..*$lib000755001750001750 012716673333 12375 5ustar00idoido000000000000Tenjin-1.000001Tenjin.pm100644001750001750 4550312716673333 14351 0ustar00idoido000000000000Tenjin-1.000001/libpackage Tenjin; # ABSTRACT: Fast templating engine with support for embedded Perl. use strict; use warnings; use Carp; use Tenjin::Context; use Tenjin::Template; use Tenjin::Preprocessor; our $VERSION = "1.000001"; $VERSION = eval $VERSION; our $USE_STRICT = 0; our $ENCODING = 'UTF-8'; our $BYPASS_TAINT = 1; # unset if you like taint mode our $TEMPLATE_CLASS = 'Tenjin::Template'; our $CONTEXT_CLASS = 'Tenjin::Context'; our $PREPROCESSOR_CLASS = 'Tenjin::Preprocessor'; our $TIMESTAMP_INTERVAL = 10; =head1 NAME Tenjin - Fast templating engine with support for embedded Perl. =head1 SYNOPSIS use Tenjin; $Tenjin::USE_STRICT = 1; # use strict in the embedded Perl inside # your templates. Recommended, but not used # by default. $Tenjin::ENCODING = "UTF-8"; # set the encoding of your template files # to UTF-8. This is the default encoding used # so there's no need to do this if your # templates really are UTF-8. my $engine = Tenjin->new(\%options); my $context = { title => 'Tenjin Example', items => [qw/AAA BBB CCC/] }; my $filename = 'file.html'; my $output = $engine->render($filename, $context); print $output; =head1 DESCRIPTION Tenjin is a very fast and full-featured templating engine, implemented in several programming languages, among them Perl. The Perl version of Tenjin supports embedded Perl code, nestable layout template, inclusion of other templates inside a template, capturing parts of or the entire template output, file and memory caching, template arguments and preprocessing. The original version of Tenjin is developed by Makoto Kuwata. This CPAN version is developed by Ido Perlmuter and differs from the original in a few key aspects: =over =item * Code is entirely revised, packages are separated into modules, with a smaller number of packages than the original version. In particular, the Tenjin::Engine module no longer exists, and is now instead just the Tenjin module (i.e. this one). =item * Support for rendering templates from non-file sources (such as a database) is added. =item * Ability to set the encoding of your templates is added (Tenjin will decode template files according to this encoding; by default, Tenjin will decode =item * HTML is encoded and decoded using the L module, instead of internally. =item * The C script is not provided, at least for now. =back To make it clear, the CPAN version of Tenjin might find itself diverting a bit in the future from the original Tenjin's roadmap. Although my aim is to be as compatible as possible (and this version is always updated with features and changes from the original), I cannot guarantee it (but I'll do my best). Please note that version 0.05 (and above) of this module is NOT backwards compatible with previous versions. =head2 A NOTE ABOUT ENCODING When Tenjin opens template files, it will automatically decode their contents according to the selected encoding (UTF-8 by default), so make sure your template files are properly encoded. Tenjin also writes cache files of compiled template structure. These will be automatically encoded according to the selected encoding. When it comes to UTF-8, it might interest you to know how Tenjin behaves: =over =item 1. "UTF-8" is the default encoding used. If for some reason, either before running C<< Tenjin->new() >> or during, you provide an alternate spelling (such as "utf8" or "UTF8"), Tenjin will convert it to UTF-8. =item 2. When reading files, Tenjin uses "<:encoding(UTF-8)", while when writing files, Tenjin uses ">:utf8", as recommended by L. =back =head1 METHODS =head2 new( \%options ) This creates a new instant of Tenjin. C<\%options> is a hash-ref containing Tenjin's configuration options: =over =item * B - Array-ref of filesystem paths where templates will be searched =item * B - A string that will be automatically prepended to template names when searching for them in the path. Empty by default. =item * B - The default extension to be automtically appended to template names when searching for them in the path. Don't forget to include the dot, such as '.html'. Empty by default. =item * B - If set to 1 (the default), compiled templates will be cached on the filesystem (this means the template's code will be cached, not the completed rendered output). =item * B - Enable template preprocessing (turned off by default). Only use if you're actually using any preprocessed Perl code in your templates. =item * B - Name of a layout template that can be optionally used. If set, templates will be automatically inserted into the layout template, in the location where you use C<[== $_content ==]>. =item * B - Another way to make Tenjin use strict on embedded Perl code (turned off by default). =item * B - Another way to set the encoding of your template files (set to "UTF-8" by default). =back =cut sub new { my ($class, $options) = @_; my $self = {}; foreach (qw[prefix postfix layout path cache preprocess templateclass strict encoding]) { $self->{$_} = delete $options->{$_}; } $self->{cache} = 1 unless defined $self->{cache}; $self->{init_opts_for_template} = $options; $self->{templates} = {}; $self->{prefix} = '' unless $self->{prefix}; $self->{postfix} = '' unless $self->{postfix}; $Tenjin::ENCODING = $self->{encoding} if $self->{encoding}; # if encoding is utf8, make sure it's spelled UTF-8 and not otherwise $Tenjin::ENCODING = 'UTF-8' if $Tenjin::ENCODING =~ m/^utf-?8$/i; $Tenjin::USE_STRICT = $self->{strict} if defined $self->{strict}; return bless $self, $class; } =head2 render( $tmpl_name, [\%_context, $use_layout] ) Renders a template whose name is identified by C<$tmpl_name>. Remember that a prefix and a postfix might be added if they where set when creating the Tenjin instance. C<$_context> is a hash-ref containing the variables that will be available for usage inside the templates. So, for example, if your C<\%_context> is C<< { message => 'Hi there' } >>, then you can use C<$message> inside your templates. C<$use_layout> is a flag denoting whether or not to render this template into a layout template (when doing so, the template will be rendered, then the rendered output will be added to the context hash-ref as '_content', and finally the layout template will be rendered with the revised context and returned. If C<$use_layout> is 1 (which is the default in case it is undefined), then Tenjin will use the layout template that was set when creating the Tenjin instance (via the 'layout' configuration option). If you want to use a different layout template (or if you haven't defined a layout template when creating the Tenjin instance), then you must add the layout template's name to the context as '_layout'. You can also just pass the layout template's name as C<$use_layout>, but C<< $_context->{_layout} >> has precedence. If C<$use_layout> is 0, then a layout template will not be used, even if C<< $_context->{_layout} >> is defined. Note that you can nest layout templates as much as you like, but the only way to do so is by setting the layout template for each template in the nesting chain with C<< $_context->{_layout} >>. Please note that by default file templates are cached on disk (with a '.cache') extension. Tenjin automatically deprecates these cache files every 10 seconds. If you find this value is too low, you can override the C<$Tenjin::TIMESTAMP_INTERVAL> variable with your preferred value. =cut sub render { my ($self, $template_name, $_context, $use_layout) = @_; $_context ||= {}; $_context->{'_engine'} = $self; # use a layout template by default $use_layout = 1 unless defined $use_layout; # start rendering the template, and if use_layout is true # then render the layout template with the original output, and # keep doing so if the layout template in itself is nested # inside other layout templates until there are no layouts left my $output; while ($template_name) { # get the template my $template = $self->get_template($template_name, $_context); # pass $_context only for preprocessing # render the template $output = $template->render($_context); # should we nest into a layout template? # check if $use_layout is 0, and if so bolt # check if $_context->{_layout} is defined, and if so use it # if not, and $use_layout is the name of a template, use it # if $use_layout is just 1, then use $self->{layout} # if no layout has been found, loop will finish last if defined $use_layout && $use_layout eq '0'; $template_name = delete $_context->{_layout} || $use_layout; undef $use_layout; # undef so we don't nest infinitely $template_name = $self->{layout} if $template_name && $template_name eq '1'; $_context->{_content} = $output; } # return the output return $output; } =head2 register_template( $template_name, $template ) Receives the name of a template and its L object and stores it in memory for usage by the engine. This is useful if you need to use templates that are not stored on the file system, for example from a database. Note, however, that you need to pass a template object who's already been converted and compiled into Perl code, so if you have a template with a certain name and certain text, these are the steps you will need to perform: # create a Tenjin instance my $tenjin = Tenjin->new(\%options); # create an empty template object my $template = Tenjin::Template->new(); # compile template content into Perl code $template->convert($tmpl_content); $template->compile(); # register the template with the Tenjin instance $tenjin->register_template($tmpl_name, $template); =cut sub register_template { my ($self, $template_name, $template) = @_; $template->{timestamp} = time; $self->{templates}->{$template_name} = $template; } =head1 INTERNAL METHODS =head2 get_template( $template_name, $_context ) Receives the name of a template and the context object and tries to find that template in the engine's memory. If it's not there, it will try to find it in the file system (the cache file might be loaded, if present). Returns the template's L object. =cut sub get_template { my ($self, $template_name, $_context) = @_; ## get cached template my $template = $self->{templates}->{$template_name}; ## check whether template file is updated or not undef $template if ($template && $template->{filename} && $template->{timestamp} + $TIMESTAMP_INTERVAL <= time); ## load and register template unless ($template) { my $filename = $self->to_filename($template_name); my $filepath = $self->find_template_file($filename); $template = $self->create_template($filepath, $template_name, $_context); # $_context is passed only for preprocessor $self->register_template($template_name, $template); } return $template; } =head2 to_filename( $template_name ) Receives a template name and returns the proper file name to be searched in the file system, which will only be different than C<$template_name> if it begins with ':', in which case the prefix and postfix configuration options will be appended and prepended to the template name (minus the ':'), respectively. =cut sub to_filename { my ($self, $template_name) = @_; if (substr($template_name, 0, 1) eq ':') { return $self->{prefix} . substr($template_name, 1) . $self->{postfix}; } return $template_name; } =head2 find_template_file( $filename ) Receives a template filename and searches for it in the path defined in the configuration options (or, if a path was not set, in the current working directory). Returns the absolute path to the file. =cut sub find_template_file { my ($self, $filename) = @_; my $path = $self->{path}; if ($path) { my $sep = $^O eq 'MSWin32' ? '\\\\' : '/'; foreach my $dirname (@$path) { my $filepath = $dirname . $sep . $filename; return $filepath if -f $filepath; } } else { return $filename if -f $filename; } my $s = $path ? ("['" . join("','", @$path) . "']") : '[]'; croak "[Tenjin] $filename not found in path (path is $s)."; } =head2 read_template_file( $template, $filename, $_context ) Receives a template object and its absolute file path and reads that file. If preprocessing is on, preprocessing will take place using the provided context object. =cut sub read_template_file { my ($self, $template, $filename, $_context) = @_; if ($self->{preprocess}) { if (! defined($_context) || ! $_context->{_engine}) { $_context ||= {}; $_context->{'_engine'} = $self; } my $pp = $Tenjin::PREPROCESSOR_CLASS->new(); $pp->convert($template->_read_file($filename)); return $pp->render($_context); } return $template->_read_file($filename, 1); } =head2 cachename( $filename ) Receives a template filename and returns its standard cache filename (which will simply be C<$filename> with '.cache' appended to it. =cut sub cachename { my ($self, $filename) = @_; return $filename . '.cache'; } =head2 store_cachefile( $cachename, $template ) Receives the name of a template cache file and the corresponding template object, and creates the cache file on disk. =cut sub store_cachefile { my ($self, $cachename, $template) = @_; my $cache = $template->{script}; if (defined $template->{args}) { my $args = $template->{args}; $cache = "\#\@ARGS " . join(',', @$args) . "\n" . $cache; } $template->_write_file($cachename, $cache, 1); } =head2 load_cachefile( $cachename, $template ) Receives the name of a template cache file and the corresponding template object, reads the cache file and stores it in the template object (as 'script'). =cut sub load_cachefile { my ($self, $cachename, $template) = @_; my $cache = $template->_read_file($cachename, 1); if ($cache =~ s/\A\#\@ARGS (.*)\r?\n//) { my $argstr = $1; $argstr =~ s/\A\s+|\s+\Z//g; my @args = split(',', $argstr); $template->{args} = \@args; } $template->{script} = $cache; } =head2 create_template( $filename, $_context ) Receives an absolute path to a template file and the context object, reads the file, processes it (which may involve loading the template's cache file or creating the template's cache file), compiles it and returns the template object. =cut sub create_template { my ($self, $filename, $template_name, $_context) = @_; my $cachename = $self->cachename($filename); my $class = $self->{templateclass} || $Tenjin::TEMPLATE_CLASS; my $template = $class->new(undef, $template_name, $self->{init_opts_for_template}); if (! $self->{cache}) { $template->convert($self->read_template_file($template, $filename, $_context), $filename); } elsif (! -f $cachename || (stat $cachename)[9] < (stat $filename)[9]) { $template->convert($self->read_template_file($template, $filename, $_context), $filename); $self->store_cachefile($cachename, $template); } else { $template->{filename} = $filename; $self->load_cachefile($cachename, $template); } $template->compile(); return $template; } 1; =head1 SEE ALSO The original Tenjin website is located at L. In there check out L for detailed usage guide, L for examples, and L for frequently asked questions. Note that the Perl version of Tenjin is referred to as plTenjin on the Tenjin website, and that, as opposed to this module, the website suggests using a .plhtml extension for the templates instead of .html (this is entirely your choice). L, L, L. =head1 CHANGES Version 0.05 of this module broke backwards compatibility with previous versions. In particular, the Tenjin::Engine module does not exist any more and is instead integrated into this one. Templates are also rendered entirely different (as per changes in the original tenjin) which provides much faster rendering. Upon upgrading to versions 0.05 and above, you MUST perform the following changes for your applications (or, if you're using Catalyst, you must also upgrade L): =over =item * C as your normally would, but to get an instance of Tenjin you must call C<< Tenjin->new() >> instead of the old method of calling C<< Tenjin::Engine->new() >>. =item * Remove all your templates cache files (they are the '.cache' files in your template directories), they are not compatible with the new templates structure and WILL cause your application to fail if present. =back Version 0.06 (this version) restored the layout template feature which was accidentally missing in version 0.05, and the ability to call the utility methods of L natively inside templates. You will want to remove your templates' .cache files when upgrading to 0.6 too. =head1 AUTHOR Ido Perlmuter Eido at ido50.netE Forked from plTenjin 0.0.2 by Makoto Kuwata (L). =head1 ACKNOWLEDGEMENTS I would like to thank the following people for their contributions: =over =item * Makoto Kuwata The original developer of Tenjin. =item * John Beppu Ebeppu at cpan.orgE For introducing me to Tenjin and helping me understand the way it's designed. =item * Pedro Melo Emelo at cpan.orgE For helping me understand the logic behind some of the original Tenjin aspects and helping me fix bugs and create tests. =back =head1 BUGS Please report any bugs or feature requests on the L. =head1 SUPPORT You can find documentation for this module with the perldoc command. perldoc Tenjin You can also read the documentation online on L. =head1 LICENSE AND COPYRIGHT Tenjin is licensed under the MIT license. Copyright (c) 2007-2016 the aforementioned authors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. See http://dev.perl.org/licenses/ for more information. =cut 06-encoding.t100644001750001750 61612716673333 14413 0ustar00idoido000000000000Tenjin-1.000001/t#!perl -T use strict; use warnings; use Test::More tests => 3; use Tenjin; use utf8; my $t = Tenjin->new({ path => ['t/data/encoding'] }); ok($t, 'Got a proper Tenjin instance'); is($t->render('hebrew.html'), "

ג'רי סיינפלד

\n", 'UTF-8 (Hebrew) properly decoded'); is($t->render('chinese.html'), "Chinese\n", 'UTF-8 (Chinese) properly decoded'); Tenjin000755001750001750 012716673333 13624 5ustar00idoido000000000000Tenjin-1.000001/libUtil.pm100644001750001750 1311412716673333 15257 0ustar00idoido000000000000Tenjin-1.000001/lib/Tenjinpackage Tenjin::Util; use strict; use warnings; use HTML::Entities; our $VERSION = "1.000001"; $VERSION = eval $VERSION; =head1 NAME Tenjin::Util - Utility methods for Tenjin. =head1 SYNOPSIS # in your templates: # encode a URL [== encode_url('http://www.google.com/search?q=tenjin&ie=utf-8&oe=utf-8&aq=t') =] # returns http%3A//www.google.com/search%3Fq%3Dtenjin%26ie%3Dutf-8%26oe%3Dutf-8%26aq%3Dt # escape a string of lines of HTML code You & Me\n

Me & You

'; ?> [== text2html($string) =] # returns <h1>You & Me</h1>
\n<h2>Me & You</h2> =head1 DESCRIPTION This module provides a few utility functions which can be used in your templates for your convenience. These include functions to (un)escape and (en/de)code URLs. =head1 METHODS =head2 expand_tabs( $str, [$tabwidth] ) Receives a string that might contain tabs in it, and replaces those tabs with spaces, each tab with the number of spaces defined by C<$tabwidth>, or, if C<$tabwidth> was not passed, with 8 spaces. =cut sub expand_tabs { my ($str, $tabwidth) = @_; $tabwidth ||= 8; my $s = ''; my $pos = 0; while ($str =~ /.*?\t/sg) { # /(.*?)\t/ may be slow my $end = $+[0]; my $text = substr($str, $pos, $end - 1 - $pos); my $n = rindex($text, "\n"); my $col = $n >= 0 ? length($text) - $n - 1 : length($text); $s .= $text; $s .= ' ' x ($tabwidth - $col % $tabwidth); $pos = $end; } my $rest = substr($str, $pos); return $s; } =head2 escape_xml( $str ) Receives a string of XML (or (x)HTML) code and converts the characters <>&\' to HTML entities. This is the method that is invoked when you use [= $expression =] in your templates. =cut sub escape_xml { encode_entities($_[0], '<>&"\''); } =head2 unescape_xml( $str ) Receives a string of escaped XML (or (x)HTML) code (for example, a string that was escaped with the L function, and 'unescapes' all HTML entities back to their actual characters. =cut sub unescape_xml { decode_entities($_[0]); } =head2 encode_url( $url ) Receives a URL and encodes it by escaping 'non-standard' characters. =cut sub encode_url { my $url = shift; $url =~ s/([^-A-Za-z0-9_.\/])/sprintf("%%%02X", ord($1))/sge; $url =~ tr/ /+/; return $url; } =head2 decode_url( $url ) Does the opposite of L. =cut sub decode_url { my $url = shift; $url =~ s/\%([a-fA-F0-9][a-fA-F0-9])/pack('C', hex($1))/sge; return $url; } =head2 checked( $val ) Receives a value of some sort, and if it is a true value, returns the string ' checked="checked"' which can be appended to HTML checkboxes. =cut sub checked { $_[0] ? ' checked="checked"' : ''; } =head2 selected( $val ) Receives a value of some sort, and if it is a true value, returns the string ' selected="selected"' which can be used in an option in an HTML select box. =cut sub selected { $_[0] ? ' selected="selected"' : ''; } =head2 disabled( $val ) Receives a value of some sort, and if it is a true value, returns the string ' disabled="disabled"' which can be used in an HTML input. =cut sub disabled { $_[0] ? ' disabled="disabled"' : ''; } =head2 nl2br( $text ) Receives a string of text containing lines delimited by newline characters (\n, or possibly \r\n) and appends an HTML line break (
) to every line (the newline character is left untouched). =cut sub nl2br { my $text = shift; $text =~ s/(\r?\n)/
$1/g; return $text; } =head2 text2html( $text ) Receives a string of text containing lines delimited by newline characters, and possibly some XML (or (x)HTML) code, escapes that code with L and then appends an HTML line break to every line with L. =cut sub text2html { nl2br(escape_xml($_[0])); } =head2 tagattr( $name, $expr, [$value] ) =cut sub tagattr { my ($name, $expr, $value) = @_; return '' unless $expr; $value = $expr unless defined $value; return " $name=\"$value\""; } =head2 tagattrs( %attrs ) =cut sub tagattrs { my (%attrs) = @_; my $s = ''; while (my ($k, $v) = each %attrs) { $s .= " $k=\"".escape_xml($v)."\"" if defined $v; } return $s; } =head2 new_cycle( @items ) Creates a subroutine reference that can be used for cycling through the items of the C<@items> array. So, for example, you can: my $cycle = new_cycle(qw/red green blue/); print $cycle->(); # prints 'red' print $cycle->(); # prints 'green' print $cycle->(); # prints 'blue' print $cycle->(); # prints 'red' again =cut sub new_cycle { my $i = 0; sub { $_[$i++ % scalar @_] }; # returns } =head1 INTERNAL(?) METHODS =head2 _p( $expression ) Wraps a Perl expression in a customized wrapper which will be processed by the Tenjin preprocessor and replaced with the standard [== $expression =]. =cut sub _p { "<`\#$_[0]\#`>"; } =head2 _P( $expression ) Wrap a Perl expression in a customized wrapper which will be processed by the Tenjin preprocessor and replaced with the standard [= $expression =], which means the expression will be escaped. =cut sub _P { "<`\$$_[0]\$`>"; } =head2 _decode_params( $s ) =cut sub _decode_params { my $s = shift; return '' unless $s; $s =~ s/%3C%60%23(.*?)%23%60%3E/'[=='.decode_url($1).'=]'/ge; $s =~ s/%3C%60%24(.*?)%24%60%3E/'[='.decode_url($1).'=]'/ge; $s =~ s/<`\#(.*?)\#`>/'[=='.unescape_xml($1).'=]'/ge; $s =~ s/<`\$(.*?)\$`>/'[='.unescape_xml($1).'=]'/ge; $s =~ s/<`\#(.*?)\#`>/[==$1=]/g; $s =~ s/<`\$(.*?)\$`>/[=$1=]/g; return $s; } 1; =head1 SEE ALSO L, L, L. =head1 AUTHOR, LICENSE AND COPYRIGHT See L. =cut utils000755001750001750 012716673333 14143 5ustar00idoido000000000000Tenjin-1.000001/t/datapP.html100644001750001750 3312716673333 15504 0ustar00idoido000000000000Tenjin-1.000001/t/data/utils[== _p($p) =] [== _P($P) =]Context.pm100644001750001750 1175512716673333 15777 0ustar00idoido000000000000Tenjin-1.000001/lib/Tenjinpackage Tenjin::Context; use strict; use warnings; use Tenjin::Util; use Carp; our $VERSION = "1.000001"; $VERSION = eval $VERSION; =head1 NAME Tenjin::Context - In charge of managing variables passed to Tenjin templates. =head1 SYNOPSIS # this module is used internally, but if you insist, it is # in charge of the context object: # in your templates (unnecessary, for illustration purposes): [== $_context->{title} =] # instead use: [== $title =] =head1 DESCRIPTION This module is in charge of managing Perl variables that are passed to templates upon rendering for direct usage. The context object is simply a hash-ref of key-value pairs, which are made available for templates as "standalone variables" named for each key in the hash-ref. This module is also in charge of the actual rendering of the templates, or more correctly, for evaluating the Perl code created from the templates, first integrating the context variables to them, and returning the rendered output. Finally, this module makes the Tenjin utility methods of L available natively inside templates. See L for more info. =head1 INTERNAL METHODS =head2 new( [\%vars] ) Constructs a new context object, which is basically a hash-ref of key-value pairs which are passed to templates as variables. If a C<$vars> hash-ref is passed to the constructor, it will be augmented into the created object. To illustrate the context object, suppose it looks like so: { scalar => 'I am a scalar', arrayref => [qw/I am an array/], hashref => { i => 'am', a => 'hash-ref' }, } Then the variables C<$scalar>, C<$arrayref> and C<$hashref> will be available for direct usage inside your templates, and you can dereference the variables normally (i.e. C<@$arrayref> and C<%$hashref>). =cut sub new { my ($class, $self) = @_; $self ||= {}; return bless $self, $class; } =head2 evaluate( $script, $template_name ) This method receives a compiled template and actually performes the evaluation the renders it, then returning the rendered output. If Tenjin is configured to C, the script will be Ced under C. =cut sub evaluate { my ($self, $script, $name) = @_; my $_context = $self; $script = ($script =~ /\A(.*)\Z/s) && $1 if $Tenjin::BYPASS_TAINT; my $s = $name ? "# line 1 \"$name\"\n" : ''; # line directive $s .= $script; my $ret; if ($Tenjin::USE_STRICT) { $ret = eval($s); } else { no strict; $ret = eval($s); use strict; } croak "[Tenjin] Failed rendering $name: $@" if $@; return $ret; } =head2 to_func( $script, [$filename] ) This method receives the script created when reading a template and wraps it in a subroutine, Cs it and returns the rendered output. This method is called when compiling the template. =cut sub to_func { my ($self, $script, $name) = @_; $script = ($script =~ /\A(.*)\Z/s) && $1 if $Tenjin::BYPASS_TAINT; my $s = $name ? "# line 1 \"$name\"\n" : ''; # line directive $s .= "sub { my (\$_context) = \@_; $script }"; my $ret; if ($Tenjin::USE_STRICT) { $ret = eval($s); } else { no strict; $ret = eval($s); use strict; } croak "[Tenjin] Failed compiling $name: $@" if $@; return $ret; } =head2 _build_decl() This method is in charge of making all the key-value pairs of the context object available to templates directly by the key names. This is simply done by traversing the key-value pairs of the context object and adding an assignment line between a scalar variable named as the key and its appropriate value. =cut sub _build_decl { my $self = shift; my $s = ''; foreach my $k (keys %$self) { next if $k eq '_context'; $s .= "my \$$k = \$_context->{'$k'}; "; } return $s; } =head1 UTILITY METHODS These methods are defined in L and used here so they are made available natively inside templates. See L for more information. =head2 _p( $expr ) =head2 _P( $expr ) =head2 escape( $expr ) =head2 escape_xml( $expr ) =head2 unescape_xml( $expr ) =head2 encode_url( $url ) =head2 decode_url( $url ) =head2 checked( $expr ) =head2 selected( $expr ) =head2 disabled( $expr ) =head2 nl2br( $text ) =head2 text2html( $text ) =head2 tagattr( $name, $expr, [$value] ) =head2 tagattrs( %attrs ) =head2 new_cycle( @items ) =cut # this makes the Tenjin utility methods available to templates 'natively' *_p = *Tenjin::Util::_p; *_P = *Tenjin::Util::_P; *escape = *Tenjin::Util::escape_xml; *escape_xml = *Tenjin::Util::escape_xml; *unescape_xml = *Tenjin::Util::unescape_xml; *encode_url = *Tenjin::Util::encode_url; *decode_url = *Tenjin::Util::decode_url; *checked = *Tenjin::Util::checked; *selected = *Tenjin::Util::selected; *disabled = *Tenjin::Util::disabled; *nl2br = *Tenjin::Util::nl2br; *text2html = *Tenjin::Util::text2html; *tagattr = *Tenjin::Util::tagattr; *tagattrs = *Tenjin::Util::tagattrs; *new_cycle = *Tenjin::Util::new_cycle; 1; =head1 SEE ALSO L, L, L. =head1 AUTHOR, LICENSE AND COPYRIGHT See L. =cut 01-nested_layouts.t100644001750001750 76612716673333 15670 0ustar00idoido000000000000Tenjin-1.000001/t#!perl -T use strict; use warnings; use Test::More; use Tenjin; my $t = Tenjin->new({ path => ['t/data/nested_layouts'] }); ok($t, 'Got a proper Tenjin instance'); # single-level layout is( $t->render('inside_layout.html', { _content => 'fake' }), 'fake', 'Single level of layouts works' ); # multiple-level layouts is( $t->render('top.html'), 'content', 'Multiple levels of layouts work' ); done_testing(); 07-delete-newline.t100644001750001750 103012716673333 15536 0ustar00idoido000000000000Tenjin-1.000001/t#!perl -T use strict; use warnings; use Test::More; use Tenjin; my $t = Tenjin->new({ path => ['t/data/delete-newline'], cache => 0 }); ok($t, 'Got a proper Tenjin instance'); # should delete newline is( $t->render('delete-newline.txt', { var => 'test' }), <<'EOF', line testanother line EOF 'Delete newline works' ); # should do nothing and not corrupt text is( $t->render('delete-no-newline.txt', { var => 'test' }), <<'EOF', line "test" another line EOF 'Delete newline with no newline to delete works' ); done_testing(); fail000755001750001750 012716673333 13716 5ustar00idoido000000000000Tenjin-1.000001/t/datafail.html100644001750001750 4412716673333 15615 0ustar00idoido000000000000Tenjin-1.000001/t/data/fail Template.pm100644001750001750 3375512716673333 16132 0ustar00idoido000000000000Tenjin-1.000001/lib/Tenjinpackage Tenjin::Template; use strict; use warnings; use Fcntl qw/:flock/; use Carp; our $VERSION = "1.000001"; $VERSION = eval $VERSION; =head1 NAME Tenjin::Template - A Tenjin template object, either built from a file or from memory. =head1 SYNOPSIS # mostly used internally, but you can manipulate # templates like so my $template = Tenjin::Template->new('/path/to/templates/template.html'); my $context = { scalar => 'scalar', arrayref => ['one', 2, "3"] }; $template->render($context); =head1 DESCRIPTION This module is in charge of the task of compiling Tenjin templates. Templates in Tenjin are compiled into standard Perl code (combined with any Perl code used inside the templates themselves). Rendering a template means Cuating that Perl code and returning its output. The Tenjin engine reads a template file or a template string, and creates a Template object from it. Then the object compiles itself by traversing the template, parsing Tenjin macros like 'include' and 'start_capture', replaces Tenjin expressions (i.e. C<[== $expr =]> or C<[= $expr =]>) with the appropriate Perl code, etc. This module ties a template object with a context object, but all context manipulation (and the actual Cuation of the Perl code) is done by L. If you're planning on using this module by itself (i.e. without the L engine), keep in mind that template caching and layout templates are not handled by this module. =cut our $MACRO_HANDLER_TABLE = { 'include' => sub { my $arg = shift; " \$_buf .= \$_context->{'_engine'}->render($arg, \$_context, 0);"; }, 'start_capture' => sub { my $arg = shift; " my \$_buf_bkup=\$_buf; \$_buf=''; my \$_capture_varname=$arg;"; }, 'stop_capture' => sub { my $arg = shift; " \$_context->{\$_capture_varname}=\$_buf; \$_buf=\$_buf_bkup;"; }, 'start_placeholder' => sub { my $arg = shift; " if (\$_context->{$arg}) { \$_buf .= \$_context->{$arg}; } else {"; }, 'stop_placeholder' => sub { my $arg = shift; " }"; }, 'echo' => sub { my $arg = shift; " \$_buf .= $arg;"; }, }; =head1 METHODS =head2 new( [$filename, \%opts] ) Creates a new Tenjin::Template object, possibly from a file on the file system (in which case C<$filename> must be provided and be an absolute path to a template file). Optionally, a hash-ref of options can be passed to set some customizations. Available options are 'escapefunc', which will be in charge of escaping expressions (from C<[= $expr =]>) instead of the internal method (which uses L); and 'rawclass', which can be used to prevent variables and objects of a certain class from being escaped, in which case the variable must be a hash-ref that has a key named 'str', which will be used instead. So, for example, if you have a variable named C<$var> which is a hash-ref, and 'rawclass' is set as 'HASH', then writing C<[= $var =]> on your templates will replace C<$var> with C<< $var->{str} >>. =cut sub new { my ($class, $filename, $template_name, $opts) = @_; my $escapefunc = defined($opts) && exists($opts->{escapefunc}) ? $opts->{escapefunc} : undef; my $rawclass = defined($opts) && exists($opts->{rawclass}) ? $opts->{rawclass} : undef; my $self = bless { 'filename' => $filename, 'name' => $template_name, 'script' => undef, 'escapefunc' => $escapefunc, 'rawclass' => $rawclass, 'timestamp' => undef, 'args' => undef, }, $class; $self->convert_file($filename) if $filename; return $self; } =head2 render( [$_context] ) Renders the template, possibly with a context hash-ref, and returns the rendered output. If errors have occurred when rendering the template (which might happen since templates have and are Perl code), then this method will croak. =cut sub render { my ($self, $_context) = @_; $_context ||= {}; if ($self->{func}) { return $self->{func}->($_context); } else { $_context = $Tenjin::CONTEXT_CLASS->new($_context) if ref $_context eq 'HASH'; my $script = $self->{script}; $script = $_context->_build_decl() . $script unless $self->{args}; # rendering is actually done inside the context object # with the evaluate method. We pass either the name of # the template or the filename of the template for debug # purposes return $_context->evaluate($script, $self->{filename} || $self->{name}); } } =head1 INTERNAL METHODS =head2 convert_file( $filename ) Receives an absolute path to a template file, converts that file to Perl code by calling L and returns that code. =cut sub convert_file { my ($self, $filename) = @_; return $self->convert($self->_read_file($filename, 1), $filename); } =head2 convert( $input, [$filename] ) Receives a text of a template (i.e. the template itself) and possibly an absolute path to the template file (if the template comes from a file), and converts the template into Perl code, which is later Cuated for rendering. Conversion is done by parsing the statements in the template (see L). =cut sub convert { my ($self, $input, $filename) = @_; $self->{filename} = $filename; my @buf = ('my $_buf = ""; my $_V; ', ); $self->parse_stmt(\@buf, $input); return $self->{script} = $buf[0] . " \$_buf;\n"; } =head2 compile_stmt_pattern( $pl ) Receives a string which denotes the Perl code delimiter which is used inside templates. Tenjin uses 'C<< >>' and 'C<< >>' (the latter for preprocessing), so C<$pl> will be 'pl'. This method returns a tranlsation regular expression which will be used for reading embedded Perl code. =cut sub compile_stmt_pattern { my $pl = shift; my $pat = '((^[ \t]*)?<\?'.$pl.'( |\t|\r?\n)(.*?) ?\?>([ \t]*\r?\n)?)'; return qr/$pat/sm; } =head2 stmt_pattern Returns the default pattern (which uses 'pl') with the L. =cut sub stmt_pattern { return compile_stmt_pattern('pl'); } =head2 expr_pattern() Defines how expressions are written in Tenjin templates (C<[== $expr =]> and C<[= $expr =]>). =cut sub expr_pattern { return qr/\[=(=?)(.*?)(=?)=\]/s; } =head2 parse_stmt( $bufref, $input ) Receives a buffer which is used for saving a template's expressions and the template's text, parses all expressions in the templates and pushes them to the buffer. =cut sub parse_stmt { my ($self, $bufref, $input) = @_; my $pos = 0; my $pat = $self->stmt_pattern(); while ($input =~ /$pat/g) { my ($pi, $lspace, $mspace, $stmt, $rspace) = ($1, $2, $3, $4, $5); my $start = $-[0]; my $text = substr($input, $pos, $start - $pos); $pos = $start + length($pi); $self->parse_expr($bufref, $text) if $text; $mspace = '' if $mspace eq ' '; $stmt = $self->hook_stmt($stmt); $stmt .= $rspace if $rspace; $stmt = $mspace . $stmt if $mspace; $stmt = $lspace . $stmt if $lspace; $self->add_stmt($bufref, $stmt); } my $rest = $pos == 0 ? $input : substr($input, $pos); $self->parse_expr($bufref, $rest) if $rest; } =head2 hook_stmt( $stmt ) =cut sub hook_stmt { my ($self, $stmt) = @_; ## macro expantion if ($stmt =~ /\A(\s*)(\w+)\((.*?)\);?(\s*)\Z/) { my ($lspace, $funcname, $arg, $rspace) = ($1, $2, $3, $4); my $s = $self->expand_macro($funcname, $arg); return $lspace . $s . $rspace if defined($s); } ## template arguments unless ($self->{args}) { if ($stmt =~ m/\A(\s*)\#\@ARGS\s+(.*)(\s*)\Z/) { my ($lspace, $argstr, $rspace) = ($1, $2, $3); my @args = (); my @declares = (); foreach my $arg (split(/,/, $argstr)) { $arg =~ s/(^\s+|\s+$)//g; next unless $arg; $arg =~ m/\A([\$\@\%])?([a-zA-Z_]\w*)\Z/ or croak "[Tenjin] $arg: invalid template argument."; croak "[Tenjin] $arg: only '\$var' is available for template argument." unless (!$1 || $1 eq '$'); my $name = $2; push(@args, $name); push(@declares, "my \$$name = \$_context->{$name}; "); } $self->{args} = \@args; return $lspace . join('', @declares) . $rspace; } } return $stmt; } =head2 expand_macro( $funcname, $arg ) This method is in charge of invoking macro functions which might be used inside templates. The following macros are available: =over =item * C Includes another template, whose name is C<$filename>, inside the current template. The included template will be placed inside the template as if they were one unit, so the context variable applies to both. =item * C and C Tells Tenjin to capture the output of the rendered template from the point where C was called to the point where C was called. You must provide a name for the captured portion, which will be made available in the context as C<< $_context->{$name} >> for immediate usage. Note that the captured portion will not be printed unless you do so explicilty with C<< $_context->{$name} >>. =item * C and C This is a special method which can be used for making your templates a bit cleaner. Suppose your context might have a variable whose name is defined in C<$var>. If that variable exists in the context, you simply want to print it, but if it's not, you want to print and/or perform other things. In that case you can call C with the name of the context variable you want printed, and if it's not, anything you do between C and C will be printed instead. =item * echo( $exr ) Just prints the provided expression. You might want to use it if you're a little too comfortable with PHP. =back =cut sub expand_macro { my ($self, $funcname, $arg) = @_; my $handler = $MACRO_HANDLER_TABLE->{$funcname}; return $handler ? $handler->($arg) : undef; } =head2 get_expr_and_escapeflag( $not_escape, $expr, $delete_newline ) =cut ## ex. get_expr_and_escapeflag('=', '$item->{name}', '') => 1, '$item->{name}', 0 sub get_expr_and_escapeflag { my ($self, $not_escape, $expr, $delete_newline) = @_; return $expr, $not_escape eq '', $delete_newline eq '='; } =head2 parse_expr( $bufref, $input ) =cut sub parse_expr { my ($self, $bufref, $input) = @_; my $pos = 0; $self->start_text_part($bufref); my $pat = $self->expr_pattern(); while ($input =~ /$pat/g) { my $start = $-[0]; my $text = substr($input, $pos, $start - $pos); my ($expr, $flag_escape, $delete_newline) = $self->get_expr_and_escapeflag($1, $2, $3); $pos = $+[0]; $self->add_text($bufref, $text) if $text; $self->add_expr($bufref, $expr, $flag_escape) if $expr; if ($delete_newline) { my $end = $+[0]; if (substr($input, $end, 1) eq "\n") { $bufref->[0] .= "\n"; $pos++; } } } my $rest = $pos == 0 ? $input : substr($input, $pos); $self->add_text($bufref, $rest); $self->stop_text_part($bufref); } =head2 start_text_part( $bufref ) =cut sub start_text_part { my ($self, $bufref) = @_; $bufref->[0] .= ' $_buf .= '; } =head2 stop_text_part( $bufref ) =cut sub stop_text_part { my ($self, $bufref) = @_; $bufref->[0] .= '; '; } =head2 add_text( $bufref, $text ) =cut sub add_text { my ($self, $bufref, $text) = @_; return unless $text; $text =~ s/([`\\])/\\$1/g; my $is_start = $bufref->[0] =~ / \$_buf \.= \Z/; $bufref->[0] .= $is_start ? "q`$text`" : " . q`$text`"; } =head2 add_stmt( $bufref, $stmt ) =cut sub add_stmt { my ($self, $bufref, $stmt) = @_; $bufref->[0] .= $stmt; } =head2 add_expr( $bufref, $expr, $flag_escape ) =cut sub add_expr { my ($self, $bufref, $expr, $flag_escape) = @_; my $dot = $bufref->[0] =~ / \$_buf \.= \Z/ ? '' : ' . '; $bufref->[0] .= $dot . ($flag_escape ? $self->escaped_expr($expr) : "($expr)"); } =head2 defun( $funcname, @args ) =cut sub defun { ## (experimental) my ($self, $funcname, @args) = @_; unless ($funcname) { my $funcname = $self->{filename}; if ($funcname) { $funcname =~ s/\.\w+$//; $funcname =~ s/[^\w]/_/g; } $funcname = 'render_' . $funcname; } my $str = "sub $funcname { my (\$_context) = \@_; "; foreach (@args) { $str .= "my \$$_ = \$_context->{'$_'}; "; } $str .= $self->{script}; $str .= "}\n"; return $str; } =head2 compile() =cut ## compile $self->{script} into closure. sub compile { my $self = shift; if ($self->{args}) { $self->{func} = $Tenjin::CONTEXT_CLASS->to_func($self->{script}, $self->{name}); return $self->{func}; } return; } =head2 escaped_expr( $expr ) Receives a Perl expression (from C<[= $expr =]>) and escapes it. This will happen in one of three ways: with the escape function defined in C<< $opts->{escapefunc} >> (if defined), with a scalar string (if C<< $opts->{rawclass} >> is defined), or with C from L, which uses L. =cut sub escaped_expr { my ($self, $expr) = @_; return "$self->{escapefunc}($expr)" if $self->{escapefunc}; return "(ref(\$_V = ($expr)) eq '$self->{rawclass}' ? \$_V->{str} : escape_xml($expr)" if $self->{rawclass}; return "escape_xml($expr)"; } =head2 _read_file( $filename, [$lock_required] ) Receives an absolute path to a template file, reads its content and returns it. If C<$lock_required> is passed (and has a true value), the file will be locked for reading. =cut sub _read_file { my ($self, $filename, $lock_required) = @_; open(IN, "<:encoding($Tenjin::ENCODING)", $filename) or croak "[Tenjin] Can't open $filename for reading: $!"; flock(IN, LOCK_SH) if $lock_required; read(IN, my $content, -s $filename); close(IN); return $content; } =head2 _write_file( $filename, $content, [$lock_required] ) Receives an absolute path to a template file and the templates contents, and creates the file (or truncates it, if existing) with that contents. If C<$lock_required> is passed (and has a true value), the file will be locked exclusively when writing. =cut sub _write_file { my ($self, $filename, $content, $lock_required) = @_; my $enc = $Tenjin::ENCODING eq 'UTF-8' ? '>:utf8' : ">:encoding($Tenjin::ENCODING)"; open(OUT, $enc, $filename) or croak "[Tenjin] Can't open $filename for writing: $!"; flock(OUT, LOCK_EX) if $lock_required; print OUT $content; close(OUT); } 1; =head1 SEE ALSO L. =head1 AUTHOR, LICENSE AND COPYRIGHT See L. =cut 02-layout_precedence.t100644001750001750 213612716673333 16332 0ustar00idoido000000000000Tenjin-1.000001/t#!perl -T use strict; use warnings; use Test::More; use Tenjin; my $t = Tenjin->new({ path => ['t/data/layout_precedence'], layout => 'instance_layout.html' }); ok($t, 'Got a proper Tenjin instance'); my $context = { msg => 'hello world', arrayref => [qw/my name is Tenjin/] }; # no layout should be used is( $t->render('content.html', $context, 0), '

hello world, my name is Tenjin

', 'Rendering without a layout works' ); # layout should be used from $_context->{_layout} $context->{_layout} = 'context_layout.html'; is( $t->render('content.html', $context, 'render_layout.html'), '

hello world, my name is Tenjin

', 'Rendering into context-defined layout works' ); # layout should be used from render is( $t->render('content.html', $context, 'render_layout.html'), '

hello world, my name is Tenjin

', 'Rendering into render-defined layout works' ); # layout should be used from Tenjin instance is( $t->render('content.html', $context), '

hello world, my name is Tenjin

', 'Rendering into instance-defined layout works' ); done_testing(); no_fail.html100644001750001750 112716673333 16262 0ustar00idoido000000000000Tenjin-1.000001/t/data/fail1release-dist-manifest.t100644001750001750 46612716673333 16572 0ustar00idoido000000000000Tenjin-1.000001/t#!perl BEGIN { unless ($ENV{RELEASE_TESTING}) { require Test::More; Test::More::plan(skip_all => 'these tests are for release candidate testing'); } } use Test::More; eval "use Test::DistManifest"; plan skip_all => "Test::DistManifest required for testing the manifest" if $@; manifest_ok(); Preprocessor.pm100644001750001750 152512716673333 17013 0ustar00idoido000000000000Tenjin-1.000001/lib/Tenjinpackage Tenjin::Preprocessor; use strict; use warnings; our $VERSION = "1.000001"; $VERSION = eval $VERSION; our @ISA = ('Tenjin::Template'); =head1 NAME Tenjin::Preprocessor - Preprocessing Tenjin templates =head1 SYNOPSIS used internally. =head1 DESCRIPTION This module provides some methods needed for preprocessing templates. =head1 INTERNAL METHODS =head2 stmt_pattern() =cut sub stmt_pattern { return shift->SUPER::compile_stmt_pattern('PL'); } =head2 expr_pattern() =cut sub expr_pattern { return qr/\[\*=(=?)(.*?)(=?)=\*\]/s; } =head2 add_expr() =cut sub add_expr { my ($self, $bufref, $expr, $flag_escape) = @_; $expr = "decode_params($expr)"; $self->SUPER::add_expr($bufref, $expr, $flag_escape); } 1; =head1 SEE ALSO L, L. =head1 AUTHOR, LICENSE AND COPYRIGHT See L. =cut 03-capture_placeholder.t100644001750001750 101412716673333 16640 0ustar00idoido000000000000Tenjin-1.000001/t#!perl -T use strict; use warnings; use Test::More; use Tenjin; my $t = Tenjin->new({ path => ['t/data/capture_placeholder'] }); ok($t, 'Got a proper Tenjin instance'); my $context = { exists => 'hello world' }; # the capture() macro is( $t->render('capture.html', $context), "hello world\nI don't have the noexist variable.\n", 'capture works' ); # the placeholder() macro is( $t->render('placeholder.html', $context), "hello world\nI don't have the noexist variable.\n", 'placeholder works' ); done_testing(); encoding000755001750001750 012716673333 14571 5ustar00idoido000000000000Tenjin-1.000001/t/datahebrew.html100644001750001750 4012716673333 17025 0ustar00idoido000000000000Tenjin-1.000001/t/data/encoding

ג'רי סיינפלד

chinese.html100644001750001750 4512716673333 17174 0ustar00idoido000000000000Tenjin-1.000001/t/data/encodingChinese encode_url.html100644001750001750 2712716673333 17247 0ustar00idoido000000000000Tenjin-1.000001/t/data/utils[== encode_url($url) =]escape_xml.html100644001750001750 7112716673333 17247 0ustar00idoido000000000000Tenjin-1.000001/t/data/utils[== escape_xml($escape) =] [== unescape_xml($unescape) =]nested_layouts000755001750001750 012716673333 16045 5ustar00idoido000000000000Tenjin-1.000001/t/datatop.html100644001750001750 10712716673333 17653 0ustar00idoido000000000000Tenjin-1.000001/t/data/nested_layouts{_layout} = 'inside_layout.html'; ?> contentlayout_precedence000755001750001750 012716673333 16475 5ustar00idoido000000000000Tenjin-1.000001/t/datacontent.html100644001750001750 6012716673333 21131 0ustar00idoido000000000000Tenjin-1.000001/t/data/layout_precedence

[== $msg =], [== join(" ", @$arrayref) =]

capture_placeholder000755001750001750 012716673333 17010 5ustar00idoido000000000000Tenjin-1.000001/t/datacapture.html100644001750001750 44312716673333 21462 0ustar00idoido000000000000Tenjin-1.000001/t/data/capture_placeholder [== $exists =] I don't have the exists variable. [== $noexist =] I don't have the noexist variable. [== $_context->{captured_part} =]delete-newline000755001750001750 012716673333 15704 5ustar00idoido000000000000Tenjin-1.000001/t/datadelete-newline.txt100644001750001750 3712716673333 21446 0ustar00idoido000000000000Tenjin-1.000001/t/data/delete-newlineline [== $var ==] another line inside_layout.html100644001750001750 13012716673333 21715 0ustar00idoido000000000000Tenjin-1.000001/t/data/nested_layouts{_layout} = 'outside_layout.html'; ?> [== $_content ==]outside_layout.html100644001750001750 4412716673333 22102 0ustar00idoido000000000000Tenjin-1.000001/t/data/nested_layouts[== $_content ==]delete-no-newline.txt100644001750001750 4112716673333 22053 0ustar00idoido000000000000Tenjin-1.000001/t/data/delete-newlineline "[== $var ==]" another line placeholder.html100644001750001750 31012716673333 22272 0ustar00idoido000000000000Tenjin-1.000001/t/data/capture_placeholder I don't have the exists variable. I don't have the noexist variable. render_layout.html100644001750001750 3512716673333 22335 0ustar00idoido000000000000Tenjin-1.000001/t/data/layout_precedence[== $_content =]context_layout.html100644001750001750 3312716673333 22540 0ustar00idoido000000000000Tenjin-1.000001/t/data/layout_precedence[== $_content =]instance_layout.html100644001750001750 3512716673333 22662 0ustar00idoido000000000000Tenjin-1.000001/t/data/layout_precedence[== $_content =]