NAME
Punk::Plugin - base class for Punk plugins
SYNOPSIS
package Punk::Plugin::RequestId;
use parent 'Punk::Plugin';
my $rid = 0;
sub register {
my ($self, $app, $opts) = @_;
$app->helper(rid => sub { my ($c) = @_; $c->stash->{rid} });
$app->hook(before_dispatch => sub {
my ($c) = @_;
$c->stash->{rid} = ++$rid;
return;
});
}
1;
# in the app:
plugin 'RequestId';
DESCRIPTION
A plugin's register($plugin, $app, \%opts) runs at registration time and receives the same registrar surface the DSL keywords use - the Punk::App - so a plugin can do anything the app class can: add routes and under scopes, register view engines and model backends, hooks, middleware, on_error, and helpers.
Helpers registered with $app->helper(name => sub) become real methods on the application's context subclass, installed once at to_app - no AUTOLOAD, no per-request cost. A helper name that collides with a core Punk::Context method or another helper croaks at boot, naming both owners.
plugin 'Name' resolves to Punk::Plugin::Name; '+Full::Class' uses the class as written.
KEYWORDS OF YOUR OWN
A plugin that wants a declaration keyword - task, queue, cron - installs it with $app->install_kw rather than assigning to a glob in the application class:
$app->install_kw(task => sub {
my ($name, $target) = @_;
push @TASKS, [$name, $target];
return;
}, __PACKAGE__);
Punk installs it as a magic CV named for the class it lands in, beside the DSL's own keywords, and keeps it in the same registry: two plugins claiming one name croak naming both owners, and a core keyword cannot be installed over. Installing the same name twice from the same owner is a no-op, so a plugin with both an import and a register can install from both without remembering which ran.
A keyword must be installed before the line that uses it is compiled, or the bareword form (task 'x' => ...) will not parse - which means from the plugin's import, i.e. use My::Plugin in the app class. register runs at runtime, so a keyword installed there is only usable in its parenthesised form (task(...)) on later lines. These are ordinary compile-time-visible subs, not calls lifted into the BEGIN phase: an argument is evaluated when the line runs, as usual.
METHODS
new
Plain constructor; override freely.
register($app, \%opts)
Override this. Called once, at plugin time.
AUTHOR
LNATION <email@lnation.org>
LICENSE AND COPYRIGHT
This software is Copyright (c) 2026 by LNATION <email@lnation.org>.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)