NAME
Punk::App - the per-application registry and boot compiler
DESCRIPTION
use Punk; creates one of these per application class. The DSL keywords and plugins record into it; to_app calls "compile", which resolves every target string, flattens guard chains, freezes the route tables and returns the PSGI coderef. Everything wrong croaks at boot; the returned closure is the only code on the request path.
THE REGISTRAR SURFACE
Plugins receive this object; each method mirrors a DSL keyword: route, under, api, docs, static, mount, websocket, sse, session, logging, views, database, model_class, hook, middleware, on_error, on_not_found, helper, plugin, config, secret, host, favicon.
$app->host with no argument reads the declared origin back (undef when the application never declared one), which is how a plugin defaults its own base-URL option; a plugin supporting older Punk should guard with $app->can('host'). install_kw gives a plugin a keyword of its own. model_auto toggles auto-discovery of MyApp::Model::* (on unless models are named explicitly). caller_class and config_object give a plugin the app's controller namespace and its Punk::Config; new and the compile-time helpers (compile, model_instance, render_view) are called by the framework, not apps.
helper
$app->helper(rid => sub { my ($c, @args) = @_; ... });
Installed as a real method on the application's context subclass at compile time. Collisions with core context methods or another helper croak, naming both owners.
env
The application environment, resolved once at compile: the loaded config's env when there is one, else PUNK_ENV, else production - the same safe default Punk::Config and punk use. Development is opted into: punk dev sets it for its server, or set PUNK_ENV=development yourself.
compile_extras
A boot hook: compile calls it just before the compiled state freezes, after the router and hooks are assembled. The framework's own extras live here (the Punk::DevError wiring in development), so a subclass that overrides it must call SUPER::compile_extras.
install_kw
$app->install_kw(task => sub { my ($name, $target) = @_; ... },
__PACKAGE__);
Installs a declaration keyword into the application class - how a plugin adds to the DSL without assigning to a glob. The keyword is a magic CV named for the class it lands in; it forwards its arguments to the code and returns what the code returns, in the caller's context.
Installing over a core keyword croaks. Two owners claiming one name croak, naming both, as helpers do; the same owner installing twice is a no-op, which is what a plugin that installs from both import and register needs. Chains. See "KEYWORDS OF YOUR OWN" in Punk::Plugin.
log
The application Punk::Logger (cached on the app), for logging outside a request - startup, background work: $app->log->info(...). Its lines have no method or path. See Punk::Logger and the logging keyword.
COMPILE
compile
Freezes the configuration and returns the PSGI app. Dispatch order: before_request hooks (when any are registered - they run before anything is matched, so they are the only phase a 404, a 405 or a mount reaches), static table, PSGI/static-file mounts (longest prefix first), dynamic buckets, then 404/405. Matched requests construct the context, run before_dispatch hooks and the route's frozen guard chain (a reference return short-circuits), call the handler, and coerce the return value:
a PSGI triplet passes through untouched;
a Punk::Response is finalized;
a Future is chained on
psgi.nonblockingservers (the server awaits it) and awaited inline on blocking ones;anything else is JSON-encoded as
200 application/json, folding in any status/headers set through the context;a die runs
on_error, then answers500 {"errors":[{"message":...}]}.
"Construct the context" happens once per request, not once per phase: when a before_request hook has already built one, the routed match is stored into it rather than a second context being made, so a stash written before routing is the same stash the handler reads.
after_dispatch hooks see the finalized triplet (mutate it, or return a replacement); HEAD responses are stripped of their body.
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)