NAME
Template::Semantic - Use pure XHTML/XML as a template
SYNOPSIS
use Template::Semantic;
print Template::Semantic->process('template.html', {
'title, h1' => 'Naoki Tomita',
'ul.urls li' => [
{
'a' => 'Homepage >',
'a@href' => 'http://e8y.net/',
'a@target' => undef,
},
{
'a' => 'Twitter >',
'a@href' => 'http://twitter.com/tomita/',
'a@target' => '_blank',
},
],
});
template:
<html>
<head><title>person name</title></head>
<body>
<h1>person name</h1>
<ul class="urls">
<li><a href="#" target="">his page</a></li>
</ul>
</body>
</html>
output:
<html>
<head><title>Naoki Tomita</title></head>
<body>
<h1>Naoki Tomita</h1>
<ul class="urls">
<li><a href="http://e8y.net/">Homepage ></a></li>
<li><a href="http://twitter.com/tomita/" targer="_blank">Twitter ></a></li>
</ul>
</body>
</html>
DESCRIPTION
Template::Semantic is a template engine for XHTML/XML that doesn't use any template syntax. This module takes pure XHTML/XML as a template, and uses XPath or CSS selector to assign value.
This is beta release. Your feedback is welcome.
See Template::Semantic::Cookbook for the practical examples.
METHODS
- $ts = Template::Semantic->new( %options )
-
Constructs a new
Template::Semantic
object.my $ts = Template::Semantic->new; my $out = $ts->process(...);
Template::Semantic uses XML::LibXML parser as follows by default.
my $parser = XML::LibXML->new; $parser->no_newwork(1); # faster $parser->recover(2); # = recover_silently(1) = no warnings
If you may not change this, call
process()
directly, skipnew()
.my $out = Template::Semantic->process(...);
Set %options if you want to change parser options:
parser
Set if you want to replace XML parser. It should be XML::LibXML based.
my $ts = Template::Semantic->new( parser => $your_libxml_parser, );
(others)
All other parameters except
"parser"
are passed to XML parser like$parser->$key($value)
. See XML::LibXML::Parser for details.my $ts = Template::Semantic->new( recover => 1, expand_xinclude => 1, );
- $out = $ts->process($filename, \%vars)
- $out = $ts->process(\$text, \%vars)
- $out = $ts->process(FH, \%vars)
-
Process a template and returns Template::Semantic::Document object.
The 1st parameter is the input template that can take these types:
# filename my $out = Tempalte::Semantic->('template.html', $vars); # text reference my $out = Tempalte::Semantic->(\'<html><body>foo</body></html>', $vars); # file handle my $out = Tempalte::Semantic->($fh, $vars); my $out = Tempalte::Semantic->(\*DATA, $vars);
The 2nd parameter is a value set to bind the template. This should be hash-ref like { 'selector' => $value, 'selector' => $value, ... }. See below "SELECTOR" and "VALUE TYPE" section.
- $ts->define_filter($filter_name, \&code)
- $ts->call_filter($filter_name)
-
See "Filter" section.
SELECTOR
Use XPath expression or CSS selector as a selector.
print Tempalte::Semantic->process($template, {
# XPath sample that indicate tag:
'/html/body/h2[2]' => ...,
'//title | //h1' => ...,
'//img[@id="foo"]' => ...,
'id("foo")' => ...,
# XPath sample that indicate attribute:
'//a[@id="foo"]/@href' => ...,
'//meta[@name="keywords"]/@content' => ...,
# CSS selector sample that indicate tag:
'title' => ...,
'.foo span.bar' => ...,
'#foo' => ...,
# CSS selector sample that indicate attribute:
'img#foo@src' => ...,
'span.bar a@href' => ...,
});
Note 1: CSS selector is converted to XPath internally. You can use '@attr' expression to indicate attribute in this module unlike CSS format.
Note 2: You can use 'id()' function in XHTML (with html xmlns="..."
) without using XML::LibXML::XPathContext. This module sets xmlns=""
namespace declarations automatically if template like a XHTML.
VALUE TYPE
Basics
selector => $text
Scalar: Replace the inner content with this as Text.
$ts->process($template, { 'h1' => 'foo & bar', # <h1></h1> => # <h1>foo & bar</h1> '.foo@href' => '/foo', # <a href="#" class="foo">bar</a> => # <a href="/foo" class="foo">bar</a> });
selector => \$html
Scalar-ref: Replace the inner content with this as flagment XML/HTML.
$ts->process($template, { 'h1' => \'<a href="#">foo</a>bar', # <h1></h1> => # <h1><a href="#">foo</a>bar</h1> });
selector => undef
undef: Delete the element/attirbute that the selector indicates.
$ts->process($template, { 'h1' => undef, # <div><h1></h1>foo</div> => # <div>foo</div> 'div.foo@class' => undef, # <div class="foo">foo</div> => # <div>foo</div> });
selector => \&foo
Code-ref: Callback subroutine. Subroutine can user
$_
as inner HTML or first argument as XML::LibXML::Node object.$ts->process($template, { 'h1' => sub { uc }, # <h1>foo</h1> => <h1>FOO</h1> 'h1' => sub { my $node = shift; $node->nodeName; # <h1>foo</h1> => <h1>h1</h1> }, });
selector => XML::LibXML::Node
Replace the inner content by the node.
$ts->process($template, { 'h1' => do { XML::LibXML::Text->new('foo') }, });
selector => Template::Semantic::Document
Replace the inner content by another
process()
-ed result.$ts->process('wrapper.html', { 'div#content' => $ts->process('inner.html', ...), });
selector => { 'selector' => $value, ... }
Hash-ref: Sub query of the part.
$ts->process($template, { 'div.foo' => { 'a' => undef, # All <a> tag *in <div class="foo">* disappears }, 'div.foo a' => undef, # same as above });
Loop
selector => [ \%row, \%row, ... ]
Array-ref of Hash-refs: Loop the part as template. Each item of the array-ref should be hash-ref.
$ts->process(\*DATA, { 'table.list tr' => [ { 'th' => 'aaa', 'td' => '001' }, { 'th' => 'bbb', 'td' => '002' }, { 'th' => 'ccc', 'td' => '003' }, ], }); __DATA__ <table class="list"> <tr> <td></td> <td></td> </tr> </table>
Output:
<table class="list"> <tr> <th>aaa</th> <td>001</td> </tr> <tr> <th>bbb</th> <td>002</td> </tr> <tr> <th>ccc</th> <td>003</td> </tr> </table>
Filter
selector => [ $value, filter, filter, ... ]
Array-ref of Scalars: Value and filters. Filter can take
A) Callback subroutine
B) Defined filter name
C) Object like Text::Pipe (
it->can('filter')
)$ts->process($template, { 'h1' => [ 'foo', sub { uc }, sub { "$_!" } ], # => <h1>FOO!</h1> 'h2' => [ ' foo ', 'trim', sub { "$_!" } ], # => <h2>FOO!</h2> 'h3' => [ 'foo', PIPE('UppercaseFirst') ], # => <h3>Foo</h3> });
- Defined basic filters
-
Some basic filters included. See Template::Semantic::Filter.
- $ts->define_filter($filter_name, \&code)
-
You can define the your filter name using
define_filter()
.use Text::Markdown qw/markdown/; $ts->define_filter(markdown => sub { \ markdown($_) }) $ts->process($template, { 'div.content' => [ $text, 'markdown' ], });
- $code = $ts->call_filter($filter_name)
-
Accessor to defined filter.
$ts->process($template, { 'div.entry' => ..., 'div.entry-more' => ..., })->process({ 'div.entry, div.entry-more' => $ts->call_filter('markdown'), });
SEE ALSO
XML::LibXML, HTML::Selector::XPath
I got a lot of ideas from Template, Template::Refine, Web::Scraper. thanks!
AUTHOR
Naoki Tomita <tomita@cpan.org>
Feedback, patches, POD English check are always welcome!
LICENSE
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.