NAME

Kelp::Less - Quick prototyping with Kelp

DESCRIPTION

Please refer to the manual at Kelp::Manual::Less

SYNOPSIS

First ...

    # lib/MyApp.pm
    package MyApp;
    use parent 'Kelp';

    sub build {
        my $self = shift;
        my $r = $self->routes;
        $r->add( "/hello", sub { "Hello, world!" } );
        $r->add( '/hello/:name', 'greet' );
    }

    sub greet {
        my ( $self, $name ) = @_;
        "Hello, $name!";
    }

    1;

Then ...

    # app.psgi
    use MyApp;
    my $app = MyApp->new;
    $app->run;

Finally ...

    > plackup app.psgi

Or, for quick prototyping use Kelp::Less:

    # app.psgi
    use Kelp::Less;

    get '/hello/?name' => sub {
        my ( $self, $name ) = @_;
        "Hello " . $name // 'world';
    };

    run;

DESCRIPTION

If you're going to be deploying a Perl based web application, chances are that you will be using Plack. Plack has almost all necessary tools to create and maintain a healthy web app. Tons of middleware is written for it, and there are several very well tested high performance preforking servers, such as Starman.

Plack, however, is not a web framework, hence its creators have intentionally omitted adding certain components. This is where Kelp gets to shine. It provides a layer on top of Plack and puts everything together into a complete web framework.

Kelp provides:

WHY KELP?

What makes Kelp different from the other Perl micro web frameworks? There are a number of fine web frameworks on CPAN, and most of them provide a complete platform for web app building. Most of them, however, bring their deployment code, and aim to write their own processing mechanisms. Kelp, on the other hand, is heavily Plack-centric. It uses Plack as its foundation layer, and it builds the web framework on top of it. Kelp::Request is an extension of Plack::Request, Kelp::Response is an extension of Plack::Response.

This approach of extending current CPAN code puts familiar and well tested tools in the hands of the application developer, while keeping familiar syntax and work flow.

Kelp is a team player and it uses several popular, trusted CPAN modules for its internals. At the same time it doesn't include modules that it doesn't need, just because they are considered trendy. It does its best to keep a lean profile and a small footprint, and it's completely object manager agnostic.

CREATING A NEW WEB APP

Using the Kelp script

The easiest way to create the directory structure and a general application skeleton is by using the Kelp script, which comes with this package.

    > Kelp MyApp

This will create lib/MyApp.pm, app.psgi and some other files (explained below).

To create a Kelp::Less app, use:

    > Kelp --less MyApp

Get help by typing:

    > Kelp --help

Directory structure

Before you begin writing the internals of your app, you need to create the directory structure either by hand, or by using the above described Kelp utility script.

     .
     |--/lib
     |   |--MyApp.pm
     |   |--/MyApp
     |
     |--/conf
     |   |--config.pl
     |   |--config_test.pl
     |   |--config_deployment.pl
     |
     |--/view
     |--/log
     |--/t
     |--app.psgi
/lib

The lib folder contains your application modules and any local modules that you want your app to use.

/conf

The conf folder is where Kelp will look for configuration files. You need one main file, named config.pl. You can also add other files that define different running environments, if you name them config_environment.pl. Replace environment with the actual name of the environment. To change the running environment, you can specify the app mode, or you can set the KELP_ENV environment variable.

    my $app = MyApp->new( mode => 'development' );

or

    > PLACK_ENV=development plackup app.psgi
/view

This is where the Template module will look for template files.

/log

This is where the Logger module will create error.log, debug.log and any other log files that were defined in the configuration.

/t

The t folder is traditionally used to hold test files. It is up to you to use it or not, although we strongly recommend that you write some automated test units for your web app.

app.psgi

This is the PSGI file, of the app, which you will deploy. In it's most basic form it should look like this:

    use lib '../lib';
    use MyApp;

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

The application classes

Your application's classes should be put in the lib/ folder. The main class, in our example MyApp.pm, initializes any modules and variables that your app will use. Here is an example that uses Moose to create lazy attributes and initialize a database connection:

    package MyApp;
    use Moose;

    has dbh => (
        is      => 'ro',
        isa     => 'DBI',
        lazy    => 1,
        default => sub {
            my $self   = shift;
            my @config = @{ $self->config('dbi') };
            return DBI->connect(@config);
        }
    );

    sub build {
        my $self = shift;
        $self->routes->add("/read/:id", "read");
    }

    sub read {
        my ( $self, $id ) = @_;
        $self->dbh->selectrow_array(q[
            SELECT * FROM problems
            WHERE id = ?
        ], $id);
    }

    1;

What is happening here?

A note about object managers: The above example uses Moose. It is entirely up to you to use Moose, another object manager, or no object manager at all. The above example will be just as successful if you used our own little Kelp::Base:

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

    attr dbi => sub {
        ...
    };

    1;

Routing

Kelp uses a powerful and very flexible router. Traditionally, it is also light and consists of less than 300 lines of loose code (commends included). You are encouraged to read Kelp::Routes, but here are some key points. All examples are assumed to be inside the "build" method and $r is equal to $self->routes:

Destinations

You can direct HTTP paths to subroutines in your classes or, you can use inline code.

    $r->add( "/home", "home" );  # goes to sub home
    $r->add( "/legal", "legal#view" ); # goes to MyApp::Legal::view
    $r->add( "/about", sub { "Content for about" }); # inline

Restrict HTTP methods

Make a route only catch a specific HTTP method:

    $r->add( [ POST => '/update' ], "update_user" );

Named captures

Using regular expressions is so Perl. Sometimes, however, it gets a little overwhelming. Use named paths if you anticipate that you or someone else will ever want to maintain your code.

Explicit

    $r->add( "/update/:id", "update" );

    # Later
    sub update {
        my ( $self, $id ) = @_;
        # Do something with $id
    }

Optional

    $r->add( "/person/?name", sub {
        my ( $self, $name ) = @_;
        return "I am " . $name // "nobody";
    });

This will handle /person, /person/ and /person/jack.

Wildcards

    $r->add( '/*article/:id', 'articles#view' );

This will handle /bar/foo/baz/500 and send it to MyApp::Articles::view with parameters $article equal to bar/foo/baz and $id equal to 500.

Placeholder restrictions

Paths' named placeholders can be restricted by providing regular expressions.

    $r->add( '/user/:id', {
        check => { id => '\d+' },
        to    => "users#get"
    });

    # Matches /user/1000, but not /user/abc

Placeholder defaults

This only applies to optional placeholders, or those prefixed with a question mark. If a default value is provided for any of them, it will be used in case the placeholder value is missing.

    $r->add( '/:id/?other', defaults => { other => 'info' } );

    # GET /100;
    # { id => 100, other => 'info' }

    # GET /100/delete;
    # { id => 100, other => 'delete' }

Bridges

A bridge is a route that has to return a true value in order for the next route in line to be processed.

    $r->add( '/users', { to => 'Users::auth', bridge => 1 } );
    $r->add( '/users/:action' => 'Users::dispatch' );

See "BRIDGES" in Kelp::Routes for more information.

URL building

Each path can be given a name and later a URL can be built using that name and the necessary arguments.

    $r->add( "/update/:id", { name => 'update', to => 'user#update' } );

    # Later

    my $url = $self->route->url('update', id => 1000); # /update/1000

Quick development using Kelp::Less

For writing quick experimental web apps and to reduce the boiler plate, one could use Kelp::Less. In this case all of the code can be put in app.psgi: Look up the POD for Kelp::Less for many examples, but to get you started off, here is a quick one:

    # app.psgi
    use Kelp:::Less;

    get '/api/:user/?action' => sub {
        my ( $self, $user, $action ) = @_;
        my $json = {
            success => \1,
            user    => $user,
            action  => $action // 'ask'
        };
        return $json;
    };

    run;

Adding middleware

Kelp, being Plack-centric, will let you easily add middleware. There are three possible ways to add middleware to your application, and all three ways can be used separately or together.

Using the configuration

Adding middleware in your configuration is probably the easiest and best way for you. This way you can load different middleware for each running mode, e.g. Debug in development only.

Add middleware names to the middleware array in your configuration file and the corresponding initializing arguments in the middleware_init hash:

    # conf/config_development.pl
    {
        middleware      => [qw/Session Debug/],
        middleware_init => {
            Session => { store => 'File' }
        }
    }

The middleware will be added in the order you specify in the middleware array.

In app.psgi:

    # app.psgi
    use MyApp;
    use Plack::Builder;

    my $app = MyApp->new();

    builder {
        enable "Plack::Middleware::ContentLength";
        $app->run;
    };

By overriding the "run" subroutine in lib/MyApp.pm:

Make sure you call SUPER first, and then wrap new middleware around the returned app.

    # lib/MyApp.pm
    sub run {
        my $self = shift;
        my $app = $self->SUPER::run(@_);
        Plack::Middleware::ContentLength->wrap($app);
    }

Note that any middleware defined in your config file will be added first.

Deploying

Deploying a Kelp application is done the same way any other Plack application is deployed:

    > plackup -E deployment -s Starman app.psgi

Testing

Kelp provides a test class called Kelp::Test. It is object oriented, and all methods return the Kelp::Test object, so they can be chained together. Testing is done by sending HTTP requests to an already built application and analyzing the response. Therefore, each test usually begins with the "request" in Kelp::Test method, which takes a single HTTP::Request parameter. It sends the request to the web app and saves the response as an HTTP::Response object.

    # file t/test.t
    use MyApp;
    use Kelp::Test;
    use Test::More;
    use HTTP::Request::Common;

    my $app = MyApp->new( mode => 'test' );
    my $t = Kelp::Test->new( app => $app );

    $t->request( GET '/path' )
      ->code_is(200)
      ->content_is("It works");

    $t->request( POST '/api' )
      ->json_cmp({auth => 1});

    done_testing;

What is happening here?

Run the rest as usual, using prove:

    > prove -l t/test.t

Take a look at the Kelp::Test for details and more examples.

Building an HTTP response

Kelp contains an elegant module, called Kelp::Response, which extends Plack::Response with several useful methods. Most methods return $self after they do the required job. For the sake of the examples below, let's assume that all of the code is located inside a route definition.

Automatic content type

Your routes don't always have to set the response object. You could just return a simple scalar value or a reference to a hash, array or anything that can be converted to JSON.

    # Content-type automatically set to "text/html"
    sub text_route {
        return "There, there ...";
    }

    # Content-type automatically set to "application/json"
    sub json_route {
        return { error => 1,  message => "Fail" };
    }

Rendering text

    # Render simple text
    $self->res->text->render("It works!");

Rendering HTML

    $self->res->html->render("<h1>It works!</h1>");

Custom content type

    $self->res->set_content_type('image/png');

Return 404 or 500 errors

    sub some_route {
        my $self = shift;
        if ($missing) {
            return $self->res->render_404;
        }
        if ($broken) {
            return $self->res->render_500;
        }
    }

Templates

    sub hello {
        my ( $self, $name ) = @_;
        $self->res->template( 'hello.tt', { name => $name } );
    }

The above example will render the contents of hello.tt, and it will set the content-type to text/html. To set a different content-type, use set_content_type or any of its aliases:

    sub hello_txt {
        my ( $self, $name ) = @_;
        $self->res->text->template( 'hello_txt.tt', { name => $name } );
    }

Headers

    $self->set_header( "X-Framework", "Kelp" )->render( { success => \1 } );

Delayed responses

To send a delayed response, have your route return a subroutine.

    sub delayed {
        my $self = shift;
        return sub {
            my $responder = shift;
            $self->res->code(200);
            $self->res->text->body("Better late than never.");
            $responder->($self->res->finalize);
        };
    }

See the PSGI pod for more information and examples.

Pluggable modules

Kelp can be extended using custom modules. Each new module must be a subclass of the Kelp::Module namespace. Modules' job is to initialize and register new methods into the web application class. The following is the full code of the Kelp::Module::JSON for example:

    package Kelp::Module::JSON;

    use Kelp::Base 'Kelp::Module';
    use JSON;

    sub build {
        my ( $self, %args ) = @_;
        my $json = JSON->new;
        $json->property( $_ => $args{$_} ) for keys %args;
        $self->register( json => $json );
    }

    1;

What is happening here?

If we instruct our web application to load the JSON module, it will have a new method json which will be a link to the JSON object initialized in the module.

See more exampled and POD at Kelp::Module.

How to load modules using the config

There are two modules that are always loaded by each application instance. Those are Config and Routes. The reason behind this is that each and every application always needs a router and configuration. All other modules must be loaded either using the "load_module" method, or using the modules key in the configuration. The default configuration already loads these modules: Template, Logger and JSON. Your configuration can remove some and/or add others. The configuration key modules_init may contain hashes with initialization arguments. See Kelp::Module for configuration examples.

ATTRIBUTES

hostname

Gets the current hostname.

    sub some_route {
        my $self = shift;
        if ( $self->hostname eq 'prod-host' ) {
            ...
        }
    }

mode

Sets or gets the current mode. The mode is important for the app to know what configuration file to merge into the main configuration. See Kelp::Module::Config for more information.

    my $app = MyApp->new( mode => 'development' );
    # conf/config.pl and conf/config_development.pl are merged with priority
    # given to the second one.

path

Gets the current path of the application. That would be the path to app.psgi

name

Gets or sets the name of the application. If not set, the name of the main class will be used.

    my $app = MyApp->new( name => 'Twittar' );

charset

Sets of gets the encoding charset of the app. It will be UTF-8, if not set to anything else. The charset could also be changed in the config files.

req

This attribute only makes sense if called within a route definition. It will contain a reference to the current Kelp::Request instance.

    sub some_route {
        my $self = shift;
        if ( $self->req->is_json ) {
            ...
        }
    }

res

This attribute only makes sense if called within a route definition. It will contain a reference to the current Kelp::Response instance.

    sub some_route {
        my $self = shift;
        $self->res->json->render( { success => 1 } );
    }

METHODS

build

On it's own the build method doesn't do anything. It is called by the constructor, so it can be overridden to add route destinations and initializations.

    package MyApp;

    sub build {
        my $self = shift;
        my $r = $self->routes;

        # Load some modules
        $self->load_module("MongoDB");
        $self->load_module("Validate");

        # Add all route destinations
        $r->add("/one", "one");
        ...

    }

load_module

Used to load a module. All modules must be under the Kelp::Module:: namespace.

    $self->load_module("Redis");
    # Will look for an load Kelp::Module::Redis

See Kelp::Module for more information on making and using modules.

request

This method is used to create the request object for each HTTP request. It returns and instance of Kelp::Request, initialized with the current requests environment. You can override this method to use a custom request module.

    package MyApp;
    use MyApp::Request;

    sub request {
        my ( $self, $env ) = @_;
        return MyApp::Requst->new( app => $app, env => $env );
    }

    # Now each request will be handled by MyApp::Request

before_render

Override this method, to modify the response object just before it gets rendered.

    package MyApp;

    sub before_render {
        my $self = shift;
        $self->res->set_header("X-App-Name", "MyApp");
    }

    ...

The above is an example of how to insert a custom header into the response of every route.

response

This method creates the response object, e.g. what an HTTP request will return. By default the object created is Kelp::Response. Much like "request", the response can also be overridden to use a custom response object.

run

This method builds and returns the PSGI app. You can override it in order to include middleware. See "Adding middleware" for an example.

param

A shortcut to $self->req->param:

    sub some_route {
        my $self = shift;
        if ( $self->param('age') > 18 ) {
            $self->can_watch_south_path(1);
        }
    }

See Kelp::Request for more information and examples.

stash

Provides safe access to $self->req->stash. When called without arguments, it will return the stash hash. If called with a single argument, it will return the value of the corresponding key in the stash. See "stash" in Kelp::Request for more information and examples.

named

Provides safe access to $self->req->named. When called without arguments, it will return the named hash. If called with a single argument, it will return the value of the corresponding key in the named hash. See "named" in Kelp::Request for more information and examples.

url_for

A safe shortcut to $self->routes->url. Builds a URL from path and arguments.

    sub build {
        my $self = shift;
        $self->routes->add("/:name/:id", { name => 'name', to => sub {
            ...
        }});
    }

    sub check {
        my $self = shift;
        my $url_for_name = $self->url_for('name', name => 'jake', id => 1003);
        $self->res->redirect_to();
    }

NAME

Kelp::Less - Quick prototyping with Kelp

SYNOPSIS

    use Kelp::Less;

    get '/person/:name' => sub {
        "Hello " . named 'name';
    };

    run;

DESCRIPTION

This class exists to provide a way for quick and sloppy prototyping of a web application. It is a wrapper for Kelp, which imports several keywords, making it easier and less verbose to create a quick web app.

It's called Less, because there is less typing involved, and because it is suited for smaller, less complicated web projects. We encourage you to use it anywhere you see fit, however for mid-size and big applications we recommend that you use the fully structured Kelp. This way you can take advantage of its powerful router, initialization and testing capabilities.

QUICK START

Each web app begins with use Kelp::Less;. This automatically imports strict, warnings, v5.10 as well as several useful functions. You can pass any parameters to the constructor at the use statement:

    use Kelp::Less mode => 'development';

The above is equivalent to:

    use Kelp;
    my $app = Kelp->new( mode => 'development' );

After that, you could add any initializations and attributes. For example, connect to a database or setup cache. Kelp::Less exports attr, so you can use it to register attributes to your app.

    # Connect to DBI and CHI right away
    attr dbh => sub {
        DBI->connect( @{ app->config('database') } );
    };

    attr cache => sub {
        CHI->new( @{ app->config('cache') } );
    };

    # Another lazy attribute.
    attr version => sub {
        app->dbh->selectrow_array("SELECT version FROM vars");
    };

    # Later:
    app->dbh->do(...);
    app->cache->get(...);
    if ( app->version ) { ... }

Now is a good time to add routes. Routes are added via the "route" keyword and they are automatically registered in your app. A route needs two parameters - path and destination. These are exactly equivalent to "add" in Kelp::Routes, and you are encouraged to read its POD to get familiar with how to define routes. Here are a few examples for the impatient:

    # Add a 'catch-all-methods' route and send it to an anonymous sub
    route '/hello/:name' => sub {
        return "Hello " . named('name');
    };

    # Add a POST route
    route [ POST => '/edit/:id' ] => sub {
        # Do something with named('id')
    };

    # Route that runs an existing sub in your code
    route '/login' => 'login';
    sub login {
        ...
    }

Each route subroutine receives $self and all named placeholders.

    route '/:id/:page' => sub {
        my ( $self, $id, $page ) = @_;
    };

Here, $self is the app object and it can be used the same way as in a full Kelp route. For the feeling of magic and eeriness, Kelp::Lite aliases app to $self, so the former can be used as a full substitute to the latter. See the exported keywords section for more information.

After you have added all of your routes, it is time to run the app. This is done via a single command:

    run;

It returns PSGI ready subroutine, so you can immediately deploy your new app via Plack:

    > plackup myapp.psgi
    HTTP::Server::PSGI: Accepting connections at http://0:5000/

KEYWORDS

The following list of keywords are exported to allow for less typing in Kelp::Less:

app

This a full alias for $self. It is the application object, and an instance of the Kelp class. You can use it for anything you would use $self inside a route.

    route '/yo' => sub {
        app->res->code(500);
    };

attr

Assigns lazy or active attributes (using Kelp::Base) to app. Use it to initialize your application.

    attr mongo => MongoDB::MongoClient->new( ... );

route

Adds a route to app. It is an alias to $self->routes->add, and requires the exact same parameters. See Kelp::Routes for reference.

    route '/get-it' => sub { "got it" };

get, post, put, del

These are shortcuts to route restricted to the corresponding HTTP method.

    get '/data'  => sub { "Only works with GET" };
    post '/data' => sub { "Only works with POST" };
    put '/data'  => sub { "Only works with PUT" };
    del '/data'  => sub { "Only works with DELETE" };

param

An alias for $self->param that gets the GET or POST parameters. When used with no arguments, it will return an array with the names of all http parameters. Otherwise, it will return the value of the requested http parameter.

    get '/names' => sub {
        my @names = param;
        # Now @names contains the names of the params
    };

    get '/value' => sub {
        my $id = param 'id';
        # Now $is contains the value of 'id'
    };

stash

An alias for $self->stash. The stash is a concept originally conceived by the developers of Catalyst. It's a hash that you can use to pass data from one route to another.

    # Create a bridge route that checks if the user is authenticated, and saves
    # the username in the stash.
    get '/user' => { bridge => 1, to => sub {
        return stash->{username} = app->authenticate();
    }};

    # This route is run after the above bridge, so we know that we have an
    # authenticated user and their username in the stash.
    get '/user/welcome' => sub {
        return "Hello " . stash 'username';
    };

With no arguments stash returns the entire stash hash. A single argument is interpreted as the key to the stash hash and its value is returned accordingly.

named

An alias for $self->named. The named hash contains the names and values of the named placeholders from the current route's path. Much like the stash, with no arguments it returns the entire named hash, and with a single argument it returns the value for the corresponding key in the hash.

    get '/:name/:id' => sub {
        my $name = named 'name';
        my $id = name 'id';
    };

In the above example a GET request to /james/1000 will initialize $name with "james" and $id with 1000.

req

An alias for $self->req, this provides quick access to the Kelp::Request object for the current route.

    # Inside a route
    if ( req->is_ajax ) {
        ...
    }

res

An alias for $self->res, this is a shortcut for the Kelp::Response object for the current route.

    # Inside a route
    res->code(403);
    res->json->render({ message => "Forbidden" });

template

A shortcut to $self->res->template. Renders a template using the currently loaded template module.

    get '/hello/:name' => sub {
        template 'hello.tt', { name => named 'name' };
    };

run

Creates and returns a PSGI ready subroutine, and makes the app ready for Plack.

TESTING

When writing a Kelp::Less app, we don't have a separate class to initialize as we please and feed into a Kelp::Test object. In this case, the Kelp::Test object can be initialized with the name of the PSGI file in the psgi argument.

    # t/main.t
    use Kelp::Test;

    my $t = Kelp::Test->new( psgi => 'app.psgi' );
    # Do some tests ...

Since you don't have control over the creation of the Kelp object, if you need to specify a different mode for testing, you can use the PLACK_ENV environmental variable:

    > PLACK_ENV=test prove -l

This will enable the conf/config_test.pl configuration, which you should tailor to your testing needs.

ACKNOWLEDGEMENTS

This module's interface was inspired by Dancer, which in its turn was inspired by Sinatra, so Viva La Open Source!

NAME

Kelp::Response - Format an HTTP response

SYNOPSIS

Examples of route definitions make a lot more sense when showing how to use this module. Note that in the below examples $self->res is an instance of Kelp::Response:

    # Render simple text
    sub text {
        my $self = shift;
        $self->res->text->render("It works!");
    }

    # Render advanced HTML
    sub html {
        my $self = shift;
        $self->res->html->render("<h1>It works!</h1>");
    }

    # Render a mysterious JSON structure
    sub json {
        my $self = shift;
        $self->res->json->render({ why => 'no' });
    }

    # Render the stock 404
    sub missing {
        my $self = shift;
        $self->res->render_404;
    }

    # Render a template
    sub view {
        my $self = shift;
        $self->res->template('view.tt', { name => 'Rick James' } );
    }

DESCRIPTION

The PSGI specification requires that each route returns an array with status code, headers and body. Plack::Response already provides many useful methods that deal with that. This module extends Plack::Response to add the tools we need to write graceful PSGI compliant responses. Some methods return $self, which makes them easy to chain.

ATTRIBUTES

render

This method tries to act smart, without being a control freak. It will fill out the blanks, unless they were previously filled out by someone else. Here is what is does:

set_content_type

Sets the content type of the response and returns $self.

    # Inside a route definition
    $self->res->set_content_type('image/png');

text, html, json, xml

These methods are shortcuts for set_content_type with the corresponding type. All of them set the content-type header and return $self so they can be chained.

    $self->res->text->render("word");
    $self->res->html->render("<p>word</p>");
    $self->res->json->render({ word => \1 });

set_header

Sets response headers. This is a wrapper around "header" in Plack::Response, which returns $self to allow for chaining.

    $self->res->set_header('X-Something' => 'Value')->text->render("Hello");

no_cache

A convenience method that sets several response headers instructing most browsers to not cache the response.

    $self->res->no_cache->json->render({ epoch => time });

The above response will contain headers that disable caching.

set_code

Set the response code.

    $self->res->set_code(401)->render("Access denied");

render_404

A convenience method that sets code 404 and returns "File Not Found".

    sub some_route {
        if ( not $self->req->param('ok') ) {
            return $self->res->render_404;
        }
    }

If your application's tone is overly friendly or humorous, you will want to create a custom 404 page. The best way to do this is to subclass this module into your own class, for example MyApp::Response. Then override the render_404 method.

    package MyApp::Response;
    use parent 'Kelp::Response';

    sub render_404 {
        my $self = shift;
        $self->set_code(404)->template('errors/404.tt');
    }

You'll have to override the "response" in Kelp method in your main module too, in order to instruct it to use your new class:

    package MyApp;
    use parent 'Kelp';

    sub response {
        my $self = shift;
        return MyApp::Response->new( app => $self );
    }

render_500

Renders the stock "500 - Server Error" message. See "render_404" for examples on how to customize it.

template

This method renders a template. The template should be previously configured by you and included via a module. See Kelp::Module::Template for a template module.

    sub some_route {
        my $self = shift;
        $self->res->template('home.tt', { login => 'user' });
    }

NAME

Kelp::Module::Config - Configuration for Kelp applications

DESCRIPTION

This is one of the two modules that are automatically loaded for each and every Kelp application. It uses Config::Hash to read Perl-style hashes from files and merge them depending on the value of the mode attribute.

The main configuration file name is config.pl, and it will be searched in the conf directory or ../conf. The latter is convenient for running tests which use the same configuration settings as the main app.

REGISTERED METHODS

This module registers the following methods into the underlying app:

config

A wrapper for the get method in Config::Hash.

    # Somewhere in the app
    my $pos = $self->config('row.col.position');

    # Gets {row}->{col}->{position} from the config hash

config_hash

A reference to the entire configuration hash.

    my $pos = $self->config_hash->{row}->{col}->{position};

Using this or config is entirely up to the application developer.

DEFAULTS

This module sets certain default values. All of them may be overridden in any of the conf/ files. It probably pays to view the code of this module and look and the defaults sub to see what is being set by default, but here is the short version:

charset

UTF-8

app_url

http://localhost:5000

modules

An arrayrf with module names to load on startup. The default value is ['JSON', 'Template', 'Logger']

modules_init

A hashref with initializations for each of the loaded modules, except this one, ironically.

middleware

An arrayref with middleware to load on startup. The default value is an empty array.

middleware_init

A hashref with iitialization arguments for each of the loaded middleware.

NAME

Kelp::Module::JSON - Simple JSON module for a Kelp application

SYNOPSIS

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

    sub some_route {
        my $self = shift;
        return $self->json->encode( { yes => 1 } );
    }

REGISTERED METHODS

This module registers only one method into the application: json.

NAME

Kelp::Test - Automated tests for a Kelp web app

SYNOPSIS

    use MyApp;
    use Kelp::Test;
    use HTTP::Request::Common;

    my $app = MyApp->new;
    my $t = Kelp::Test->new( app => $app );

    $t->request( GET '/path' )
      ->code_is(200)
      ->content_is("It works");

    $t->request( POST '/api' )
      ->json_cmp({auth => 1});

DESCRIPTION

This module provides basic tools for testing a Kelp based web application. It is object oriented, and all methods return $self, so they can be chained together. Testing is done by sending HTTP requests to an already built application and analyzing the response. Therefore, each test usually begins with the "request" method, which takes a single HTTP::Request parameter. It sends the request to the web app and saves the response as an HTTP::Response object.

ATTRIBUTES

app

The Kelp::Test object is instantiated with single attribute called app. It is a reference to a Kelp based web app.

    my $myapp = MyApp->new;
    my $t = Kelp::Test->new( app => $myapp );

From this point on, all requests run with $t->request will be sent to $app.

psgi

This is only used when testing Kelp::Less apps. Since we don't have an application class to load, we need to load the app.psgi file.

    my $t = Kelp::Test->new( psgi => 'app.psgi' );

All testing methods work exactly the same way. Note that since we don't have control over how the Kelp class is loaded, we may have to use the PLACK_ENV environment variable to set different testing modes.

    > PLACK_ENV=test prove -l

The above is the same as:

    use Kelp::Less mode => 'test';

res

Each time $t-request> is used to send a request, an HTTP::Response object is returned and saved in the res attribute. You can use it to run tests, although as you will see, this module provides methods which make this a lot easier. It is recommended that you use the convenience methods rather than using res.

    $t->request( GET '/path' )
    is $t->res->code, 200, "It's a success";

METHODS

request

request( $http_request )

Takes an HTTP::Request object and sends it to the application. When the HTTP::Response object is returned, it is initialized in the "res" attribute. It is very convenient to use HTTP::Request::Common in your test modules, so you can take advantage of the simplified syntax for creating an HTTP request.

    $t->request( POST '/api', [ user => 'jane' ] );

This method returns $self, so other methods can be chained after it.

code_is, code_isnt

code_is( $code, $test_name ), code_isnt( $code, $test_name )

Tests if the last response returned a status code equal or not equal to $code. An optional name of the test can be added as a second parameter.

    $t->request( GET '/path' )->code_is(200);
    $t->request( GET '/path' )->code_isnt(500);

content_is, content_isnt

content_is( $value, $test_name ), content_isnt( $value, $test_name )

Tests if the last response returned content equal or not equal to $value. An optional name of the test can be added as a second parameter.

    $t->request( GET '/path' )->content_is("Ok.");
    $t->request( GET '/path' )->content_isnt("Fail.");

content_like, content_unlike

content_like( $regexp, $test_name ), content_unlike( $regexp, $test_name )

Tests if the last response returned content that matches or doesn't match $regexp. An optional name of the test can be added as a second parameter.

    $t->request( GET '/path' )->content_like(qr{Amsterdam});
    $t->request( GET '/path' )->content_unlike(qr{Rotterdam});

content_type_is, content_type_isnt

content_type_is( $value, $test_name ), content_type_isnt( $value, $test_name )

Tests if the last response's content-type header is equal or not equal to $value. An optional name of the test can be added as a second parameter.

    $t->request( GET '/path' )->content_type_is("text/plain");
    $t->request( GET '/path' )->content_type_isnt("text/html");

header_is, header_isnt

header_is( $header, $value, $test_name ), header_isnt( $header, $value, $test_name )

Tests if the last response returned a header $header that is equal or not equal to $value. An optional name of the test can be added as a second parameter.

    $t->request( GET '/path' )->header_is( "Pragma", "no-cache" );
    $t->request( GET '/path' )->header_isnt( "X-Check", "yes" );

header_like, header_unlike

header_like( $header, $regexp, $test_name ), header_unlike( $header, $regexp, $test_name )

Tests if the last response returned a header $header that matches or doesn't match $regexp. An optional name of the test can be added as a second parameter.

    $t->request( GET '/path' )->header_like( "Content-Type", qr/json/ );
    $t->request( GET '/path' )->header_unlike( "Content-Type", qr/image/ );

json_cmp

json_cmp( $expected, $test_name )

This tests for two things: If the returned content-type is application-json, and if the returned JSON structure matches the structure specified in $expected. To compare the two structures this method uses cmp_deeply from Test::Deep, so you can use all the goodies from the SPECIAL-COMPARISONS-PROVIDED section of the Test::Deep module.

    $t->request( GET '/api' )->json_cmp(
        {
            auth      => 1,
            timestamp => ignore(),
            info      => subhashof( { name => 'Rick James' } )
        }
    );

An optional name of the test can be added as a second parameter.

note

note( $note )

Print a note, using the Test::More note function.

    $t->request( GET '/path' )
      ->note("Checking headers now")
      ->header_is( "Content-Type", qr/json/ );

diag_headers

Prints all headers for debugging purposes.

    $t->request( GET '/path' )
      ->header_is( "Content-Type", qr/json/ )
      ->diag_headers();

diag_content

Prints the entire content for debugging purposes.

    $t->request( GET '/path' )
      ->content_is("Well")
      ->diag_content();

NAME

Kelp::Request - Request class for a Kelp application

SYNOPSIS

    my $request = Kelp::Request( app => $app, env => $env );

DESCRIPTION

This module provides a convenience layer on top of Plack::Request. It extends it to add several convenience methods.

ATTRIBUTES

app

A reference to the Kelp application.

stash

An all use, utility hash to use to pass information between routes.

named

This hash is initialized with the named placeholders of the path that the current route is processing.

param

Returns the HTTP parameters of the request. This method delegates all the work to "param" in Plack::Request, except when the content type of the request is application/json. In that case, it will decode the JSON body and return as follows:

is_ajax

Returns true if the request was called with XMLHttpRequest.

is_json

Returns true if the request's content type was application/json.

NAME

Kelp::Routes - Routing for a Kelp app

SYNOPSIS

    use Kelp::Routes;
    my $r = Kelp::Routes->new( base => 'MyApp' );
    $r->add( '/home', 'home' );

DESCRIPTION

The router provides the connection between the HTTP requests and the web application code. It tells the application "If you see a request coming to *this* URI, send it to *that* subroutine for processing". For example, if a request comes to /home, then send it to sub home in the current namespace. The process of capturing URIs and sending them to their corresponding code is called routing.

This router was specifically crafted as part of the Kelp web framework. It is, however, possible to use it on its own, if needed.

It provides a simple, yet sophisticated routing utilizing Perl 5.10's regular expressions, which makes it fast, robust and reliable.

The routing process can roughly be broken down into three steps:

Adding routes

First you create a router object:

    my $r = Kelp::Routes->new();

Then you add your application's routes and their descriptions:

    $r->add( '/path' => 'Module::function' );
    ...
Matching

Once you have your routes added, you can match with the "match" subroutine.

    $r->match( $path, $method );

The Kelp framework already does matching for you, so you may never have to do your own matching. The above example is provided only for reference.

Building URLs from routes

You can name each of your routes, and use that name later to build a URL:

    $r->add( '/begin' => { to => 'function', name => 'home' } );
    my $url = $r->url('home');    # /begin

This can be used in views and other places where you need the full URL of a route.

PLACEHOLDERS

Often routes may get more complicated. They may contain variable parts. For example this one /user/1000 is expected to do something with user ID 1000. So, in this case we need to capture a route that begins with /user/ and then has something else after it.

Naturally, when it comes to capturing routes, the first instinct of the Perl programmer is to use regular expressions, like this:

    qr{/user/(\d+)} -> "sub home"

This module will let you do that, however regular expressions can get very complicated, and it won't be long before you lose track of what does what.

This is why a good router (this one included) allows for named placeholders. These are words prefixed with special symbols, which denote a variable piece in the URI. To use the above example:

    "/user/:id" -> "sub home"

It looks a little cleaner.

Placeholders are variables you place in the route path. They are identified by a prefix character and their names must abide to the rules of a regular Perl variable. If necessary, curly braces can be used to separate placeholders from the rest of the path.

There are three types of place holders:

Explicit

These placeholders begin with a column (:) and must have a value in order for the route to match. All characters are matched, except for the forward slash.

    $r->add( '/user/:id' => 'module#sub' );
    # /user/a       -> match (id = 'a')
    # /user/123     -> match (id = 123)
    # /user/        -> no match
    # /user         -> no match
    # /user/10/foo  -> no match

    $r->add( '/page/:page/line/:line' => 'module#sub' );
    # /page/1/line/2        -> match (page = 1, line = 2)
    # /page/bar/line/foo    -> match (page = 'bar', line = 'foo')
    # /page/line/4          -> no match
    # /page/5               -> no match

    $r->add( '/{:a}ing/{:b}ing' => 'module#sub' );
    # /walking/singing      -> match (a = 'walk', b = 'sing')
    # /cooking/ing          -> no match
    # /ing/ing              -> no match

Optional

Optional placeholders begin with a question mark ? and denote an optional value. You may also specify a default value for the optional placeholder via the "defaults" option. Again, like the explicit placeholders, the optional ones capture all characters, except the forward slash.

    $r->add( '/data/?id' => 'module#sub' );
    # /bar/foo          -> match ( id = 'foo' )
    # /bar/             -> match ( id = undef )
    # /bar              -> match ( id = undef )

    $r->add( '/:a/?b/:c' => 'module#sub' );
    # /bar/foo/baz      -> match ( a = 'bar', b = 'foo', c = 'baz' )
    # /bar/foo          -> match ( a = 'bar', b = undef, c = 'foo' )
    # /bar              -> no match
    # /bar/foo/baz/moo  -> no match

Optional default values may be specified via the defaults option.

    $r->add(
        '/user/?name' => {
            to       => 'module#sub',
            defaults => { name => 'hank' }
        }
    );

    # /user             -> match ( name = 'hank' )
    # /user/            -> match ( name = 'hank' )
    # /user/jane        -> match ( name = 'jane' )
    # /user/jane/cho    -> no match

Wildcards

The wildcard placeholders expect a value and capture all characters, including the forward slash.

    $r->add( '/:a/*b/:c'  => 'module#sub' );
    # /bar/foo/baz/bat  -> match ( a = 'bar', b = 'foo/baz', c = 'bat' )
    # /bar/bat          -> no match

Using curly braces

Curly braces may be used to separate the placeholders from the rest of the path:

    $r->add( '/{:a}ing/{:b}ing' => 'module#sub' );
    # /looking/seeing       -> match ( a = 'look', b = 'see' )
    # /ing/ing              -> no match

    $r->add( '/:a/{?b}ing' => 'module#sub' );
    # /bar/hopping          -> match ( a = 'bar', b = 'hopp' )
    # /bar/ing              -> match ( a = 'bar' )
    # /bar                  -> no match

    $r->add( '/:a/{*b}ing/:c' => 'module#sub' );
    # /bar/hop/ping/foo     -> match ( a = 'bar', b = 'hop/p', c = 'foo' )
    # /bar/ing/foo          -> no match

BRIDGES

The "match" subroutine will stop and return the route that best matches the specified path. If that route is marked as a bridge, then "match" will continue looking for another match, and will eventually return an array of one or more routes. Bridges can be used for authentication or other route preprocessing.

    $r->add( '/users', { to => 'Users::auth', bridge => 1 } );
    $r->add( '/users/:action' => 'Users::dispatch' );

The above example will require /users/profile to go through two subroutines: Users::auth and Users::dispatch:

    my $arr = $r->match('/users/view');
    # $arr is an array of two routes now, the bridge and the last one matched

TREES

A quick way to add bridges is to use the "tree" option. It allows you to define all routes under a bridge. Example:

    $r->add(
        '/users' => {
            to   => 'users#auth',
            name => 'users',
            tree => [
                '/profile' => {
                    name => 'profile',
                    to   => 'users#profile'
                },
                '/settings' => {
                    name => 'settings',
                    to   => 'users#settings',
                    tree => [
                        '/email' => { name => 'email', to => 'users#email' },
                        '/login' => { name => 'login', to => 'users#login' }
                    ]
                }
            ]
        }
    );

The above call to add causes the following to occur under the hood:

ATTRIBUTES

base

Sets the base class for the routes destinations.

    my $r = Kelp::Routes->new( base => 'MyApp' );

This will prepend MyApp:: to all route destinations.

    $r->add( '/home' => 'home' );          # /home -> MyApp::home
    $r->add( '/user' => 'user#home' );     # /user -> MyApp::User::home
    $r->add( '/view' => 'User::view' );    # /view -> MyApp::User::view

A Kelp application will automatically set this value to the name of the main class. If you need to use a route located in another package, you'll have to wrap it in a local sub:

    # Problem:

    $r->add( '/outside' => 'Outside::Module::route' );
    # /outside -> MyApp::Outside::Module::route
    # (most likely not what you want)

    # Solution:

    $r->add( '/outside' => 'outside' );
    ...
    sub outside {
        return Outside::Module::route;
    }

SUBROUTINES

add

Adds a new route definition to the routes array.

    $r->add( $path, $destination );

$path can be a path string, e.g. '/user/view' or an ARRAY containing a method and a path, e.g. [ PUT => '/item' ].

The route destination is very flexible. It can be one of these three things:

Destination Options

There are a number of options you can add to modify the behavior of the route, if you specify a hashref for a destination:

to

Sets the destination for the route. It should be a subroutine name or CODE reference.

    $r->add( '/user' => { to => 'users#home' } ); # /home -> MyApp::Users::home
    $r->add( '/sys' => { to => sub { ... } });    # /sys -> execute code
    $r->add( '/item' => { to => 'Items::handle' } ) ;   # /item -> MyApp::Items::handle
    $r->add( '/item' => { to => 'Items::handle' } );    # Same as above

via

Specifies an HTTP method to be considered by "match" when matching a route.

    # POST /item -> MyApp::Items::add
    $r->add(
        '/item' => {
            via => 'POST',
            to  => 'items#add'
        }
    );

A shortcut for the above is this:

    $r->add( [ POST => '/item' ] => 'items#add' );

name

Give the route a name, and you can always use it to build a URL later via the "url" subroutine.

    $r->add(
        '/item/:id/:name' => {
            to   => 'items#view',
            name => 'item'
        }
    );

    # Later
    $r->url( 'item', id => 8, name => 'foo' );    # /item/8/foo

check

A hashref of checks to perform on the captures. It should contain capture names and stringified regular expressions. Do not use ^ and $ to denote beginning and ending of the matched expression, because it will get embedded in a bigger Regexp.

    $r->add(
        '/item/:id/:name' => {
            to    => 'items#view',
            check => {
                id   => '\d+',          # id must be a digit
                name => 'open|close'    # name can be 'open' or 'close'
            }
          }
    );

defaults

Set default values for optional placeholders.

    $r->add(
        '/pages/?id' => {
            to       => 'pages#view',
            defaults => { id => 2 }
        }
    );

    # /pages    -> match ( id = 2 )
    # /pages/   -> match ( id = 2 )
    # /pages/4  -> match ( id = 4 )

bridge

If set to one this route will be treated as a bridge. Please see "bridges" for more information.

tree

Creates a tree of sub-routes. See "trees" for more information and examples.

match

Returns an array of Kelp::Routes::Pattern objects that match the path and HTTP method provided. Each object will contain a hash with the named placeholders in "named" in Kelp::Routes::Pattern, and an array with their values in the order they were specified in the pattern in "param" in Kelp::Routes::Pattern.

    $r->add( '/:id/:name', "route" );
    for my $pattern ( @{ $r->match('/15/alex') } ) {
        $pattern->named;    # { id => 15, name => 'alex' }
        $pattern->param;    # [ 15, 'alex' ]
    }

Routes that used regular expressions instead of patterns will only initialize the param array with the regex captures, unless those patterns are using named captures in which case the named hash will also be initialized.

ACKNOWLEDGEMENTS

This module was inspired by Routes::Tiny.

NAME

Kelp::Routes::Pattern - Route patterns for Kelp routes

SYNOPSIS

    my $p = Kelp::Routes::Pattern->new( pattern => '/:name/:place' );
    if ( $p->match('/james/london') ) {
        %named = %{ $p->named };    # ( name => 'james', place => 'london' )
        @param = @{ $p->param };    # ( 'james', 'london' )
    }

DESCRIPTION

This module is needed by Kelp::Routes. It provides matching for individual route patterns, returning the named placeholders in a hash and an array.

ATTRIBUTES

pattern

The pattern to match against. Each pattern is a string, which may contain named placeholders. For more information on the types and use of placeholders, look at "PLACEHOLDERS" in Kelp::Routes.

    my $p = Kelp::Routes::Patters->new( pattern => '/:id/*other' );
    ...
    $p->match('/4/something-else');    # True

via

Specifies an HTTP method to be matched by the route.

    my $p = Kelp::Routes::Patters->new(
        pattern => '/:id/*other',
        via     => 'PUT'
    );

    $p->match('/4/something-else', 'GET');    # False. Only PUT allowed.

name

You are encouraged to give each route a name, so you can look it up later when you build a URL for it.

    my $p = Kelp::Routes::Patters->new(
        pattern => '/:id/*other',
        name    => 'other_id'
    );
    ...

    say $p->build( 'other_id', id => '100', other => 'something-else' );
    # Prints '/100/something-else'

If no name is provided for the route, the pattern is used.

check

A hashref with placeholder names as keys and regular expressions as values. It is used to match the values of the placeholders against the provided regular expressions.

    my $p = Kelp::Routes::Patters->new(
        pattern => '/:id/*other',
        check   => { id => qr/\d+/ }    # id may only be a didgit
    );

    $p->match('/4/other');    # True
    $p->match('/q/other');    # False

Note: Do not add ^ at the beginning or $ at the end of the regular expressions, because they are merged into a bigger regex.

defaults

A hashref with placeholder defaults. This only applies to optional placeholders, or those prefixed with a question mark. If a default value is provided for any of them, it will be used in case the placeholder value is missing.

    my $p = Kelp::Routes::Patters->new(
        pattern  => '/:id/?other',
        defaults => { other => 'info' }
    );

    $p->match('/100');
    # $p->named will contain { id => 100, other => 'info' }

    $p->match('/100/delete');
    # $p->named will contain { id => 100, other => 'delete' }

bridge

A True/False value. Specifies if the route is a bridge. For more information about bridges, please see "BRIDGES" in Kelp::Routes

regex

We recommend that you stick to using patterns, because they are simpler and easier to read, but if you need to match a really complicated route, then you can use a regular expression.

    my $p = Kelp::Routes::Patters->new( regex => qr{^(\d+)/(\d+)$} );
    $p->match('/100/200');  # True. $p->param will be [ 100, 200 ]

After matching, the "param" array will be initialized with the values of the captures in the order they appear in the regex. If you used a regex with named captures, then a hashref "named" will also be initialized with the names and values of the named placeholders. In other words, this hash will be a permanent copy of the %+ built-in hash.

    my $p = Kelp::Routes::Patters->new( regex => qr{^(?<id>\d+)/(?<line>\d+)$} );
    $p->match('/100/200');  # True.
                            # $p->param will be [ 100, 200 ]
                            # $p->named will be { id => 100, line => 200 }

If regex is not explicitly given a value it will be built from the pattern.

named

A hashref which will be initialized by the "match" function. After matching, it will contain placeholder names and values for the matched route.

param

An arrayref, which will be initialized by the "match" function. After matching, it will contain all placeholder values in the order they were specified in the pattern.

to

Specifies the route destination. See examples in Kelp::Routes.

METHODS

match

match( $path, $method )

Matches an already initialized route against a path and http method. If the match was successful, this sub will return a true value and the "named" and "param" attributes will be initialized with the names and values of the matched placeholders.

build build( %args )

Builds a URL from a pattern.

    my $p = Kelp::Routes::Patters->new( pattern  => '/:id/:line/:row' );
    $p->build( id => 100, line => 5, row => 8 ); # Returns '/100/5/8'

ACKNOWLEDGEMENTS

This module was inspired by Routes::Tiny.

The concept of bridges was borrowed from Mojolicious

NAME

Kelp::Base - Simple lazy attributes

SYNOPSIS

    use Kelp::Base;

    attr source => 'dbi:mysql:users';
    attr user   => 'test';
    attr pass   => 'secret';
    attr opts   => { PrintError => 1, RaiseError => 1 };

    attr dbh => sub {
        my $self = shift;
        DBI->connect( $self->sourse, $self->user, $self->pass, $self->opts );
    };

    # Later ...
    sub do_stuff {
        my $self = shift;
        $self->dbh->do('DELETE FROM accounts');
    }

or

    use Kelp::Base 'Module::Name';    # Extend Module::Name

or

    use Kelp::Base -strict;    # Only use strict, warnings and v5.10
                                  # No magic

DESCRIPTION

This module provides simple lazy attributes.

WHY?

Some users will naturally want to ask "Why not use Moose/Mouse/Moo/Mo?". The answer is that the Kelp web framework needs lazy attributes, but the author wanted to keep the code light and object manager agnostic. This allows the users of the framework to choose an object manager to their liking. There is nothing more annoying than a module that forces you to use Moose when you are perfectly fine with Moo or Mo, for example.

USAGE

    use Kelp::Base;

The above will automatically include strict, warnings and v5.10. It will also inject a new sub in the current class called attr.

    attr name1 => 1;         # Fixed value
    attr name2 => [1,2,3];   # Array
    attr name3 => sub {
        $_[0]->other
    }

    ...

    say $self->name1;               # 1
    $self->name2( [ 6, 7, 8 ] );    # Set new value

All those attributes will be available for reading and writing in each instance of the current class. If you want to create a read-only attribute, prefix its name with a dash.

    attr -readonly => "something";

    # Later
    say $self->readonly;           # something
    $self->readonly("nothing");    # no change

SEE ALSO

Kelp, Moose, Moo, Mo, Any::Moose

NAME

Kelp::Module - Base class for Kelp modules

SYNOPSIS

    package Kelp::Module::MyModule;
    use parent 'Kelp::Module';

    sub build {
        my ( $self, %args ) = @_;
        $self->register( greet => sub { print "Hi there." } );
    }

DESCRIPTION

Provides the base class for creating Kelp modules. Creating a Kelp module means extending this class and overriding the build method. Kelp modules usually register a new method into the web application.

Registering methods

Modules use the "register" method to register new methods into the underlying web application. All the registrations are done in the "build" subroutine. All types of values can be registered and then accessed as a read-only attribute from the web app. The simplest thing you can register is a scalar value:

First...

    # lib/Kelp/Module/Month.pm
    package Kelp::Module::Month;
    use Kelp::Base 'Kelp::Module';

    sub build {
        my ( $self, %args ) = @_;
        $self->register( month => 'October' );
    }

Then ...

    # lib/MyApp.pm
    package MyApp;
    use parent 'Kelp';

    sub build {
        $self->load_module("Month");
    }

    sub is_it_october_yet {
        my $self = shift;
        if ( $self->month eq 'October' ) {
            return "It is October";
        }
        return "Not yet.";
    }

The above example doesn't do anything meaningful, but it's a good way to show how to create and use Kelp modules. Pay attention to the next example, as it will show you how to register an anonymous subroutine:

    package Kelp::Module::Date;
    use Kelp::Base 'Kelp::Module';
    use DateTime;

    sub build {
        my ( $self, %args ) = @_;
        $self->register(
            date => sub {
                return DateTime->from_epoch( epoch => time );
            }
        );
    }

Now, each time you use $self->date in the web application, you will create a new DateTime object for the current time.

It is more practical to register an already created object. Consider this example, which uses Redis, initializes an instance of it and registers it as a method in the web app:

    package Kelp::Module::Redis;
    use Kelp::Base 'Kelp::Module';
    use Redis;

    sub build {
        my ( $self, %args ) = @_;
        my $redis = Redis->new(%args);
        $self->register( redis => $redis );
    }

Passing arguments to your module

The arguments for all modules are taken from the configuration. If you want to pass arguments for your Redis module (example above), you will have to have a structure in your config, similar to this:

Example of conf/myapp.conf:

    {
        # Load module Redis on start
        modules      => ['Redis'],
        modules_init => {
            Redis => {
                server   => '192.168.0.1:6379',
                encoding => 'UTF-8',
                password => 'boo'
            }
        }
    };

The hash specified by Redis will be sent as %args in the build method of the module.

METHODS

build

build( %args )

Each module must override this one in order to register new methods. The %args hash will be taken from the configuration.

register

register( %items )

Register one or many methods into the web application.

    $self->register(
        json => JSON->new,
        yaml => YAML->new
    );

NAME

Kelp - A web framework light, yet rich in nutrients.

MANUAL

Kelp::Manual::Main

Main manual.

Kelp::Manual::Less

Information on using Kelp::Less.

SUPPORT

AUTHOR

Stefan Geneshky - minimal@cpan.org

LICENSE

This module and all the modules in this package are governed by the same license as Perl itself.