La séptima vida

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

Extending Kelp web applications using Role::Tiny

By now it should be obvious that, well, I have a blog. Some of its articles talk about Perl, and so it seemed that building a blog engine in Perl would be nice for a Perl-related blog. The blog engine is written using Kelp, a web application framework. You can read about Kelp here.

You know that blogs normally have tag lists and fancy side bars. These are usually little modules or extensions that are loaded via configuration files, without modifying the engine itself. Kelp already proposes a way to extend its functionalities: Kelp::Module. By inheriting from Kelp::Module, you can build extensions that add methods to your web application class. A quick search over CPAN gives you several interesting modules such as Kelp::Module::Template::Toolkit or Kelp::Module::FlashMessage.

While the Kelp::Module namespace is great for extending Kelp itself, or for marrying Kelp with other technologies, little insets to this humble blog lack the generality and usefulness to be shared as Kelp modules. But it is still possible to use roles to bring in new functions to your appplication by using Role::Tiny.

Role::Tiny will let you compose new functionalities into a class or object. Because it does its job at run-time, role names can be pulled in from Kelp's configuration file and composed into your application class using Role::Tiny's apply_roles_to_package method. Methods will then be inherited to your sub-classes, which is great.

The code

Because Kelp keeps its configuration in a hash, you need to add a new key-value pair with the list of roles that will be pulled-in. In the case of this blog engine, the following line will bring the About inset and the tag list:

    blog_modules   => [qw/About TagCloud/]

These modules are implemented using Role::Tiny. Below, the TagCloud module is shown. For security reasons, the modules live in the Blog::Module namespace. The contents of the actual method are not relevant for this post, but they interact with the database using my own DBIx::Mint:

package Blog::Module::TagCloud;

use URI::Escape;
use Role::Tiny;
use strict;
use warnings;

# Builds the list of tags for the side bar
sub tag_cloud {
    my $self = shift;

    my $iter = Blog::Schema::Tag
        ->result_set
        ->order_by('tag')
        ->set_target_class('Blog::Schema::Tag')
        ->as_iterator;

    my @tags;
    while (my $obj = $iter->next) {
        push @tags, { name => $obj->tag, url => uri_escape_utf8($obj->tag)};
    }
    return \@tags;
}

1;

Finally, this all needs to be pulled in together in the main class of the blog engine. The following two lines take care of composing the roles listed in the configuration file into our application class:

    
    # Blog modules
    my @modules = map { "Blog::Module::$_" } @{$self->config('blog_modules')}; 
    Role::Tiny->apply_roles_to_package('Blog', @modules);
 

The call to apply_roles_to_package was done in the build method of the main application class so that it is pulled in as soon as the application is instantiated, and before any route is executed.

Now, the application class has the methods about and tag_cloud which are used to fill the side bar of this blog!

Conclusion

Pulling in functionality into your Kelp application can be done simply and in a clean way using role composition. Role::Tiny and its cousin Moo::Role allow you to do this in run time, which works nicely for bringing in the list of roles from the configuration file of your application.