NAME
PSGI::Handy - a tiny dependency-free PSGI web framework for Perl 5.005_03 and later
VERSION
Version 0.01
SYNOPSIS
use PSGI::Handy;
my $app = PSGI::Handy->new(
renderer => \&my_template_renderer, # or an object with render()
db => $dbh, # any database handle
);
$app->get('/', sub {
my $c = shift;
return $c->html('<h1>Hello</h1>');
});
$app->get('/users/:id', sub {
my $c = shift;
return $c->render('user.html', { id => $c->param('id') });
});
$app->post('/users', sub {
my $c = shift;
my $name = $c->param('name');
# ... use $c->db ...
return $c->redirect('/');
});
# PSGI::Handy builds the PSGI app; serve it with any PSGI server:
my $psgi_app = $app->to_app; # sub { my $env = shift; ... }
# for example, with HTTP::Handy as the delivery layer:
use HTTP::Handy;
HTTP::Handy->run(app => $psgi_app, host => '127.0.0.1', port => 8080);
TABLE OF CONTENTS
DESCRIPTION
PSGI::Handy is the application layer of the "Handy" stack. It wires a router ("PSGI::Handy::Router"), a request and a response object ("PSGI::Handy::Request", "PSGI::Handy::Response") and a per-request context ("PSGI::Handy::Context") into a single PSGI-subset $app through to_app. Templates and a database handle are injected at construction time, so the framework loads nothing outside the Perl core and stays easy to test. You serve the resulting $app with any PSGI server, such as HTTP::Handy.
All four component classes are defined in this one file; there are no separate PSGI/Handy/*.pm files to install or to keep in version step. Their module names are still loadable (use PSGI::Handy::Router is a no-op once PSGI::Handy is loaded), and each is documented in its own section below so that perldoc PSGI::Handy covers the whole framework.
Every handler receives a context ("PSGI::Handy::Context") and may return a Response object, a raw PSGI array reference, or a plain string (treated as an HTML 200 response).
METHODS
These are the methods of the PSGI::Handy application object:
new, renderer, db, config, router, route, get, post, put, patch, del, head, any, before, after, to_app.
Routing details (named :params, trailing *, exact matching, 405 handling) are documented under "PSGI::Handy::Router".
DIAGNOSTICS
The framework dies (via Carp::croak) with these messages. They are collected here for the whole single-file distribution.
before: a code reference is required-
The argument to
beforewas not a CODE reference. after: a code reference is required-
The argument to
afterwas not a CODE reference. new: a PSGI env hash reference is required-
PSGI::Handy::Request::newwas called without a PSGI environment hash reference. add: method is required-
PSGI::Handy::Router::addwas called without an HTTP method. add: pattern is required-
PSGI::Handy::Router::addwas called without a path pattern. add: handler must be a code reference-
The handler passed to
PSGI::Handy::Router::addwas not a CODE reference. add: pattern must begin with '/' (got '$pattern')-
The path pattern passed to
PSGI::Handy::Router::adddid not begin with a slash. match: method is required-
PSGI::Handy::Router::matchwas called without an HTTP method. match: path is required-
PSGI::Handy::Router::matchwas called without a path. redirect: location is required-
PSGI::Handy::Response::redirectwas called without a target location. json: body must be a pre-encoded JSON string, not a reference-
PSGI::Handy::Response::jsonwas given a reference (array or hash). The body must be a JSON string that the caller has already encoded (for example withmb::JSON). -
PSGI::Handy::Response::cookiewas called without a cookie name. render: no renderer configured (pass renderer =... to PSGI::Handy->new)>-
renderwas called but no renderer was injected into the application. render: renderer must be a code reference or an object with a render() method-
The configured renderer is neither a CODE reference nor an object with a
rendermethod.
LIMITATIONS
The $app returned by to_app always produces the buffered, three-element PSGI response [ $status, \@headers, \@body ]. The PSGI delayed-response form (the streaming "responder" callback) is not generated; this is what "PSGI-subset" means throughout this distribution.
Concurrency and the HTTP version depend on the PSGI server you choose. No multipart uploads or WebSocket in this version. HEAD requests are served by the matching GET route with the body removed.
PSGI::Handy::Router
A tiny PSGI route dispatcher. It resolves an incoming request, expressed as an HTTP method and a PATH_INFO string, to a previously registered handler.
Pattern syntax
Literal segments match exactly. A dot is a literal dot, not a regular-expression wildcard (
/feed.xmldoes not match/feedaxml).:namematches a single non-empty path segment ([^/]+) and stores it inparamsundername.A
*used as the final segment matches the remainder of the path, including slashes, and is stored undersplat.
Matching is exact (anchored), so a trailing slash is significant: /a and /a/ are different routes.
Router methods
- new
-
Returns a new, empty router.
- add($method, $pattern, $handler)
-
Registers a route.
$handlermust be a code reference.$patternmust begin with/. Returns the router for chaining. - match($method, $path)
-
Returns a hash reference
{ handler => ..., params => ... }on success,{ allowed => [...] }when the path is known but the method is not (HTTP 405), orundefwhen nothing matched (HTTP 404). The first registered matching route wins. - routes
-
Returns the internal array reference of route records. For introspection and testing.
Path parameters rely on positional captures paired with a name list because named captures were not available until Perl 5.10.
PSGI::Handy::Request
A tiny PSGI environment wrapper. It provides convenient read access to the request. Query-string and application/x-www-form-urlencoded body parameters are parsed in pure Perl and merged (body values appended after query values); multi-value fields are available through param_all. multipart/form-data is not parsed in this version; use body for the raw payload.
Request methods
new, method, path, query_string, content_type, content_length, env, header, body, param, param_all, param_names, params, cookie, cookies.
PSGI::Handy::Response
A tiny PSGI response builder. finalize returns the PSGI three-element array a PSGI-subset server such as HTTP::Handy expects. Content-Length is computed from the body at finalize time unless the caller already set it. Bodies are expected to be byte strings already in the desired encoding; the class does no character encoding itself.
Response methods
new, html, text, json, redirect, status, set_status, body, set_body, header, set_header, remove_header, content_type, cookie, finalize. Mutators return the object for chaining; for example:
my $res = PSGI::Handy::Response->new;
$res->set_status(201)
->content_type('text/html; charset=utf-8')
->header('X-App', 'PSGI::Handy')
->cookie('sid', $id, path => '/', httponly => 1)
->set_body($html);
return $res->finalize; # [ 201, [...], [ $html ] ]
PSGI::Handy::Context
The per-request context handed to every route handler as its single argument. It exposes the request (req), matched path parameters (param, params), a per-request stash, the injected database handle (db), configuration (config), response builders (html, text, json, redirect, res), and template rendering (render).
Context methods
new, req, app, params, param, db, config, stash, html, text, json, redirect, res, render.
SEE ALSO
HTTP::Handy, HP::Handy, DB::Handy.
AUTHOR
INABA Hitoshi <ina.cpan@gmail.com>
COPYRIGHT AND LICENSE
This software is distributed under the same terms as Perl itself.