#title Shawn M Moore - Best Practical Template::Declare --- #title Template::Declare Pure Perl templating language --- HTML and XML ### we're not all web developers, but I'd bet most of you have personal websites ### which does mean writing HTML --- And that sucks. --- #title What's it look like? Count the angle brackets --- #mode perl template '/' => sub { html { body { h1 { "Hello, world!" } } } }; --- #mode perl template '/todo' => sub { my $task_collection = shift; if ($task_collection->count == 0) { show 'empty'; } else { dl { for my $task (@$task_collection) { dt { $task->summary } dd { $task->metadata } } } } }; --- #title Templates are code Code? In my templates? --- It's more likely than you think. ### vim ' --- Designers make CSS --- Programmers make HTML --- #title Template::Declare Ideology --- #title Ideology Work with Perl, not against it --- Templates run like code --- Templates ARE code --- Templates = subroutines --- Templates live in packages --- Templates = methods --- Refactoring --- #title Ideology - refactoring #mode perl template 'whats-for-dinner' => sub { h1 { "Choose wisely!" } div { class is "item"; h2 { "Chicken" } } div { class is "item"; h2 { "Salmon" } } }; --- #mode perl template 'whats-for-dinner' => sub { h1 { "Choose wisely!" }; for (qw/Chicken Salmon/) { dinner($_) } }; sub dinner { my $choice = shift; div { class is "item"; h2 { $choice } } } --- #title Template::Declare Neat tools for coding: --- #title Neat tools for code Module::Refresh ### update your template code without restarting your program --- Devel::Cover ### how well tested are your templates? --- Perl::Critic ### your templates are subject to best practices as well --- Carp::confess ### your templates will be in the stack trace --- Devel::Trace ### see each line as it's run, even inside your templates --- Acme::Bleach ### clean up those dirty templates --- #title Template::Declare Time savers --- #title Time savers String escaping --- #title Time savers - string escaping #mode perl template 'show_profile' => sub { div { attr { class => 'user-profile' }; $profile } }; my $profile = ''; show('show_profile', $profile); --- #title Time savers - string escaping #mode html
<script type="text/js"> malicious script </script>
--- #title Time savers Catch syntax errors at compile time --- #title Time savers - catch syntax errors #mode perl template 'why-does-this-page-look-funny' => sub { html { body { h1 { "I'm missing a right brace!" p { "Go on, look up!" } } } }; --- #title Template::Declare Features --- #title Features (or, "bigger time savers") --- Template inheritance --- #title Features - inheritance #mode perl package MySite::View::GenericItem; template 'list' => sub { div { for my $item (@_) { show('item', $item); } } }; template 'item' => sub { span { shift } }; --- #mode perl package MySite::View::Quote; use base 'MySite::View::GenericItem'; template 'item' => sub { blockquote { shift } }; # MySite::View::GenericItem->list will do the right thing! --- #title Features Mixins --- #title Features - mixins #mode perl package MySite::View; use MySite::View::Quote; alias MySite::View::Quote under 'quote/'; use MySite::View::User; alias MySite::View::User under 'user/'; use MySite::View::Admin; alias MySite::View::Admin under 'secret/admin/site/'; ### this is the preferred way to get other packages' templates ### if the mixed in templates use relative paths, then they can be mixed in ### anywhere --- #title Template::Declare More features --- #title More features Multiple roots --- #title More features - multiple roots #mode perl package Framework::View; # templates package Application::View; # templates package main; Template::Declare->init(roots => [qw/Framework::View Application::View/]); --- #title More features Private templates --- #title More features - private templates #mode perl private template 'footer' => sub { hr {} div { "Last generated at " . gmtime } }; template 'index' => sub { html { body { h1 { "My private template example!" }; show 'footer'; } } }; --- #mode perl my $uri = 'footer'; # ... Template::Declare->show($uri); --- "The template 'footer' could not be found (it might be private)" --- #title More features Instrumentation --- #title More features - instrumentation #mode perl Template::Declare->init(around_template => sub { my ($orig, $path) = @_; my $start = time; $orig->(); my $end = time; debug "$path took " . ($end - $start) . " seconds."; }); --- Stack traces --- SQL queries --- Memory leaks --- Jifty halos --- #title Template::Declare Pitfalls --- #title Pitfalls Semicolons --- #title Pitfalls - semicolons #mode perl template 'blab' => sub { h1 { "Words!" } for (@paragraphs) { p { $_ } } }; --- #title Pitfalls - semicolons #mode perl template 'blab' => sub { h1 { "Words!" }; for (@paragraphs) { p { $_ } } }; --- #title Pitfalls Strings --- #title Pitfalls - strings #mode perl template 'compliment' => sub { p { "You are "; em { "super!" } } }; --- #title Pitfalls - strings #mode perl template 'compliment' => sub { p { "You are ", em { "super!" } } }; --- #title Pitfalls - strings #mode perl template 'compliment' => sub { p { outs "You are "; em { "super!" } } }; --- #title Pitfalls Tables --- #title Pitfalls - tables #mode perl template 'stats' => sub { table { tr { th { @col_names } }; for my $cells (@rows) { tr { for (@$cells) { td { $_ } } } } } }; --- #title Pitfalls - tables #mode perl template 'stats' => sub { table { row { th { @col_names } }; for my $cells (@rows) { row { for (@$cells) { cell { $_ } } } } } }; --- #title Template::Declare Implementation --- #title Implementation Tags --- #title Implementation - tags #mode perl sub div (&) { my $code = shift; print "
" . $code->() . "
"; } div { "hello world" } --- #mode html
hello world
--- #mode perl div { "hello" } div { "world" } --- 'Too many arguments for main::div' --- #mode perl div( sub { "hello" }, div( sub { "world" } ) ); --- #mode perl sub div (&;$) { my ($code, $morecode) = @_; my $output = "
" . $code->() . "
" . ($morecode || ''); if (defined wantarray and not wantarray) { return $output; } else { print $output; } } --- #mode perl div { "hello" } div { "world" } --- #mode html
hello
world
--- Can we break this? --- #title Implementation - tags - can we break it? #mode perl div { div { "hello world" } } --- #mode html
hello world
Good. --- #mode perl div { "hello" } div { div { "world" } } --- #mode html
hello
world
Good. --- #mode perl div { div { $_ } for 1 .. 3; } --- #mode html
1
2
3
Bzzt! --- #title Implementation Inheritance --- #title Implementation - inheritance #mode perl package MySite::View; template 'search' => sub { h1 { "No results, mwa ha ha." } }; --- #mode perl package MySite::View; sub search { # ... h1 { "No results, mwa ha ha." } # ... } --- #mode perl package MySite::View; template 'search.html' => sub { h1 { "No results, mwa ha ha." } }; --- #mode perl package MySite::View; sub search.html { h1 { "No results, mwa ha ha." } } --- 'Illegal declaration of subroutine MySite::View::search' --- #mode perl package MySite::View; no strict 'refs'; *{"search.html"} = sub { h1 { "No results, mwa ha ha." } }; --- #mode perl use MySite::View; MySite::View->search.html --- 'Bareword "html" not allowed while "strict subs" in use' --- #mode perl use MySite::View; my $method = "search.html"; MySite::View->$method; --- #title Implementation Mixins --- #title Implementation - mixins #mode perl sub alias { my ($mixin, $prepend_path) = @_; my $alias_into = caller; my $alias_key = $mixin . " " . $prepend_path; push @{ Template::Declare->aliases->{$alias_into} }, $alias_key; my $aliases = $alias_into->alias_metadata; $aliases->{$alias_key} = { class => $mixin, path => $prepend_path, }; } --- #title Template::Declare TagSets --- #title TagSets Template::Declare::TagSet::HTML --- Template::Declare::TagSet::XUL --- Template::Declare::TagSet::RDF --- #mode perl package Template::Declare::TagSet::HTML::Circa::1997; use base 'Template::Declare::TagSet::HTML'; sub get_tag_list { my $self = shift; my @sane_tags = @{ $self->SUPER::get_tag_list(@_) }; return [@sane_tags, qw{marquee blink bgsound}]; } --- #title Template::Declare Other templating systems --- #title Comparisons CGI --- #title Comparisons - CGI Grand-daddy of them all --- #mode perl print dl( map { dt(escapeHTML($_)), dd(escapeHTML($data->{$_})) } keys %$data ); --- Expressions, but no statements --- #title Comparisons HTML::Mason --- #title Comparisons - HTML::Mason Mason is good! --- We use it --- (Dave Rolsky)++ --- #mode html <%args> $data => {}
% for my $key (keys %$data) {
<% $key %>
<% $data->{$key} %> % }
--- But it's still HTML --- #title Comparisons Template Toolkit --- #title Comparisons - Template Toolkit It ain't Perl! ---- I like Perl --- #mode html
[% FOREACH datum in data %]
[% datum.key %]
[% datum.value %]
[% END %]
--- #title Template::Declare Conclusion --- #title Conclusion STOP WRITING HTML --- #mode perl template 'display_hash' => sub { my $data = shift; dl { for my $key (keys %$data) { dt { $key } dd { $data->{$key} } } } };