Template::Semantic - Use pure XHTML/XML as a template
use Template::Semantic; print Template::Semantic->process('template.html', { 'title, h1' => 'Naoki Tomita', 'ul.urls li' => [ { 'a' => 'Profile & Contacts', 'a@href' => 'http://e8y.net/', }, { 'a' => 'Twitter', 'a@href' => 'http://twitter.com/tomita/', }, ], });
template.html
<html> <head><title>person name</title></head> <body> <h1>person name</h1> <ul class="urls"> <li><a href="#">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/">Profile & Contacts</a></li> <li><a href="http://twitter.com/tomita/">Twitter</a></li> </ul> </body> </html>
Template::Semantic is a template engine for XHTML/XML based on XML::LibXML that doesn't use any template syntax. This module takes pure XHTML/XML as a template, and uses XPath or CSS selectors to assign values.
Constructs a new Template::Semantic object.
Template::Semantic
my $ts = Template::Semantic->new( ... ); my $res = $ts->process(...);
If you do not want to change the options from the defaults, you may skip new() and call process() directly:
new()
process()
my $res = Template::Semantic->process(...);
Set %options if you want to change parser options:
parser => $your_libxml_parser
Set if you want to replace XML parser. It should be XML::LibXML based.
my $ts = Template::Semantic->new( parser => My::LibXML->new, );
(others)
All other parameters are applied to the XML parser as method calls ($parser->$key($value)). Template::Semantic uses this configuration by default:
$parser->$key($value)
no_newwork => 1 # faster recover => 2 # "no warnings" style
See "PARSER OPTIONS" in XML::LibXML::Parser for details.
# "use strict;" style my $ts = Template::Semantic->new( recover => 0 ); # "use warnings;" style my $ts = Template::Semantic->new( recover => 1 );
Process a template and return a Template::Semantic::Document object.
The first parameter is the input template, which may take one of several forms:
# filename my $res = Template::Semantic->process('template.html', $vars); # text reference my $res = Template::Semantic->process(\'<html><body>foo</body></html>', $vars); # file handle, GLOB my $res = Template::Semantic->process($fh, $vars); my $res = Template::Semantic->process(\*DATA, $vars);
The second parameter is a value set to bind the template. $vars should be a hash-ref of selectors and corresponding values. See the "SELECTOR" and "VALUE TYPE" sections below. For example:
{ '.foo' => 'hello', '//title' => 'This is a title', }
See the "Filter" section.
Use XPath expression or CSS selector as a selector. If the expression doesn't look like XPath, it is considered CSS selector and converted into XPath internally.
print Template::Semantic->process($template, { # XPath sample that indicate <tag> '/html/body/h2[2]' => ..., '//title | //h1' => ..., '//img[@id="foo"]' => ..., 'id("foo")' => ..., # XPath sample that indicate @attr '//a[@id="foo"]/@href' => ..., '//meta[@name="keywords"]/@content' => ..., # CSS selector sample that indicate <tag> 'title' => ..., '#foo' => ..., '.foo span.bar' => ..., # CSS selector sample that indicate @attr 'img#foo@src' => ..., 'span.bar a@href' => ..., '@alt, @title' => ..., });
Template::Semantic allows some selector syntax that is different from usual XPath for your convenience.
1. You can use xpath '//div' without using XML::LibXML::XPathContext even if your template has default namespace (<html xmlns="...">).
'//div'
<html xmlns="...">
2. You can use 'id("foo")' function to find element with id="foo" instead of xml:id="foo" without DTD. Note: use '//*[@xml:id="foo"]' if your template uses xml:id="foo".
'id("foo")'
id="foo"
xml:id="foo"
'//*[@xml:id="foo"]'
3. You can '@attr' syntax with CSS selector that specifies the attribute. This is original syntax of this module.
'@attr'
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 fragment 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>foo</h1>bar</div> => # <div>bar</div> 'div.foo@class' => undef, # <div class="foo">foo</div> => # <div>foo</div> });
selector => XML::LibXML::Node
Replace the inner content by the node. XML::LibXML::Attr isn't supported.
$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, { # All <a> tag *in <div class="foo">* disappears 'div.foo' => { 'a' => undef, }, # same as above 'div.foo a' => undef, # xpath '.' = current node (itself) 'a#bar' => { '.' => 'foobar', './@href' => 'foo.html', }, # same as above 'a#bar' => 'foobar', 'a#bar/@href' => 'foo.html', });
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> <th></th> <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>
selector => \&foo
Code-ref: Callback subroutine. The callback receives
$_ => innerHTML $_[0] => XML::LibXML::Node object (X::L::Element, X::L::Attr, ...)
Its return value is handled per this list of value types (scalar to replace content, undef to delete, etc.).
$ts->process($template, { # samples 'h1' => sub { "bar" }, # <h1>foo</h1> => <h1>bar</h1> 'h1' => sub { undef }, # <h1>foo</h1> => disappears # sample: use $_ 'h1' => sub { uc }, # <h1>foo</h1> => <h1>FOO</h1> # sample: use $_[0] 'h1' => sub { my $node = shift; $node->nodeName; # <h1>foo</h1> => <h1>h1</h1> }, });
selector => [ $value, filter, filter, ... ]
Array-ref of Scalars: Value and filters. Filters may be
A) Callback subroutine (code reference)
B) Defined filter name
C) Object like Text::Pipe (it->can('filter'))
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> });
Some basic filters included. See Template::Semantic::Filter.
You can define your own filters using define_filter().
define_filter()
use Text::Markdown qw/markdown/; $ts->define_filter(markdown => sub { \ markdown($_) }) $ts->process($template, { 'div.content' => [ $text, 'markdown' ], });
Accessor to defined filter.
$ts->process($template, { 'div.entry' => ..., 'div.entry-more' => ..., })->process({ 'div.entry, div.entry-more' => $ts->call_filter('markdown'), });
Template::Semantic::Cookbook
Template::Semantic::Document
XML::LibXML, HTML::Selector::XPath
I got a lot of ideas from Template, Template::Refine, Web::Scraper. thanks!
Naoki Tomita <tomita@cpan.org>
Feedback, patches, POD English check are always welcome!
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
To install Template::Semantic, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Template::Semantic
CPAN shell
perl -MCPAN -e shell install Template::Semantic
For more information on module installation, please visit the detailed CPAN module installation guide.