fREWdiculous!
13 Aug
I’m probably preaching to the choir here, but it must be said: code reuse is most excellent!
Today I got a somewhat complex feature working for our customer, and almost all of it was features I’d already written, and due to the organization of our system I could easily reuse most of the code.
Our customer fixes airplane parts. When they fix a part they need to document every single thing they did to the part. We have each operation (more or less) that can be done already defined so that they can at least save those keystrokes (they are actually operation templates.) But there are a lot of operations and a typical work order will be around 50 operations, so choosing the same operations over and over is a waste of time. So we have a feature that lets them look at other work orders that were fixing the same type of part and copy operations (and materials) from that.
It was really easy to use the existing view for operations and materials because the front end is entirely comprised of JS classes. I even used an instance of the work scope grid to list all of the work scopes that are for the given part. The nice thing was that so far I’ve written no new server side code yet. And for the classes I didn’t use inheritance; I used a role style object modification by doing what Moose people would see as an after method on new (called a plugin in ExtJS). With the plugins I could simply change the store to ask for a given part-type’s work orders, hide extra columns, and add listeners to update the operations and materials when a user clicked a row.
Don’t think this is all just JS praise; Perl and Catalyst were help too. But really the benefit here was the use of any web framework in general. Because I’m using a framework I can easily find server side actions that do what I need (which the grids were already tied to in their base classes, but still.) In our other projects I’d be hard pressed to give you a list of all of our “actions,” whereas with CGIApp I can easily make a list of runmodes myself, and with Catalyst the server will make a list for me.
Excellent!
17 Jul
So this week, as previously alluded to, I convinced my boss to let me switch my current app from CGI::Application to Catalyst. I had gotten the book in the mail and I showed it to him to make the point that it’s a serious framework. Fortunately the switch has been mostly painless. The first reason being that our controller is pretty bare right now aside from validation, which took about a day to get entirely ironed out.
The interesting thing for me, most of all, is that I have gotten pretty good at writing regular expressions with vim to search and replace for CGIApp-isms to replace with Cat-isms.
Here are a list of some of the big ones:
Simple replacements:
1 2 | :%s/return/$c->stash->{json} = :%s/$self->query->Vars/$c->request->params |
More complex stuff
1 2 3 | :%s/$self->query->param(\(.\{-}\) = $c->request->params->{\1} :%s/method (.\{-}) : Runmode/method \1($c) : Local :%s/$self->schema->resultset(\s*'\(.\{-}\)'\s*) = $c->model('DB::\1') |
If you know anything about regular expressions you know that the \1 means the first back reference. Now, vim’s regex flavor is a little strange because it is optimized for searching for plain text, so *most* characters default to literals. That’s why I have to escape the parentheses to make a matching group. Also note the following unusual construct: .\{-} . That’s the same as .*? in Perl. That’s actually surprisingly important.
Anyway, this switch has been fairly fun and exciting. The best part being the inimitable structure of a Catalyst application. For example, the fact that we have a dev server with lots of affordances for (duh) developers other than little setup is great, and built in config file reading is something that I have always wanted. We always ended up rolling our own solution in other projects, but this is really supreme since it’s in one place and not just Perl code.
An there are lots of pleasant things like how it’s really easy for our app to have both JSON and TT support. This will be really good later on when we start to do pdf printouts and whatnot. Instead of adding methods for those things into the controller, like in CGIApp, we will just add another View module.
The main thing that has weirded my out so far is that in CGIApp the App is the controller. In Catalyst you have an App, which also seems to be an instance variable, with accessors for CGI parameters and whatnot, and you also have Controllers. Anyway, I need to wrap my head around all that. Hopefully reading through the book will help with some of these issues.
How about you? Are you still happy with CGIApp? Are you adventurous enough to use Reaction?
11 Jun
You may remember my post from before asking about the differences between these two frameworks. I only got a couple of responses, but they certainly helped me to see what is up.
Basically it boils down to this (as pointed out by mst): CGI::Application is a microframework, and Catalyst is an extremely configurable MVC stack. Before you correct me, Catalyst doesn’t actually provide the Model or View code; it lets you pick whatever you want to pull that off. But nonetheless it has affordances for both model and view code.
CGI::Application, on the other hand, doesn’t even have a built in way to deal with models! I love CGI::Application, but mostly because it keeps our code way more organized than our previous framework; (note, our previous framework were files that had use CGI; somewhere at the top…)
So you could really look at it like this: Catalyst is extremely extensible, because of their brilliant design. Maybe large frameworks have to be designed the way Catalyst is; I don’t know. I do know that at this point in my career my coding chops are not good enough to have that good of a design/API.
CGI::Application, on the other hand, is simple enough to grasp in an hour or so. It has:
And that’s it! There are plugins that give you extra features, like RESTful dispatching, authorization, and authentication, but out of the box it’s just a microframework.
Catalyst, on the other hand, is much more complex. For the ruby people out there it’s probably a mix between Rails and merb. Not quite Rails because it’s much less opinionated, but not quite merb because it has quite a few features that I don’t think merb has.
Recently I have been feeling some of the growing pains of the app that we recently started from scratch at work. It’s based on CGI::Application. The reason behind that was that my boss was hesitant to try something new (to us) like Catalist. I had used CGI::Application in TOME and so I had at least a little experience with it, although in TOME we didn’t even go close to what we could have done.
Anyway, if you are starting a new project that will be large (for almost any value of large) you probably want Catalyst. If you are making something simple (like WebCritic,) using Catalyst is totally overkill, and CGI::App fits the bill nicely.
5 Jun
So I’d like to do a post on CGIApp and Catalyst. People on IRC keep telling me that using CGIApp is wrong (mostly because they’ve never used it) and that I should switch to Catalyst.
Catalyst may be great, but I haven’t seen any solid posts about how Catalyst is great. So help me out. Ignoring the fact that Catalyst is what everyone uses (so there are lots of plugins for it) what makes it so good?
13 Feb
Today I was talking with a friend about the stuff we are doing at work and I mentioned to him how I was planning on doing the authorization. Since I had only thought about it at that point I didn’t even know if my idea was valid Perl syntax, let alone a feasible idea. But enough with the backstory, how about some real information.
Let’s assume that we have a webpage that lets you read user data and write user data. Theoretically we have already logged the user in, so we know who they are talking to, and we have a simple database model of the roles the user is authorized for. That’s no fun but it’s pretty easy. Three tables: one for the user, one join table from user to roles, and one that lists roles. So if we wanted to limit a sub to a user with role ‘read_user’ and ‘write_user’ we could do this:
1 2 3 4 5 6 7 8 | sub read_user { my $self = shift; if ($self->user->has_role('read_user') and $self->user->has_role('read_user')) { # display user data somehow } else { # display some form of error } } |
That’s really a fine way to do it. It works. It’s how most things work. But it would be no fun to have to write that for essentially every page on a site. That’s a drag!
Perl has this thing call attributes; which is basically a way to tag functions. At first I thought, “Hey, we’ll just tag a function with it’s roles and have validation work based on that. So our previous thing would look like this:
1 2 3 4 |
That would be great! But how on earth would you do something like that? I started off looking at the source to AutoRunmode which basically gave me this idea in the first place. There is some very deep magic in there, so I decided to keep looking. The source to AutoRunmode references Attribute::Handlers, originally by Damian Conway (author of numerous Perl
books
) which allows me to at least do something when someone includes a handler. That’s a start. So I looked at how to use Attribute::Handlers and after seeing what was possible I decided it would be easier and more clear to change my goal to this:
1 2 3 4 | sub read_user : Authorize(qw/role_read_user role_write_user/) { my $self = shift; # display user data somehow } |
That way if I use the Attribute::Handlers system I write a single function that gets a list of roles (amongst other things.) Then came the hard part. How can you change a function in Perl? Well, it turns out that changing a method is Kinda Hard, but Dave Rolsky Stevan Little made Moose, which makes it totally easy!
So this is the final mockup of how I plan on doing it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | package MyClass; use feature ':5.10'; use Attribute::Handlers; use Moose; sub Authorize : ATTR(CODE) { my ($class, $globref, $referent, $attr, $data, $phase, $filename, $linenum) = @_; # deep magic that gets the name of the function my ($function) = ${$globref} =~ /::([^:]+)$/; $class->meta->add_before_method_modifier ($function => sub { foreach (@{$data}) { $class->validate($_); } }); return; } sub read_user : Authorize(qw/user_read user_write/) { my $self = shift; say "reading personal files!"; } sub validate { my $self = shift; my $role = shift; say "validating $role for ".$self->user; } sub user { my $self = shift; return 'frew'; } |
Debolaz from #perl helped out a lot with this one. The only major thing left is some way to check all functions with the attribute ‘Runmode’ and ensure that they also have the Authorize attribute with at least one thing in there. That way we can’t accidentally forget to authorize people. I don’t think that will be very hard, but even if we can’t do it, this is still great.
The only thing I am worried about is whether I can use Moose in a CGI::App class. Probably, but we’ll see.
Hurray for Perl!
14 Jan
So I haven’t totally figured everything out about CGI::Application::Dispatch, but I am learning a lot. First off, here are two things that I learned today.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package ACD::Dispatch; use base 'CGI::Application::Dispatch'; use warnings; sub dispatch_args { return { prefix => 'ACD', debug => 0, table => [ '/' => { app => 'Welcome', rm => 'index' }, # The rm must be optional if you want # /controller to go to the startrunmode. ':app/:rm?/:foo?' => { }, ], args_to_new => { PARAMS => { cfg_file => '/path/to/config.pl', } }, }; } 1; |
Now, notice the :foo param. If you want to get access to that in your controller you use
1 | $self->param('foo') |
but if you had a regular parameter as well and wanted to get access to that, you’d use
1 | $self->query->param('bar') |