Stuffing your stash with closures

The stash

Every request in Catalyst has a stash, which is scoped to that request. It's accessed with the $c->stash accessor inside Catalyst, and made available within whatever View you are using in your application.

For the purposes of this article, I'll concentrate on Template, i.e. TT, but this technique is widely applicable.

The usual pattern is to stash various data (simple scalars, objects, arrays, etc) during the course of your request, and you can then retrieve them (and for more complex data, like objects, call methods on them), once inside your template.

Example of trivial usage

    sub foo : Local {
        my ($self, $c) = @_;
        $c->stash( things => $c->model('DB::Things')->search({ bar => $c->request->param('bar') }) );
    }

    ....

    <ul>
    [% FOREACH thing IN things.all %]
        <li>[% thing.name %]</li>
    [% END %]
    </ul>

Stashing closures

So, you can stash simple scalars, arrays and hashes (and then iterate through them later), objects (which you can then call methods on later), but less obviously, you can stash code references, and then call that code later..

Trivial example

    sub foo : Local {
        my ($self, $c) = @_;
        $c->stash( thing => sub { return 'quux' } );
    }

    ....

    [% thing() %]
    prints:
    quux

So what's this useful for?

Well, less trivial cases, of course!!

    $c->stash( uri_for_secure => sub { my $uri = $c->uri_for(@_); $uri->scheme('https'); return $uri } );

    [% c.uri_for_secure(...your normal params here...) %]

Hey, that's useful, eh?

Danger Will Robinson!!!

THE EXAMPLE ABOVE IS UNSAFE. It causes circular references!

Closing over $c (i.e. making a sub reference that includes $c inside it), and then putting it into a data structure $c->stash which is directly connected to $c, causes a loop, meaning that perl won't ever de-allocate the memory.

If you do this once per request, then your application will quickly run out of memory!!

ContextClosure

    with 'Catalyst::Component::ContextClosure';

    sub foo : Local {
        my ($self, $c) = @_;
        $c->stash(
            uri_with_secure => $self->make_context_closure(sub {
                my ($c) = @_;
                my $uri = $c->uri_for(@_);
                $uri->scheme('https');
                return $uri;
            },
            $c)
        );
    }

Whilst this looks somewhat more baroqe than the previous version, it won't leak any memory, and is tremendously useful.

Sugar

The latest (0.36) version of Catalyst::View::TT has some syntax sugar to make adding closures much easier. You don't get the flexibility of being able to change them during the request cycle, but for most cases that isn't needed.

In your view configuration, you declare which methods on your view class will be exposed:

    expose_methods => [qw/uri_for_css/],

and you then implement the method (in your View class), without having to worry about circular references through the context object, E.g.

    sub uri_for_css {
        my ($self, $c, $filename) = @_;
        # Additional complexity here - this is a trivial example!
        return $c->uri_for('/static/css/' . $filename);
    }

Further thoughts and ideas.

Other great things about a stashed closures is that you can have entirely different implementations of them for different parts of your site (without needing to add a method on your MyApp object with horrible conditional logic in it).

If you're using Chained dispatch, then you can fill in a default closure at the base of your chain (for generic requests), and replace it with more specific versions later in the dispatch cycle for more specific / customised parts of your application.

As the closures are code refs, it is in fact entirely possible to further customise them by wrapping them inside another code ref which does some further work.

For example (building in the ContextClosure example):

    my $old = $c->stash->{uri_with_secure};
    $c->stash( uri_with_secure => sub { my $uri = $old->(@_); $uri->path('/sekrit' . $uri->path); return $uri });

    ....

    [% uri_with_secure('...') %] => forces https, adds /sekrit to the front of any paths generated by uri_for

Conclusion

I hope this short article has opened your eyes to the potential useages for closures to clean up repeated template logic fragments within your application, and given you some ideas for alternatative ways to abstract specific fragments of display logic which don't really belong in your application templates.

Author

Tomas (t0m) Doran <bobtfish@bobtfish.net>.