NAME
Template::EmbeddedPerl - A template processing engine using embedding Perl code
SYNOPSIS
use Template::EmbeddedPerl;
Create a new template object:
my $template = Template::EmbeddedPerl->new(); # default open and close tags are '<%' and '%>'
Compile a template from a string:
my $compiled = $template->from_string('Hello, <%= shift %>!');
execute the compiled template:
my $output = $compiled->render('John');
$output is:
Hello, John!
You can also use class methods to create compiled templates in one step if you don't need the reusable template object
my $compiled = Template::EmbeddedPerl->from_string('Hello, <%= shift %>!');
my $output = $compiled->render('John');
Or you can render templates from strings directly:
my $template = Template::EmbeddedPerl->new(use_cache => 1); # cache compiled templates
my $output = $template->render('Hello, <%= shift %>!', 'John');
Other class methods are available to create compiled templates from files, file handles, and data sections. See the rest of the docs for more information.
DESCRIPTION
Template::EmbeddedPerl is a template engine that allows you to embed Perl code within template files or strings. It provides methods for creating templates from various sources, including strings, file handles, and data sections.
The module also supports features like helper functions, automatic escaping, and customizable template compilation namespaces.
Its quite similar to Mojo::Template and other embedded Perl template engines but its got one trick the others can't do (see EXCUSE below).
The core supports standalone composition with partials, layouts, named content blocks, and optional typed view objects. Framework integrations can provide a view resolver when logical child names need application-specific construction or dependency injection.
ACKNOWLEDGEMENTS
I looked at Mojo::Template and I lifted some code and docs from there. I also copied some of their test cases. I was shooting for something reasonable similar and potentially compatible with Mojo::Template but with some additional features. Template::EmbeddedPerl is similiar to how template engines in popular frameworks like Ruby on Rails and also similar to EJS in the JavaScript world. So nothing weird here, just something people would understand and be comfortable with. A type of lowest common denominator. If you know Perl, you will be able to use this after a few minutes of reading the docs (or if you've used Mojo::Template or Mason you might not even need that).
EXCUSE
Why create yet another one of these embedded Perl template engines? I wanted one that could properly handle block capture like following:
<% my @items = map { %>
<p><%= $_ %></p>
<% } @items %>
Basically none of the existing ones I could find could handle this. If I'm wrong and somehow there's a flag or approach in Mason or one of the other ones that can handle this please let me know.
Mojo::Template is close but you have to use begin and end tags to get a similar effect and it's not as flexible as I'd like plus I want to be able to use signatures in code like the following:
<%= $f->form_for($person, sub($view, $fb, $person) { %>
<div>
<%= $fb->label('first_name') %>
<%= $fb->input('first_name') %>
<%= $fb->label('last_name') %>
<%= $fb->input('last_name') %>
</div>
<% }) %>
Again, I couldn't find anything that could do this. Its actually tricky because of the way you need to localize capture of template output when inside a block. I ended up using PPI to parse the template so I could properly find begin and end blocks and also distinguish between control blocks (like if an unless) blocks that have a return like sub or map blocks. In Mojo::Template you can do the following (its the same but not as pretty to my eye):
<% my $form = $f->form_for($person, begin %>
<% my ($view, $fb, $person) = @_; %>
<div>
<%= $fb->label('first_name') %>
<%= $fb->input('first_name') %>
<%= $fb->label('last_name') %>
<%= $fb->input('last_name') %>
</div>
<% end; %>
On the other hand my system is pretty new and I'm sure there are bugs and issues I haven't thought of yet. So you probably want to use one of the more mature systems like Mason or Mojo::Template unless you really need the features I've added. Or your being forced to use it because you're working for me ;)
TEMPLATE SYNTAX
The template syntax is similar to other embedded Perl template engines. You can embed Perl code within the template using opening and closing tags. The default tags are '<%' and '%>', but you can customize them when creating a new template object. You should pick open and closing tags that are not common in your template content.
All templates get strict, warnings and utf8 enabled by default. Please note this is different than Mojo::Template which does not seem to have warnings enabled by default. Since I like very strict templates this default makes sense to me but if you tend to play fast and loose with your templates (for example you don't use my to declare variables) you might not like this. Feel free to complain to me, I might change it.
Basic Patterns:
<% Perl code %>
<%= Perl expression, replaced with result %>
Examples:
<% my @items = qw(foo bar baz) %>
<% foreach my $item (@items) { %>
<p><%= $item %></p>
<% } %>
Would output:
<p>foo</p>
<p>bar</p>
<p>baz</p>
You can also use the 'line' version of the tags to make it easier to embed Perl code, or at least potentially easier to read. For example:
% my @items = qw(foo bar baz) % foreach my $item (@items) { <p><%= $item %></p> % }
Smart Lines and Named Arguments
Set smart_lines to a true value to make a line beginning with % or %= a complete directive without a trailing delimiter. This is especially useful for declarative named template arguments:
% args $name, $greeting = 'Hello', $heading = sub { "Hello, $name" }
<p><%= $heading %></p>
args must be the first executable directive. An argument without a default is required; a scalar expression supplies a default; a coderef is a lazy default evaluated only when its argument is absent. An explicit undef is a supplied value and does not evaluate a lazy default. Render arguments are named key/value pairs when a template declares args.
You can add '=' to the closing tag to indicate that the expression should be trimmed of leading and trailing whitespace. This is useful when you want to include the expression in a block of text. where you don't want the whitespace to affect the output.
<%= Perl expression, replaced with result, trimmed =%>
You can add '-' to the closing tag to trim whitespace after the tag through the next newline. This is useful when a readable code block should not emit a leading newline before the next element, for example in partial HTML responses.
<% Perl code -%>
<%= Perl expression, replaced with result -%>
If you want to skip the newline after the closing tag you can use a backslash.
<% Perl code %>\
<%= Perl expression, replaced with result, no newline %>\
You probably don't care about this so much with HTML since it collapses whitespace but it can be useful for other types of output like plain text or if you need some embedded Perl inside your JavaScript.
If you really need that backslash in your output you can escape it with another backslash.
<%= "This is a backslash: " %>\\
If you really need to use the actual tags in your output you can escape them with a backslash.
\<% => <%
\<%= => <%=
\%> => %>
\%= => %=
\% => %
Lastly you can add full line comments to your templates that will be removed from the final output
# This is a comment
<p>Regular HTML</p>
A comment is declared with a single # at the start of the line (or with only whitespace preceeding it). This line will be removed from the output, including its newline. If you really need a '#'you can escape it with \# (this is only needed if the '#' is at the beginning of the line, or there's only preceding whitespace.
Interpolation Syntax
If you want to embed Perl variables directly in the template without using the %= ... % syntax, you can enable interpolation. This allows you to embed Perl variables directly in the template without using the %= ... % syntax. For example:
my $template = Template::EmbeddedPerl->new(interpolation => 1, prepend => 'my $name = shift');
my $compiled = $template->from_string('Hello, $name!');
my $output = $compiled->render('John');
$output is:
Hello, John!
This works by noticing a '$' followed by a Perl variable name (and method calls, etc). So if you need to put a real '$' in your code you will need to escape it with \$.
This only works on a single line and is intended to help reduce template complexity and noise for simple placeholder template sections. Nevertheless I did try top make it work with reasonable complex single variable expressions. Submit a test case if you find something that doesn't work which you think should.
See the section on the interpolation configuration switch below for more information. This is disabled by default and I consider it experimental at this time since parsing Perl code with regular expressions is a bit of a hack.
METHODS
new
my $template = Template::EmbeddedPerl->new(%args);
Creates a new Template::EmbeddedPerl object. Accepts the following arguments:
open_tagThe opening tag for template expressions. Default is
'<%'. You should use something that's not common in your template content.close_tagThe closing tag for template expressions. Default is
'%>'.expr_markerThe marker indicating a template expression. Default is
'='.sandbox_nsThe namespace where templates are compiled. Default is
'Template::EmbeddedPerl::Sandbox'. This isolates unqualified symbols but is not a security boundary: templates execute arbitrary Perl and can access modules, files, processes, and application globals. Only compile templates from trusted sources.directoriesAn array reference of directories to search for templates. Default is an empty array. A directory to search can be either a string or an array reference containing each part of the path to the directory. Directories will be searched in order listed.
my $template = Template::EmbeddedPerl->new(directories=>['/path/to/templates']); my $template = Template::EmbeddedPerl->new(directories=>[['/path', 'to', 'templates']]);I don't do anything smart to make sure you don't reference templates in dangerous places. So be careful to make sure you don't let application users specify the template path.
The first matching file wins. Partials, layouts, explicit typed-view templates, and convention-derived typed-view templates use this same ordered search path.
smart_linesBoolean indicating whether a line beginning with
%or%=is a complete directive. Default is0. See "Smart Lines and Named Arguments".view_namespaceOptional namespace prefix for logical typed-view class lookup and convention-based typed-view template lookup. For a logical name, the engine prefixes this namespace to find the class. For a view without an explicit template, the matching prefix is removed,
::becomes/, and each remaining CamelCase segment is converted to lowercase snake case. Acronym runs stay together:HTML,HTMLPage, andContactListbecomehtml,html_page, andcontact_list.For example,
MyApp::View::HTML::ContactListwithview_namespace => 'MyApp::View'resolveshtml/contact_listbefore the normaltemplate_extensionis added.view_factoryOptional coderef used to construct nested logical typed views. It receives
($class, \%args, $context)and must return a blessed object.$classis the class resolved from the logical name andview_namespace;\%argscontains only values supplied by the template. Use it for dependency injection, such asrootandparentrelationships. Without a factory, the engine calls$class->new(%args).template_extensionThe file extension for template files. Default is
'epl'. So for example:my $template = Template::EmbeddedPerl->new(directories=>['/path/to/templates', 'path/to/other/templates']); my $compiled = $template->from_file('hello');Would look for a file named
hello.eplin the directories specified.auto_escapeBoolean indicating whether to automatically escape content. Default is
0. You probably want this enabled for web content to prevent XSS attacks. If you have this on and want to return actual HTML you can use therawhelper function. Example:<%= raw '<a href="http://example.com">Example</a>' %>Obviously you need to be careful with this.
The
rawhelper (also available as a method on the template object) takes the string and turns it into an instance of Template::EmbeddedPerl::SafeString. The auto escape code when it sees that object knows to pass it thru without trying to escape it. Therawhelper has a version calledsafewhich does any needed encoding first (or passes unchanged any already created safe string objects).If the value is an object that does
to_safe_stringthen the object will first be converted to a safe string by calling it. Legacyrendermethods pass the template engine as the first parameter; typedrender_viewrendering passes the active view. That allows you to safely stringify objects without needing to do so manually.NOTE we only check for objects with the
to_safe_stringmethod when usingauto_escapeIf you are not using this safety feature and you are manually performing any needed escaping then you can just use ordinary overloading to stringify your object values.auto_flatten_exprBoolean indicating whether to automatically flatten expressions. Default is
1. What this means is that if you have an expression that returns an array we will join the array into a string before outputting it. Example:<% my @items = qw(foo bar baz); %> <%= map { "$_ " } @items %>Would output:
foo bar bazpreambleAdd Perl code to the 'preamble' section of the compiled template. This is to top of the generated script prior to the anonymous sub representing your template.Default is an empty string. For example you can enable modern Perl features like signatures by setting this to
'use v5.40;'.Use this to setup any pragmas or modules you need to use in your template code.
prependPerl code to prepend to the compiled template. Default is an empty string. This goes just inside the anonyous subroutine that is called to return your document string. For example you can use this to pull passed arguments off
@_.helpersA hash reference of helper functions available to the templates. Default is an empty hash. You can add your own helper functions to this hash and they will be available to the templates. Example:
my $template = Template::EmbeddedPerl->new(helpers => { my_helper => sub { return 'Hello, World!' }, });use_cacheBoolean indicating whether to cache compiled templates. Default is
0. If you set this to1, the module will cache compiled templates in memory. This is only useful if you are throwing away the template object after compiling a template. For example:my $ep = Template::EmbeddedPerl->new(use_cache => 1); my $output = $ep->render('Hello, <%= shift %>!', 'John');In the case above since you are not capturing the compiled template object each time you call
renderyou are recompiling the template. which could get expensive.On the other hand if you are keeping the template object around and reusing it you don't need to enable this. Example:
my $ep = Template::EmbeddedPerl->new(use_cache => 1); my $compiled = $ep->from_string('Hello, <%= shift %>!'); my $output = $compiled->render('John');In the valid above the compiled template is cached and reused each time you call
render.Obviously this only works usefully in a persistent environment like mod_perl or a PSGI server.
comment_markDefaults to '#'. Indicates the beginning of a comment in the template which is to be removed from the output.
interpolationBoolean indicating whether to enable interpolation in the template. Default is
0(disabled).Interpolation allows you to embed Perl variables directly in the template without using the
%= ... %syntax. For example:my $template = Template::EmbeddedPerl->new(interpolation => 1, prepend => 'my ($name) = @_'); my $output = $template->render('Hello, $name!', 'John');This will output:
Hello, John!Interpolation is reasonable sophisticated and will handle many cases including have more then one variable in a line. For example:
my $template = Template::EmbeddedPerl->new(interpolation => 1, prepend => 'my ($first, $last) = @_'); my $output = $template->render('Hello, $first $last!', 'John', 'Doe');This will output:
Hello, John Doe!It can also handle variables that are objects and call methods on them. For example:
my $template = Template::EmbeddedPerl->new(interpolation => 1, prepend => 'my ($person_obj) = @_'); my $output = $template->render('Hello, $person_obj->first_name $person_obj->last_name!', Person->new('John', 'Doe'));This will output:
Hello, John Doe!If you need to disambiguate a variable from following text you enclose the variable in curly braces.
my $template = Template::EmbeddedPerl->new(interpolation => 1); my $output = $template->render('Hello, ${arg}XXX', 'John');This will output:
Hello, JohnXXXYou can nest method calls and the methods can contain arguments of varying complexity, including anonymous subroutines. There is a limited ability to span lines; you make break lines across the deference operator and in many cases across balanced parenthesis, square and curly brackets. If you do so you cannot mix 'template text' with the Perl code. For example:
<div class="name"> $person->profile ->first_name </div>and
$obj->compute( sub { my $arg = shift; return $arg * 2; } )are both valid. But this will fail:
<p> $arg->compute(sub { my $value = shift; $value = $value * 5 <div>$value</div> }) </p>For this case you need to use the
%= ... %syntax or %= and %:<p> %= $arg->compute(sub { % my $value = shift; % $value = $value * 5 <div>$value</div> % }) </p>You can review the existing test case at
t/interpolation.tfor examples.This works by noticing a '$' followed by a Perl variable name (and method calls, etc). So if you need to put a real '$' in your code you will need to escape it with
\$. It does not work for other perl sigils at this time (for example '@' or '%').This feature is experimental so if you have trouble with it submit a trouble ticket with test case (review the
t/interpolation.ttest cases for examples of the type of test cases I need). I intend interpolation to be a 'sweet spot' feature that tries to reduce amount of typing and overall template 'noise', not something that fully parses Perl code. Anything super crazy should probably be encapsulated in a helper function anyway.
from_string
my $compiled = $template->from_string($template_string, %args);
Creates a compiled template from a string. Accepts the template content as a string and optional arguments to modify behavior. Returns a Template::EmbeddedPerl::Compiled object.
pass 'source => $path' to the arguments to specify the source of the template if you want neater error messages.
This can be called as a class method as well::
my $compiled = Template::EmbeddedPerl->from_string($template_string, %args);
Useful if you don't need to keep the template object around. This works for all the other methods as well (from_file, from_fh, from_data).
from_file
my $compiled = $template->from_file($file_name, %args);
Creates a compiled template from a file. Accepts the filename (without extension) and optional arguments. Searches for the file in the directories specified during object creation.
from_fh
my $compiled = $template->from_fh($filehandle, %args);
Creates a compiled template from a file handle. Reads the content from the provided file handle and processes it as a template.
pass 'source => $path' to the arguments to specify the source of the template if you want neater error messages.
from_data
my $compiled = $template->from_data($package, %args);
Creates a compiled template from the __DATA__ section of a specified package. Returns a compiled template object or dies if the package cannot be loaded or no __DATA__ section is found.
trim
my $trimmed = $template->trim($string);
Trims leading and trailing whitespace from the provided string. Returns the trimmed string.
mtrim
Same as trim but trims leading and trailing whitespace for a multiline string.
default_helpers
my %helpers = $template->default_helpers;
Returns a hash of default helper functions available to the templates.
get_helpers
my %helpers = $template->get_helpers($helper_name);
Returns a specific helper function or all helper functions if no name is provided.
parse_template
my @parsed = $template->parse_template($template);
Parses the provided template content and returns an array of parsed blocks.
compile
my $code = $template->compile($template, @parsed);
Compiles the provided template content into executable Perl code. Returns a code reference.
directory_for_package
my $directory = $template->directory_for_package($package);
Returns the directory containing the package file. If you don't provide a package name it will use the current package for $template.
Useful if you want to load templates from the same directory as your package.
render
my $output = $template->render($template, @args);
Compiles and executes the provided template content with the given arguments. You might want to enable the cache if you are doing this.
Compatibility note: render and Template::EmbeddedPerl::Compiled::render keep every legacy argument, including a blessed first argument, in @_. They do not infer a typed $self.
render_view
Experimental: Typed view support, including render_view, view, view_namespace, and view_factory, may change as real-world integration needs become clearer.
my $output = $template->render_view($view);
Renders a preconstructed blessed view object. Its template receives that object as lexical $self. A non-blessed value is an error. Root and nested views use the same template precedence:
A nonempty
$view->templateresult.The
view_namespaceconvention.
Use the view helper for a nested typed object. view $object renders a preconstructed child, which bypasses construction. view $logical_name, %args resolves its class from view_namespace and calls $class->new(%args) by default. If configured, view_factory alone performs construction and receives ($class, \%args, $context); use it to inject dependencies. The final coderef, if any, is a wrapper body. In that callback lexical $self remains the caller; the callback argument is the wrapper object, and the wrapper template itself receives the wrapper as $self.
The core accepts any blessed object. Moo is used only by the test and cookbook examples. Template::EmbeddedPerl::Cookbook::TypedViews contains complete Moo examples for applications that use it.
HELPER FUNCTIONS
The module provides a set of default helper functions that can be used in templates.
rawReturns a string as a safe string object without escaping. Useful if you want to return actual HTML to your template but you better be sure that HTML is safe.
<%= raw '<a href="http://example.com">Example</a>' %>safeReturns a string as a safe html escaped string object that will not be escaped again.
safe_concatLike
safebut for multiple strings. This will concatenate the strings into a single string object that will not be escaped again.html_escapeEscapes HTML entities in a string. This differs for
safein that it will just do the escaping and not wrap the string in a safe string object.url_encodeEncodes a string for use in a URL.
escape_javascriptEscapes a value for use inside a JavaScript string, including preventing a closing
scripttag from terminating an enclosing HTML script element. This is not a general JavaScript sanitizer; use a context-appropriate sanitizer for untrusted code.trimTrims leading and trailing whitespace from a string.
partialpartial $identifier, %argsrenders an untyped template immediately with the caller's$self. Its output is safe rendered output and is not escaped a second time byauto_escape.layoutlayout $identifier, %argsregisters an untyped outer template for the current output. Layout arguments are independent named arguments for the layout. Multiple layouts nest with the first declaration outermost.yieldyieldreturns the current body.yield $namereturns named content captured in the current render frame.content_for,content_replace, andhas_contentcontent_for $name, sub { ... }appends named content in render order.content_replacereplaces it, andhas_content $namereports whether the named content is nonempty.viewRenders a typed child as described in "render_view". The optional final coderef supplies the wrapper body.
ERROR HANDLING
If an error occurs during template compilation or rendering, the module will throw an exception with a detailed error message. The error message includes the source of the template, the line number, and the surrounding lines of the template to help with debugging. Example:
Can't locate object method "input" at /path/to/templates/hello.yat line 4.
3: <%= label('first_name') %>
4: <%= input('first_name') %>
5: <%= errors('last_name') %>
Each top-level render, compiled-template render, and render_view call creates exactly one render frame. Nested partials, layouts, and views share that frame. Render cycles are rejected with their active chain. A nested exception is decorated with one source-aware Render stack; failed rendering restores the frame's body, named content, layouts, and stack state so the engine can be reused for a later top-level render.
ENVIRONMENT VARIABLES
The module respects the following environment variables:
DEBUG_TEMPLATE_EMBEDDED_PERLSet this to a true value to print the compiled template code to the console. Useful when trying to debug difficult compilation issues, especially given this is early access code and you might run into bugs.
REPORTING BUGS & GETTING HELP
If you find a bug, please report it on the GitHub issue tracker at https://github.com/jjn1056/Template-EmbeddedPerl/issues. The bug tracker is the easiest way to get help with this module from me but I'm also on irc.perl.org under jnap.
DEDICATION
This module is dedicated to the memory of my dog Bear who passed away on 17 August 2024. He was a good companion and I miss him.
If this module is useful to you please consider donating to your local animal shelter or rescue organization.
AUTHOR
John Napiorkowski, <jjnapiork@cpan.org>
LICENSE AND COPYRIGHT
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.