Dec 27

In Mason 1 there are several ways to filter content: the <%filter> section, escape flags, and Component Calls with Content. Each of these has their own disparate implementation and API.

In Mason 2 we aim to unify these with a single implementation and a more consistent syntax.

A set of standard filters are automatically available in components, and other filter packages can be loaded via plugins.

Invoking filters (block syntax)

Here’s the typical way of invoking a filter:

<% $.Trim { %>
This string will be trimmed
</%>

Things to note here:

  • An { at the end of a <% %> tag denotes a filter call.
  • The </%> tag marks the end of the filtered content.
  • The expression $.Trim, aka $self->Trim, is a method call on the component object which returns a filter. In general everything before the brace is evaluated and is expected to return a filter or list of filters.
  • Filter names use CamelCase to distinguish themselves from other methods in the Mason::Component namespace. Another option was to use a standard prefix or suffix, e.g. trim_filter or filter_trim, but I thought this was the least ugly option and made filters look a little more like built-in tags.

Filters can take arguments:

<% $.Repeat(3) { %>
  There's no place like home.
</%>

  ==>  There's no place like home.
 There's no place like home.
 There's no place like home.

Again, the expression $.Repeat(3) returns a filter, meaning that it can
be curried:

% my $repeat_three = $.Repeat(3);
<% $repeat_three { %>
  There's no place like home.
</%>

A simple filter is just a subroutine that takes text as input and return the
new text. Thus you can create one-off filters with anonymous subroutines:

<% sub { reverse($_[0]) } { %>Hello</%>

     ==> olleH

Filters can be nested, with separate tags:

<% $.Trim { %>
  <% sub { uc($_[0]) } { %>
    This string will be trimmed and uppercased
  </%>
</%>

or within a single tag:

<% $.Trim, sub { uc($_[0]) } { %>
  This will be trimmed and uppercased
</%>

Multiple filters within the same tag are applied, intuitively, in reverse order with the last one being innermost. e.g. in this block

% my $i = 1;
<% $.Repeat(3), $.Cache($key, '1 hour') { %> <% $i++ %> </%>

  => 1 1 1

the output of <% $i++ %> is cached, and then repeated three times, whereas in this block

% my $i = 1;
<% $.Cache($key, '1 hour'), $.Repeat(3) { %> <% $i++ %> </%>

  => 1 2 3

<% $i++ %> is executed and output three times, and then the whole thing cached.

Invoking filters (pipe syntax)

Filters can also appear in a limited way inside a regular <% %> tag; this replaces Mason 1′s escape flags.

<% $content | NoBlankLines,Trim %>

The filter list contains one or more comma-separated names, which are treated as methods on the current component class. With this syntax you cannot use anonymous subroutines or variables as filters, or pass arguments to filters (but you can define local filter methods to get around this).

One common use of this form is to escape HTML strings:

<% $message_body | H %>

The H filter is provided along with other web-related filters in Mason::Plugin::HTMLFilters.

Applying filter to current component

Mason 1′s <%filter> section was good for filtering the content of the current component. e.g. to make it uppercase:

<%filter>
$_ = uc($_);
</%filter>

In Mason 2 the most succinct replacement is currently an around modifier:

<%around main>
<% sub { uc($_[0]) } { %>
% $self->$orig();
</%>
</%around>        

Creating filters

Here’s a filter package that implements two filters, Upper and Lower:

package MyApp::Filters;
use Method::Signatures::Simple;
use Moose::Role;
 
method Upper () {
    return sub { uc($_[0]) }
}
 
method Lower () {
    return sub { lc($_[0]) }
}
 
1;

To use these in a component:

<%class>
with 'MyApp::Filters';
</%class>

<% $.Upper { %>
...
</%>

Or if you want them available to all components, put them in Base.pm at the top of your component hierarchy, or in your application’s Mason::Component subclass.

Simple vs. dynamic filters

A simple filter is a code ref which takes a string and returns the output, like Upper and Lower above.

A dynamic filter (a Mason::DynamicFilter object) contains a code ref which takes a yield block and returns the output. A yield block is a zero-argument code ref that returns a content string.

The power of dynamic filters is that they can choose if and when to execute the yield block. For example, here’s the standard Cache filter in action:

<% $.Cache($key, '1 hour') { %>
... some computed content ...
</%>

This caches the inner content for one hour based on cache key $key. Here’s the implementation:

method Cache ( $key, $set_options ) {
    Mason::DynamicFilter->new(
        filter => sub {
            my $yield = shift;
            $self->cmeta->cache->compute( $key, $yield, $set_options );
        }
    );
}

Using CHI’s compute method, we check the cache first and return the output immediately if it is available. Only on a cache miss do we actually execute the (presumably expensive) yield block. This could not be implemented with a simple filter since the content would be computed every time.

3 Responses to “Mason 2: Filters”

  1. Rolf Says:

    Should
    Hello
    not be
    Hello
    ?

  2. Rolf Says:

    Ok, the Mason code dissappears when posting.
    I think in the example with the anon sub reverse a { ist missing :
    sub { reverse $_[0] } {

  3. Jonathan Swartz Says:

    You’re right – fixed. Thanks.

Leave a Reply

preload preload preload