Calling Catalyst functions outside Catalyst.

Several times, I’ve wished I could write command-line tools to manage Catalyst applications.  Those tools, invariably, needed to call some of the Catalyst functions, or at least get to the database.

I had struggled with Config::JFDI and tried to create DBIx::Class objects outside of the Catalyst models so they could be used standalone.  This sort of worked, but wasn’t as easy as I wanted.

I found a way, and it’s so simple it hurts.   I can’t help think this is one of those things the Catalyst team will read and think, “Well, duh!” but it was never clear to me and I struggled with it for months.

Make sure MyApp/lib is in @INC and use MyApp.  Catalyst is loaded, along with all the details of your application.  No Engine is running, so it isn’t a server.  You can then call methods listed for Catalyst, as MyApp uses it as a parent.

The key to this is that Catalyst::model and Catalyst::view can both be called as class functions or as methods.  You usually seem them as $c->model() and $c->view, called off a context object.  They can work as MyApp->model() and MyApp->view(), too.

So, your tool does:

use MyApp;

my $foo = MyApp->model(‘Model::Whatever’)->search({ name => ‘foo’ })->first();

… or whatever thing it needs to do.  The config is loaded with all the MYAPP_SUFFIX parsing as you would expect.

A problem I couldn’t for a year and a half has finally been solved.  YAY!

The biggest quirk is that if MYAPP_DEBUG is set, or the app has $c->debug on, the debug output will happen when the module loads.  For the tools I’m writing that isn’t a problem, but it could be for other applications.

Tags: ,

2 Responses to “Calling Catalyst functions outside Catalyst.”

  1. fREW Schmidt says:

    The problem with this solution is that it will be WAY slower than using Config::JFDI and DBIC. You’re loading a lot more than you really need for simple DB stuff.

  2. Laufeyjarson says:

    I can’t argue with this. It is a serious case of using a bulldozer when a shovel will do.

    The startup manages to display the entire debug and diagnostics of Catalyst, which looks particularly odd in the middle of the tool’s run.

    I think I can even agree that it is probably not a good general solution.

    I can live with this, because this tool is something only a developer will see.

    I can also live with it because trying to figure out how to extract the model names and connection information from Config::JFDI – which I have already used in this program! – and being able to find the real DBIC class and get it created without the model’s help was driving me crazy.

    The Catalyst app in question uses several models, from several sources, and Catalyst has already got the code internally to read the config, find the models, load them, figure out where they really are, and be able to create usable handles from then.

    Why should I work to duplicate all that code, in a likely fragile and hard to maintain way (actually, I gleefully threw out some horrid and sort of working code) when Catalyst already knows how to do all this for me?

    (Yes, this is me saying, “I already had a bulldozer, and didn’t want to drive across town and get a shovel.”)

    Your point makes me wonder if another module to use the data from $config to load all the right DBIC classes might be worthwhile. It would certainly be more appropriate for general use.

Leave a Reply