An alternative View Engine
Do you remember the good old times where CGI scripts ware scattered with heredocs and print statements containing snippets of HTML? Some time passed by since then and in the meantime every Framework offers some ways for generating HTML using a View layer.
When looking at the basic principles behind the various View implementations you can find various alternative approaches:
- pure HTML
-
Some view engines are using pure HTML files without any extra content. The HTML markup might contain placeholder text or simply is empty. Generating the HTML for delivery to the browser means gluing the predefined template files together after filling certain parts with meaningful content. Another option could be repeating or deleting certain blocks and filling in some content during the repetition process.
Working with engines like these typically is very much like using jQuery on the browser.
Examples for view engines of this kind are:
- mixed HTML and processing instructions
-
Most templating engines work like this. Some kind of extra language is available to allow conditions, repetitions or filling-in content.
Example for a view engine of this kind: Catalyst::View::TT, Catalyst::View::XSlate
- different syntax to HTML
-
Instead of typing in HTML syntax directly, some Engines offer a different syntax (or a DSL if you like) for generating HTML output. This is what we observe today.
Examples for view engines of this kind are: Catalyst::View::Template::Declare, Markapl, Catalyst::View::ByCode, Catalyst::View::Haml, Template::Caribou
- another language exporting to HTML
-
Finally one could use a completely different markup and generate HTML from it. Examples could be Text::Markdown, POD as well as any other type of widget library that might generate HTML from its content.
Today, we will look into Catalyst::View::ByCode.
What is Catalyst::View::ByCode?
Basically, Catalyst::View::ByCode offers a subroutine for every known HTML Tag. The subs are available inside every template. The only exceptions are tags whose names would otherwise collide with Perl's built-in subs (like "s", "tr") or would interfere with Moose (like "meta"). These subs have names different from the HTML-Tag. Typically all Template files reside an a directory named "bycode" inside the "root" directory. The template name is guessed by concatenating the Controller's namespace and the action name. All template files are pure perl and they are compiled during their first execution by wrapping the file contents with some extra things the template author does not like to write every time. Nothing you need to worry.
A templating block is added to a file with the "template" keyword followed by a code reference that contains the template content.
# a file inside 'root/bycode/...' template { # your markup goes here };
Additionally, Devel::Declare acts in the background and mangles the template code at compile time to allow a nicer syntax that is closer to HTML than regular Perl syntax would be.
div some_id.hidden(foo => 'bar') { 'some content' };
would generate this HTML:
<div id="some_id" class="hidden" foo="bar">some content</div>
The content-part of every HTML-Tag is a code reference whose last expression is added to the content of the Tag as HTML-escaped text.
If there is more text to write, two predefined globs may get used.
div { # unescaped text -- be careful print RAW '<?foo bar="42" ?>'; # some other tag strong { 'Santa is coming soon' }; # escaped text print OUT '{ foo => 42 }'; };
And there is an idea borrowed from Template::Declare: modifiable attributes with some magic subroutines usable inside a Tag's code-ref.
div { attr foo => 42; id "element_$i"; class '+visible' if $visible; ... };
Building your own Tag-like things
So far, we only used the standard HTML Tags for creating our markup. However, every website has common building blocks. Would be great if we could use a similar syntax for creating such blocks. This is what the "block" keyword is for.
# build a block block info_box { # read attributes from definition my $head = attr('head') // 'headline'; my $foo = attr('foo') // 'default_value'; div.some_class { h3 { $head }; div.content { # insert markup from callee block_content; }; }; }; # use this block: info_box(head => 'Some Title') { # box content goes here };
Construct your scaffolding
Every page callable via an URL must provide a structure in order to be valid HTML. Repeating such a structure every time must be avoided.
A specially named template file, "wrapper.pl" is called before rendering the template in question. This behavior can be changed when setting the "wrapper" stash variable to some other file name or undef for having no wrapping behavior at all. The special keyword "yield" executes the template requested and inserts the generated markup at this very position.
# file "wrapper.pl" html { head { ... }; body { # TODO: header # TODO: navigation yield; # TODO: side bars # TODO: footer }; };
Conclusion
There are many options for choosing a templating engine. There is no "best" engine available. Choose your engine based on the requirements of your workflow and the skills and tools available by the people involved. If you do HTML-coding by hand, an engine like Catalyst::View::ByCode may be interesting for you. The win is a guarantee to generate valid HTML code and properly escaped Text with high speed. On the other hand, if you get HTML Templates from designers, it would be a bad choice.
See also
Author
Wolfgang Kinkeldei <wolfgang [at] kinkeldei [dot] de>