Monday, June 21, 2010

Plugins for Catalyst

The current advice is, "don't do it":
19:40 <@hobbs> ijw: unless you have a major back-compatibility requirement, always write a role. A role for the app is equivalent to a plugin.
From the Catalyst manual, as it presently stands:
Plugins
The first thing to say about plugins is that if you're not sure if your module should be a plugin, it probably shouldn't. It once was common to add features to Catalyst by writing plugins that provide accessors to said functionality. As Catalyst grew more popular, it became obvious that this qualifies as bad practice.
There's other useful design advice there, well worth a read if you're thinking of this and you have a couple of minutes.  To summarise the plugin/role choice, if you're doing something that someone's going to use in a new application but is unlikely to be of use in an old one, you might as well make it a role: the plugin architecture's only real advantage is that it gives you backward compatibility with pre-Moose versions.

Models are confusing

And they annoy me. Here's an example.

Imagine I have a model that I made some time ago that has nothing to do with Catalyst. This is an ideal situation - best practice is that your models should always be independent of Catalyst - so let's start from here.

Now, the first thing I find I have to do is to create a little file to refer to my model in Catalyst.
package CatApp::Model::MyModel;
use Moose;
extends Catalyst::Model::Adaptor;
has class => ( default => 'External::MyModel' };
It's in a ::Model:: namespace, and it is-a Catalyst::Model. We often refer to it as a model. But it's not much of a model, since it contains no data. It's a shim; it exists to create the model (and to manage its lifecycle, be it per-process or per-request). Even the Catalyst method $c->model doesn't return a CatApp::Model::MyModel, it returns an External::MyModel. So we have the External::MyModel, our model-object-that-contains-data, and CatApp::Model::MyModel, the model-object-that-catalyst-needs-that-isn't-really-a-model-at-all...

Also, you'll notice, it's a verbose bit of configuration. It ties an external class to a Catalyst name (and a set of constructor arguments, pulled out of the config), but in order to do that I need to create a new file, 5 lines of boilerplate plus any special config I'd like to give it. Eww.

In the not-too-distant future, Bread::Board is coming to Catalyst. It should make it possible to take your data models and pull them into Catalyst, complete with construction and lifecycle, all in a single chunk of configuration. Better still, you'll be able to take that config and use it outside of Catalyst so that you can do sensible model layer integration tests. Can't wait.

In the meantime, remember when you post on the mailing list or the IRC channel that the word 'model' is a bit ambiguous.

Yet Another Perl blog

I'm ijw on perl.org's #catalyst, and I ask stupid questions.

When I design applications, and when I code, I don't like making mistakes.  Mistakes are annoying and they waste my time.

One way to avoid them is to listen to other people in the Catalyst world. Application design is a well trodden path; you can avoid making some of the big mistakes if you listen to the people who've walked the path before.

So, I ask questions.  They're stupid questions, because to the people who've done something tens of times before, obviously you do it like this and obviously you would never do that.  (And, of course, sometimes they're just plain stupid.)


So, here's a blog dedicated to exploring what works, and what doesn't work in Perl.  I'll break down what I've worked out, people will tell me why I've still got it wrong, but in the end, the obvious answer will be out there for Google to find for you.