Mason - A Template system for us Perl programmers
-
Upload
jerome-eteve -
Category
Technology
-
view
1.104 -
download
1
description
Transcript of Mason - A Template system for us Perl programmers
A bit of myth busting
● Mason is old● Mason is a viewcontroller● Mason is too complex● Mason is slow● Mason uses embedded Perl
Mason is old
Mason 1 is old (HTML::Mason)
Mason 2 released in 02/2011
Complete Moosified rewrite
Mason is a viewtroller
Kind of true for Mason 1
But that was 10 years ago
Mason 2 is a pure templating system
The controller bit has been moved to Poet
Mason is too complex
AugmentationInheritanceCompositionMethod modifiersDynamic filters
Mason is too complex
AugmentationInheritanceCompositionMethod modifiersDynamic filters
But wait..
We are programmersSo it should be just fine
Mason is slow
Hum, well...
Compared to TT (as complex as TT can take benchmark)
Rate Mason TTMason 909/s -- -53%TT 1923/s 112% --
Mason is slow - but
Power comes with a price tag
Experience shows it scales very well with complexity
Mason is slow - but
Power comes with a price tag
Experience shows it scales very well with complexity
The benchmark I wrote is not bigfat.Have a look:
https://bitbucket.org/jeteve/mason-pres/
Mason is slow - but honestly
Do we use Perl because it's faster than X?
Mason uses embedded Perl
Prefer PHP?
Mason uses embedded Perl
Or worse, a mini-language ?
Mason uses embedded Perl
Perl is just fine no?
use <Anything you like>;
The basics - Embedded code
% while( my $product = $products->next() ){ <p>Buy our great <% $product->name() | H %>. It's only <% $product->price() | H %> </p>% }
With DefaultFilter set to H:<% $product->name() %>
And now, some cool Mason stuff
Header
Main content
Footer
Augmentation - A typical page
Augmentation - Non Mason-ish
/index.mc<& /comp/header.mi &>Welcome to index!<& /comp/footer.mi &>
Augmentation - The Mason Base.mc
/Base.mc<%augment wrap> <html><head><title>Site</title></head> <body> <% inner() %> <body></html></%augment>
Augmentation - a page
/index.mc<h1>Welcome</h1>
Augmentation - a page
Evaluates to <html><head><title>Site</title></head> <body> <h1>Welcome</h1> <body></html>
Augmentation - another page?
/products.mc<ul> <li>Lathe</li> <li>Piano</li></ul>
Augmentation - another page?
Evaluates to ..
Well you get the idea
Augmentation - Become specific
/products/Base.mc<%augment wrap> <div class="product"> <% inner() %> </div></%product>
Augmentation - Become specific
/products/lathe.mc<h1>Lathe</h1>
Augmentation - Become specific
Evaluates to<html> <head><title>Site</title></head> <body> <div class="product"> <h1>Lathe</h1> </div> <body></html>
Augmentation - Wanna go bare?
/products/baremetal.mc<%flags> extends => undef</%flags><h1>Some bare content</h1>
Evaluates to<h1>Some bare content</h1>
A typical layout
/index
Header
Menu
Content
Footer
A typical layout
/products/lathe.htmlHeader
Menu
Share BoxProduct description
Footer
A typical layout
/errors/404.htmlHeader
Menu
Error description
Footer
Augmentation - Layout control
Typical hierarchy/layout/Base.mc/layout/withmenu.mc/Base.mc/index.mc/products/Base.mc/products/lathe.mc/errors/Base.mc/errors/404.mc
Augmentation - Layout control
/layout/Base.mc<%flags> extends => undef</%flags><%augment wrap><html> <head><title>My site</title></head> <body><% inner() %></body></html></%augment>
Augmentation - Layout control
/layout/withmenu.mc<%flags> extends => undef</%flags><%augment wrap> <div class="menu">...</div> <% inner() %></%augment>
Augmentation - Layout control
/Base.mc<%flags> extends => '/layout/withmenu.mc'</%flags>
Augmentation - Layout control
/index.mc<h1>This is index</h1>
Augmentation - Layout control
/products/Base.mc<%augment wrap> <div class="share">...</div> <div class="product"> <% inner() %> </div></%augment>
Augmentation - Layout control
/products/lathe.mc<h1>This is a lathe</h1>
Augmentation - Layout control
/errors/Base.mc<%flags> extends => '/layout/Base.mc'</%flags><%augment wrap><div class="error"><% inner() %></div></%augment>
Augmentation - Layout control
/errors/404.mc<h1>Sorry, nothing here</h1>
Actually, I want the menu in the error pages
No problem:/errors/Base.mc<%flags> extends => '/layout/Base.mc'</%flags><%augment wrap><div class="error"><% inner() %></div></%augment>
And now, inheritanceAnd method modifiers
Let's speak about page titles
Remember /layout/Base.mc ?<%flags> inherit => undef</%flags><%method title>Site</%method><%augment wrap> ... <title><% $.title() %></title> ...</%augment>
Now the index title
<%method title>Welcome to Site!</%method><h1>This is index</h1>
The lathe.mc
<%after title> - Lathe</%method><h1>This is a Lathe</h1>
Renders as:<title>Site - Lathe</title>
a_product.mc
<%class> has 'product' => ( isa => 'My::Product', required => 1 );</%class><%after title> - <% $.product->title() %></%after><h1>This is a <% $.product->title() %></h1>
a_product.mc
In Catalyst
$c->stash()->{product} = $product;
a_product.mc instance is built with what's on the stash
a_product.mc
Trivial unit testing
my $mason = Mason->new();
my $p_page = $mason->run('/products/a_product', product => $p )->output;
ok_contains($p_page , $p->name() );
CompositionAnd a bit more
Classic composition
/comp/share.mi<%class> has 'stuff' => ( does => 'Sharable' );</%class><div class="share">Share <% $.stuff->share_name() %> on blabla</div>
Composition - New /products/Base.mc
/products/Base.mc<%class> has 'product' => ( isa => 'Product' );<%augment wrap> <& /comp/share.mi , stuff => $.product &> <div class="product"> <% inner() %> </div></%augment>
Internal components are components
So inheritance works
/comp/share/advanced.mi<%flags> extends => '/comp/share.mi'</%flags>Advanced share <% $.stuff->share_name() %>
Internal components - Unit testing
my $m = $mason->_make_request();my $m_text = $m->scomp('/comp/share.mi', p => 'Banana' );
ok_contains($m_text, "Share Banana");
FiltersMore powerful than it sounds
A Simple filter
% $.NoBlankLines {{Hello
World% }}Renders:HelloWorld
With argument(s)
% my $i = 1;% $.Repeat(3) {{<% $i++ %> \% }}
Renders:1 2 3
With parametric content
% my $nums = [ 1 , 2 , 3 ];% $.Loop($nums) {{% my ( $num ) = @_;<% $num %>, \% }}
Renders:1, 2, 3,
Filters - Make your own
package My::Mason::Filters;use Mason::PluginRole;method Iterate($on){ Mason::DynamicFilter->new( filter => sub{ my ($yield) = @_;my $txt = ''; while( my $next = $on->next() ){ $txt .= $yield->($next); } return $txt; });}
Filters - Make your own
/Base.mc<%class> with My::Mason::Filter;</%class>/anywhere% $.Iterate($resultset){{% my ($result) = @_; This is result <% $result->name() %>!%}}
Filters - As component
/comp/pager.mi<%class> has 'on' => ( isa => 'DBIx::Class::Resulset' ); has 'page' => ( isa => 'Int' , default => 1 ); has 'yield';</%class>% my $rs=$.on->search({} , {page => $.page});<div class="pager">.. Do stuff with $rs->page() ..</div>
Filters - As component
/comp/pager.mi .. Continued
%# Some paging was output% $.Iterate($rs){{% my $stuff = $_[0]; <% $.yield->($stuff) %>% }}%# Some other paging maybe?
Let's use our pager
/products/index.mc<$class> has 'products' => ( isa => 'DBIx::Resultset' );</$class>% $.CompCall('/comp/page.mi',on=>$.products){{% my ( $product ) = @_; Product <% product->name() %>% }}
Filters can be curried<%class> has 'lang'; has 'Translate' ( lazy_build => 1 ); sub _build_Translate{ my ($self) = @_; $self->TranslateIn($self->lang));}</%class>% $.Translate(){{Some Text% }}
Filters - One more nice one% $.Cache($key , '10 minute') {{ <div class="sluggish"> .... </div>% }}
Combine with currying for cleaner code :)
ConclusionsIn case you're not convinced yet
Mason helps writing clean code
Strict by default
Enforce type consistency a-la Moose
Enforce stash content (With Catalyst)
Minimal global variables
Easy unit testing
Mason is powerful
Code the view like a programmer with inheritance, augmentation, method modifiers, dynamic filters, currying ..
No change of mindset required
Changes are simple and consistent
Writing a new pages is trivial
Mason is extensible
Base class injection.
my $mason = Mason->new(base_*_class => 'MyApp::Mason::*Subclass' );
Or Role based Plugins (and Plugins Bundles)
For instance: Mason::Plugin::Cache
Reference
In order of preference● MasonHQ: http://www.masonhq.com/● Cpan: Mason● Cpan: Catalyst::View::Mason2● Mailing list● Jerome :P
Give it a go
$ sudo apt-get install libmason-perl$ echo "Hello world" > mason/index.mc$ mason.pl mason/index.mc
$ ## In catalyst$ sudo cpan -i Catalyst::View::Mason2
Thanks for your watching!
Questions?