La séptima vida

...o el gato así lo espera/teme

Structuring Kelp Web Applications

Kelp is a web application micro-framework for Perl which proposes a minimalistic approach. It is a fairly thin layer on top of Plack, adding request routing, configuration facilities, logging, templating, JSON encoding/decoding, testing, and a nice extensibility interface. Kelp will turn your application into a code reference per the PSGI specification, ready for delivery to any Plack-enabled server.

The most important thing Kelp provides is the ability to route HTTP requests to the routines that are expected to handle them. Its routing model defines the inheritance tree of the web application, which can be broken into different classes. This article discusses how to structure a Kelp application.

Application components

Among the different files that compose a Kelp web application, there are a PSGI file and one or more application classes. The job of the PSGI file is to instantiate the application. It will simply create a new application object and then call its run method:

use lib 'lib';
use MyApp;

my $app = MyApp->new();
$app->run;

This small script turns your application classes into a code reference suitable for Plack's consumption. Plack will then do its magic and actually run your application in whatever environment it is. For example, it will run as CGI, within Apache with mod_perl, or in one of the Perl web servers. Yes, that is what those four lines do.

Application classes (MyApp in this case) extend the Kelp module. In fact, both new and run are inherited from Kelp. Routes are added in the build method, which is executed just after the application object has been created. build should be overriden by your own main class.

Among other possibilities, the flexible routing model offered by Kelp includes using named methods in the main class and in other classes within the same namespace:

package MyApp;
use Kelp::Base 'Kelp';

sub build {
    my $self = shift;
    my $r    = $self->routes;
    $r->add( '/home',   'home'        );
    $r->add( '/first',  'first#test'  );
    $r->add( '/second', 'second#test' );
    $r->add( '/config', sub { $_[0]->config_hash } );
}

The example above shows methods local to the class (/home), a code reference (in this case, to display the configuration of the application), and routes that live in other classes (/first and /second). Because this class is called MyApp, requests for /first would be routed to &MyApp::First::test, a method in the class MyApp::First, and /second would go to &MyApp::Second::test.

This arrangement suggests that Kelp applications can be structured as follows:

Subclassing allows for simpler, more readable code

Clearly, application functionalities may be broken into different classes to keep the code clean, related, and short. Derived class names will live in the namespace of MyApp for security reasons. This model suggests that functions common to diverse routes may be stored in the main application module, while routes may be distributed into sub-classes. The PSGI script will instantiate the main class, and for this reason, it is the one that must implement the build method.

This model posed one problem. The method &MyApp::First::test, which resides in MyApp::First, would receive an object of class MyApp. Therefore, other code within MyApp::First would not be available to the application object even if MyApp::First inherited from MyApp. This is normal, since so far the two classes are unrelated, and it would be true even if MyApp::First inherited from MyApp because objects of base classes do not have access to code in their sub-classes.

This issue is solved by Kelp::Routes::Controller.

Kelp::Routes::Controller is a routing module that actually clones the application object and then re-blesses the clone into the class where the route lives. Following our example, the application object will be cloned and re-blessed from MyApp to MyApp::First before executing &MyApp::First::test. This way, it gains access to any code local to MyApp::First, but loses its MyApp identity. Because MyApp::First must inherit from Kelp for the application to work normally, it is advantageous for it to inherit from MyApp.

To use Kelp::Routes::Controller, add the following to your configuration in the modules_init section:

Routes => {
    base   => 'MyApp',
    router => 'Controller',
}

Conclusion

Kelp allows breaking applications into multiple classes. Its routing model and the use of Kelp::Routes::Controller let us split routes into different sub-classes of the main application class, which is a perfect place to store common functionalities in addition to routes.

By using sub-classes, we enhance readability, keep related routes close together, and routes that live in subclasses benefit from code which is private to them.

The main class must implement the build method to define all the routes of the application, and sub-classes must inherit from it. By using Kelp::Routes::Controller, the application object will both get access to any code local to the sub-classes and keep its web application heritage.

See Kelp, the routing section of its documentation and Kelp::Routes::Controller for more information.