I hope I’m not too late in my third week of posting for the iron man contest. My last post was on May 31, and so I should have posted something by June 7, but I was traveling cross-country to my original hometown of Los Angeles! Talk about a car city. I think that’s a good enough excuse. We’ll see if I get busted back to paper man.
I wanted to extend on my last post about a way to make sure your multiple-table-inheritance database can be updated properly across whatever views you are running your app off of. But I just got some new code to look at for that, and haven’t quite grokked it yet. So that will have to wait till next time.
Meanwhile, I’d like to present a couple of ideas I have been working on in my Metalabel rewrite, and perhaps solicit some feedback. I’d love to hear suggestions or strategies. The first is a module some people have seen around—it is called Moosifier, and it takes DBIx::Class Schema::Loader result class files, reads them, and adds Moose bits at the top and bottom to allow you to use your auto-generated classes with Reaction or whatever.
Before:
package Test::Schema::Amazon;
use strict;
use warnings;
use base 'DBIx::Class';
__PACKAGE__->load_components("TimeStamp", "Core");
__PACKAGE__->table("amazon");
__PACKAGE__->add_columns(
"asin",
{
data_type => "text",
default_value => undef,
is_nullable => 1,
size => undef,
},
...
1;
After:
package Test::Schema::Amazon;
use Moose;
extends 'DBIx::Class';
has 'asin' => (
isa => SimpleStr,
is => 'rw',
required => 0
);
has 'locale' => (
isa => SimpleStr,
is => 'rw',
required => 0
);
has 'filename' => (
isa => SimpleStr,
is => 'rw',
required => 0
);
has 'refetchdate' => (
isa => Int,
is => 'rw',
required => 0
);
use namespace::autoclean;
__PACKAGE__->load_components("TimeStamp", "Core");
__PACKAGE__->table("amazon");
__PACKAGE__->add_columns(
"asin",
{
data_type => "text",
default_value => undef,
is_nullable => 1,
size => undef,
},
...
__PACKAGE__->meta->make_immutable(inline_constructor => '0');
1;
I wrote it because I thought I was going to be using Reaction in my rewrite, but phaylon advised against it. It adds a whole other layer to Catalyst’s M, V and C, and I agreed that it’s wisest to make sure the basic rewrite is solid before adding a whole new layer, and, for instance, having to think about chopping my as-yet-unwritten templates into widgets or what have you.
Thus Moosifier. It’s pretty darned simple: it works by reading the result class files and using a bunch of regexes to extract the Moose attributes from the __PACKAGE__->add_columns(...) lines. I was excited to put it up and finish it, but no sooner had I done so than I hit upon another problem. Now that I had my classes written, I had to make my controllers.
Metalabel 2.0 needs at least 76 controllers. There’s no way I’m going to write those by hand. I could use myapp/script/myapp_create.pl helper script to make skeleton controllers, of course. But that’s just a skeleton. What about basic CRUD? There’s no way I’m going to write CRUD for 76 controllers by hand.
I asked on #catalyst whether there are any tools to help generate CRUD automatically in your controllers, and glob recommended Catalyst::Controller::DBIC::API, for which there are RPC and REST implementations. Of course, these just provide a way to write your CRUD. They don’t do it for you. So I had a new automation problem: “How can I automate the creation of basic but smart RPC or REST controllers for my Catalyst app, based on my DBIx::Class::Schema::Loader result classes?”
Of course I asked on irc first. Knowing that I had done something similar with Moosifier, mst suggested that instead of reading the files from disk, I take the table info directly from the database using DBIx::Class::Schema::Loader. Why the hell not? It makes much more sense than stepping through a file with regexes and substitutions and such.
The goal is to create 76+ controllers (in this case, of the RPC subclass) like this sample code from the Catalyst::Controller::DBIC::API documentation:
package MyApp::Controller::API::RPC::Artist;
use base qw/Catalyst::Controller::DBIC::API::RPC/;
__PACKAGE__->config ( action => { setup => { PathPart => 'artist', Chained => '/api/rpc/rpc_base' } },
class => 'MyAppDB::Artist', # DBIC schema class
create_requires => ['name', 'age'], # columns required to create
create_allows => ['nickname'], # additional non-required columns that create allows
update_allows => ['name', 'age', 'nickname'], # columns that update allows
list_returns => [qw/name age/], # columns that list returns
list_prefetch => ['cds'], # relationships that are prefetched when no prefetch param is passed
list_prefetch_allows => [ # every possible prefetch param allowed
'cds',
qw/ cds /,
{ cds => 'tracks' },
{ cds => [qw/ tracks /] }
],
list_ordered_by => [qw/age/], # order of generated list
list_search_exposes => [qw/age nickname/, { cds => [qw/title year/] }],
);
I figure I can dump out such a controller for every table I need by pulling the whole database into a hash using DBIx::Class::Schema::Loader, and using this hash to populate the __PACKAGE__->config(...) for each table/controller. For instance, the class name is easy. So is create_requires: those are the non-nullable columns in the table, besides any auto-incrementing serials or sequences. create_allows is every column but those in create_required, also excluding serials and sequences. update_allows is the columns in both create_allows and create_requires. I can’t escape some manual labor here: I am going to make list_returns return every column in the table. I’ll go through the controllers by hand to customize those. The same with list_prefetch_allows: the object of every relationship identified by DBIx::Class::Schema::Loader is going into list_prefetch_allows (none are going into the default list_prefetch). I’ll have to customize those manually, as well. list_ordered_by may need to be manually specified later as well, but using date column if it exists might be a good default. list_search_exposes will expose everything by default.
So, I am in the process of writing 2 modules to do this: one for RPC and one for REST. They’ll be on my github soon.
Meanwhile, using DBIx::Class::Schema::Loader to read table definitions directly from the database in these modules has made me realize I need to change Moosifier to do the same. In fact, D::C::S::Loader simply needs to be able to write the Moose declarations itself. Moosifier needs to move into Loader.
That’s my next project.
EDIT:
There’s a branch for it now. I need some tests though.
- BROWSE / IN TIMELINE
- « A Meager Attempt at CatchUp
- » Strange Interviewing Experiences
- BROWSE / IN object-oriented programming object-relation mapping perl
- « A Meager Attempt at CatchUp
- » Strange Interviewing Experiences
SPEAK / ADD YOUR COMMENT
Comments are moderated.

Recent Comments