INTRODUCTION
This is a step-by-step tutorial on how to use Plift. We will start using 100% pure HTML templates, so you can learn how to render data using render directives.
Then you'll learn how to use the builtin processing instructions in your template files. There are just three: <x-include/>
, <x-wrap/>
and <x-render/>
.
Finnaly, I'll show how to create your own custom processing instructions (triggered by custom tags/attributes) and how to bundle them in the form of reusable components/plugins.
HELLO WORLD
Lets start by the classic "Hello World" example.
First our template file, index.html
:
<section>
<h1>Message placeholder</h1>
</section>
Now our script, hello_world.pl
:
### hello_world.pl ###
use Plift;
my $plift = Plift->new;
my %data = (
hello_message => 'Hello World!'
);
my @directives = (
'h1' => 'hello_message'
);
my $html = $plift->render('index', \%data, \@directives );
That will render (not surprisingly) the following text into $html
:
<section>
<h1>Hello World!</h1>
</section>
Ok, now lets explain what $plift-
render > does. As you can see, we passed three arguments: the template name, some data, and a list of render directives. The first is the name of the template file, without the .html
extension. The second argument \%data
is a reference to a hash of data to make available for templates. Finally, the third argument \@directives
is a reference to a list of render directives, which are simple rules mapping a CCS selector to a data key.
Our example uses only one render directive, 'h1' =
'hello_message' >, that tells Plift to render the value of the 'hello_message' data key into the <h1>
element. If there were multiple <<h1
>> elements in the template, all of them would get rendered. Under the hood, Plift uses XML::LibXML::jQuery to manipulate the HTML templates. The render directive in the example is equivalent to the following code:
my $data_value = $context->get('hello_message');
$document->find('h1')->text($data_value);
THE PATH
In the above example Plift used the default config for the 'path' option, which is the current working dir. In a real world application you usualy pass some pre defined template path.
If you pass an arrayref with multiple paths, Plift will search all of them in order. You can use that funcionality to create a simple way to "extend" the website "theme" or "skin":
my $plift = Plift->new(
path => [
'/myapp/website-foo/skin/templates/',
'/myapp/share/skin/base/templates/'
]
);
Any file with the same name (and relative path) on the first paths will read instead of the equivalent file of the second path. If you ask for the template 'error_pages/404'
(remember, no .html extension), Plift first looks for /myapp/website-foo/skin/templates/error_pages/404.html
then /myapp/share/skin/base/templates/error_pages/404.html
.
THE CONTEXT
Before proceding, we must learn about the Plift::Context object. Is is responsible for holding all data and render directives for the given rendering... context.
In fact, the methods "render" in Plift and "process" in Plift methods are just wrappers around the methods "set" in Plift::Context, "at" in Plift::Context and "render" in Plift::Context. The follwing code:
my $html = $plift->render('index', \%data, \@directives );
is equivalent to:
my $tpl = $plift->template('index'); # returns a Plift::Context inst
$tpl->set(\%data);
$tpl->at();
RENDER DIRECTIVES
Render directives are pairs of <MATCH =
ACTION>> values. The match part is a CSS selector that target the element the action applies to. You can use any selector supported by HTML::Selector::XPath. The action part takes a few variations, as described next.
All render directives and template data are stored in the Plift::Context object. Directives are added using the method "at" in Plift::Context. And data is added using the using the method "set" in Plift::Context. ance
Scalar - Interpolate data value
$tpl->set
$tpl->at( '.first-name' => 'user.first_name' );
This is the most common directive, where the selector points to a data point, like '.first-name' =
'user.first_name' >. By default the data value is rendered as the element content. You can add a special suffix to you MATCH in the form of '<selector>@<attribute>' to render the value as an element's HTML attribute. A final variation exists to allow the data value to be rendered as HTML in the element's content, set the special sufixx '@HTML' (yes, uppercase.)
- HTML vs TEXT
-
The distinction between text and HTML is very important, is the cause of the XSS vulnerability. This vulnerability got so common on web because almost all template engines are designed to process text. These engines can be used to render a config file, a christmas letter, and also HTML. Sure, all of those templating engines also provides a way to properly HTML-escape a value, usualy in the form of filters. But being a text templating engine, they can't just HTML-escape values by default. The reality when using that kind of engine is that developers can (and will) forget to add the proper filters, or just don't know about this security vulnerability at all.
Plift is a template engine just for HTML, and it's implemented on top of libxml. It means that for rendering text, we call "createTextNode" in XML::LibXML::Document, for attributes, "setAttribute" in XML::LibXML::Document and for rendering the final document we call "toStringHTML" in XML::LibXML::Document. Libxml takes care of the proper escaping.
Does it mean I'm immune to XSS vulnerability? No, you can still tell Plift to render unescaped HTML, but its not the default.
Arrayref - Run directives under a new DOM root
Hashref - Move the root of the Data Context
HashRef - Create a loop
CodeRef - Programmatically render the element
More on the selector
THE DOT NOTATION
PROCESSING INSTRUCTIONS
ELEMENT REMOVAL
CUSTOM HANDLERS
CREATING REUSABLE COMPONENTS
AUTHOR
Carlos Fernando Avila Gratz <cafe@kreato.com.br>