Plugins
A plugin packages behaviour an application opts into with one line:
plugin 'RequestId';
plugin '+My::Plugin' => { opt => 1 };
A name resolves against Punk::Plugin::; the + prefix takes the
class literally. The plugin's register receives the application
registrar and its options, and wires whatever it needs - routes, hooks,
helpers, keywords.
Helpers
The lightest extension is a context helper - a new method every handler can call:
helper uid => sub { my ($c) = @_; $c->stash->{uid} };
# later, anywhere
my $uid = $c->uid;
Plugins usually install these from register.
Hooks
hook before_dispatch => sub { my ($c) = @_; ...; return };
hook after_dispatch => sub { my ($c, $resp) = @_; ... };
before_dispatch runs after routing, before guards; a reference return
short-circuits the request. after_dispatch sees the finalized PSGI
triplet and may mutate or replace it. A request-id plugin is ten lines:
stamp the context in before_dispatch, stamp the header in
after_dispatch.
Middleware
For behaviour that must wrap the whole PSGI app - compression, raw timing - there is the outer layer:
middleware sub {
my ($app) = @_;
sub { my ($env) = @_; ...; $app->($env) };
};
Keywords of your own
A plugin can install declaration keywords into the application class, so its surface reads like the core DSL:
$app->install_kw(queue => sub { ... });
Punk installs the keyword from C beside its own: installing over a core
keyword croaks, two owners claiming one name croak naming both, and the
same owner installing twice is a no-op. The keyword forwards its
arguments and returns in the caller's context - my $q = queue(); is a
keyword call like any other.
This is how Punk::Queue gives an application
queue, task and cron that look and behave exactly like get and
under:
use Punk::Plugin::Queue; # compile time: the keywords exist
plugin 'Queue' => { dsn => ... }; # runtime: the configuration
task 'mail.send' => 'Job::Mail#send';
What a plugin sees
register receives the Punk::App registrar - the same surface the
keywords write to - so a plugin can declare routes, guards, mounts,
helpers, hooks and keywords with no privileged API. If the application
class could write it, the plugin can.