http://qs321.pair.com?node_id=411968

I have only recently started learning to use mod_perl, but, so far, I like what I see. There is one thing I couldn't find, though. I wanted a simple application controller, which would handle some of the grunt work for me. I wanted to separate the logic and display into modules and templates. This didn't seem like a lot to ask, but I couldn't find what I was looking for on CPAN.

CGI::Application came close, and merlyn's newly released CGI::Prototype looks interesting (apparently this is a recently popular subject), but both are geared towards CGI. They can doubtlessly be made to work with mod_perl via Apache::Registry, but I would rather run my controller directly as a PerlHandler. I got [a bit too] excited when I saw Apache::MVC, but, alas, that is specifically for Maypole. Maypole looks very nice indeed, but it's not quite what I was looking for. I also tried searching CPAN for 'apache controller', and other similar permutations. I found many things that seemed specific to one framework or another, but nothing generic that had the attributes I wanted. Perhaps this was a failing -- on my part -- to search for the relevant terms.

So I thought, "what the heck," and rolled my own. I made it about as simple as I could possibly imagine, while still being useful, and began to use it for my personal website. It seems to work fairly well, but I was interested in some feedback. The main things I was wonder are:

Here is the documentation I have written:


SYNOPSIS

Apache::Controller is a generic controller for mod_perl web applications. It will map each incoming request to a certain action module, fill out a template, and send the resulting data to the client.

    package MyApplication::Controller;
    use lib "/my/application/root/modules";
    use Apache::Controller;
    my $controller = Apache::Controller->new(
        ROOT => "/my/application/root",
        action_modules => {
            _default => 'MyApplication::Default',
            manage   => 'MyApplication::Manage',
            view     => 'MyApplication::View',
        },
    );

    sub handler { $controller->handler(shift) }
    1;


METHODS

new
Creates a new Apache::Controller object. Required named parameters are ROOT and action_modules.

ROOT defines the root directory where this application resides.

action_modules is a hash that maps action names to modules. The keys are names of the actions, and the values are corresponding modules. The modules should each contain a subroutine called handler.

handler
Handles the request. Requires one argument, an Apache::Request object.

The first stage of request handling is URI parsing. The URI is split into an action name and arguments portion. The action name looks like a top-level directory in the URI, and the arguments consist of anything following the action name. For example, a request to http://www.example.com/view/page1/print would call the view action with arguments page1/print.

If there is no action name found in the URI, _default is used. This is analogous to index.html. Handling of arguments is currently left entirely to the action module, but may be standardized in the future.

Once the action name is parsed, the action_modules hash is consulted to find an action module. If one is not found, NOT_IMPLEMENTED is returned. If one is found, it is checked for a handler subroutine. If the handler is not found, NOT_IMPLEMENTED is returned.

The handler subroutine can return one of two things: a redirect URL, or a list of parameters. This determination is done by counting the number of elements returned. If there's only a single element returned, it is assumed to be a redirect URL. If there is an even number of elements returned, it is assumed to be a list of parameters. If there is an odd number of elements, or no elements at all, a SERVER_ERROR is returned.

The final stage in request handling is output. First, a template is looked for in the ROOT/templates directory. The template is assumed to have the same name as the action, with the .tmpl file extension. For example, an action called view would have a template file named ROOT/templates/view.tmpl.

If found, the template is loaded and passed the parameter list, then output to the client. If no template is found, a default page is populated, which lists each element in the parameter list. This is a debug feature which may be disabled in the future.


I have decided not to include the code in this post, because I'd rather not (yet) concentrate the Power of the Monks in that direction. Right now I'm more concerned with getting the documentation, name, and interface right, than with code cleanliness and correctness. I will nail down those bits if there's any further interest in this thing.