Last week I was looking at what Dancer had to offer in term of cache plugins. I found Dancer::Plugin::Memcached which had a pretty nice interface, but was unfortunately using a backend that isn’t available on my server. So I thought: “how hard can it be to write a similar plugin that interfaces with CHI?”.
Not very hard, it turns out, and a few hours later Dancer::Plugin::Cache was born.
Writing a Plugin
All the tools you need to write a Dancer plugin are contained in he helper module Dancer::Plugin. To invoke them, you just need to ‘use’ Dancer::Plugin
within your module — all the inheritance stuff is taken care of behind the curtain:
package Dancer::Plugin::Cache; use strict; use warnings; use Dancer ':syntax'; use Dancer::Plugin;
With this simple invocation, we have now at our disposal the three functions that module provides: register
, plugin_setting
and register_plugin
.
register()
register()
is the most importance of the bunch, and defines keywords that are imported in the application’s namespace when the plugin is used. For example, for providing a ‘cache
‘ keyword returning the CHI
instance of the application, what we need to do is:
register cache => sub { state $cache = CHI->new(%{ plugin_setting() }); return $cache; };
Which is not high magic, mind you; we’re only providing a wrapper around the underlaying cache object.
The fun, however, doesn’t have to end there. Such keywords can be used to inject some new route behaviors to the application as well:
register check_page_cache => sub { before sub { halt cache()->get(request->{path_info}); }; };
In this case, calling ‘check_page_cache’ in the application will enable a check that, if available, will return the cached output of a route instead of executing it.
Also, since the calls to register
are done at runtime, we can also indulge in the kind of fun that Moose allows and programmatically create routes:
# create a bunch of helper functions for my $method ( qw/ set get clear compute / ) { register 'cache_'.$method => sub { return cache()->$method( @_ ); } }
plugin_setting()
If you noticed, I already used plugin_setting()
in the definition of the ‘cache
‘ keyword. It has a very simple function: it returns the configuration hash related to the current plugin. For Dancer::Plugin::Cache
, it’ll return whatever is contained within config->{plugins}{Cache}
, which in the YAML configuration file will be, e.g.:
plugins: Cache: driver: Memory global: 1
register_plugin()
This last function is the final amen of the plugin that tells Dancer to go forth and register it with the application. There are no knobs or settings that we have to be aware of. It just needs to be there at the end of the plugin module, and everything will be peachy.
register_plugin; 1; __END__
Ready to Rock
And that’s it, we are ready to use our plugin (see the source of Dancer::Plugin::Cache for the full working example):
package MyApp; use Dancer ':syntax'; use Dancer::Plugin::Cache; # caching pages' response check_page_cache; # this page will be automatically cached get '/cache_me' => sub { cache_page template 'foo'; }; # but not this one get '/uncached' => sub { template 'bar'; }; # using our helper functions get '/clear' => sub { cache_clear; }; put '/stash' => sub { cache_set secret_stash => request->body; }; get '/stash' => sub { return cache_get 'secret_stash'; }; # using the cache directly get '/something' => sub { my $thingy = cache->compute( 'thingy', sub { compute_thingy() } ); return template 'foo' => { thingy => $thingy }; };
A last, small detail that is good to know: the plugin must be ‘use’d in every module of the app where you want to access its keywords.
5 Comments. Leave new
Nice demonstration on how easy it is to create a Dancer plugin. However, I think it’d be better to call it Dancer::Plugin::Cache::CHI or Dancer::Plugin::CHI.
It makes sense to have CHI in the name, and to not “squat” the Dancer::Plugin::Cache name, which is very generic. What do you think ?
In this case, I think the generic naming makes sense, as CHI is aimed at being the generic way of interfacing with any cache system, the same way DBI is for databases. Buuut I’m admittedly biased and tainted by my secret agenda for world domination. ;-)
Seriously, though, I could easily be coaxed into migrating the module to Dancer::Plugin::Cache::CHI (which I would prefer over P::C::CHI, as the latter don’t carry the information that it’s a caching module for anyone not already acquainted with CHI). Do you know how the rest of the Dancer core team feels about it?
follow-up: Sukria also thinks D:P:C is too generic. I bow down to the wisdom of the Cha-cha Cabal (or the Mambo Meisters? Lambada Leaders?), and shall migrate the plugin.
Btw, mucho thanks for the feedback!
Thank *you* for understanding the namespace potential issues, and agreeing to change the name :) We try to communicate with plugin authors to leave freedom of inovation but keep homogeneous namespaces and equality.
Dancer::Plugin::Cache::CHI is a perfect name !
And there is no Cabal. (I think).
> Thank *you* for understanding the namespace potential issues, and agreeing to change the name :)
You’re very welcome. The request was sensible, and the tone as civil as can be. I would have been a certified lout to off-handily dismiss it. :-)
> And there is no Cabal. (I think).
Oh, I didn’t mean anything negative by that, but merely meant the Dancer devs core. Y’know, the guys at the head of Dancer’s beautiful conga line. :-)