#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
--- #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 "