Model-View-Controller
for Raku.
MVC::Keayl is a Model-View-Controller web framework for Raku. Resourceful routing, controllers
with filters and strong parameters, HAML views, sessions, signed and encrypted cookies,
CSRF protection, background jobs, mailers, and caching. Conventional MVC patterns,
expressed in Raku idioms.
It is the web layer only. Models are delegated to ORM::ActiveRecord and view
rendering to Template::HAML, both pluggable. The HTTP server is reached through
an abstract adapter, with a Cro-based
default. Bring your own pieces, or take the stack whole.
Every layer of the request, in one framework.
From the routes file to the rendered response, from the cookie jar to the job queue, Keayl ships the whole web tier so you can skip the wiring and write features.
Resourceful routing
resources, namespace, scope, concern, constraints, redirects, mounts, and named URL helpers. One file maps every request.
Controllers & actions
One instance per request. Filters with before-action, strong parameters with permit, implicit render, and rescue-from for exceptions.
HAML views
Format and variant resolution, layouts, partials, collection and object partials, content-for / yield, and escape-by-default output safety.
View helpers
content-tag and link-to, asset and URL helpers, form-with and simple_form inputs, select builders, and text, number, and date formatting.
Security built in
CSRF tokens, signed and encrypted cookies, sessions, parameter filtering, SSL enforcement, host authorization, and secure headers.
Lock it down →Background jobs
perform-later with inline, async, test, and database queue adapters. GlobalID serialization, named queues, runnable without setup.
Mailers
Email built like a response: an action renders HAML into the parts of a multipart message, a delivery method sends it. Class-level defaults included.
Send a message →Caching & conditional GET
fresh-when and ETags, expires-in cache headers, a pluggable cache store with fetch, and fragment caching.
Content negotiation
respond-to serves HTML, JSON, and your own formats from one action, with device variants and an API controller base.
One routes file, the whole URL map.
Declare the seven REST routes with resources, nest them, scope them under a
namespace, and constrain them. Every named route generates a path and URL helper,
and recognition tells you whether a path was found, answered the wrong verb, or was unknown.
- Resourceful by default.
resources 'users'draws index, show, new, create, edit, update, destroy. - Composable scopes.
namespaceandscopeprefix the path, module, and helper name together or apart. - Dynamic segments.
:id,*glob, optional(.:format), and per-segment constraints. - URL helpers.
path-for,url-for, and polymorphic dispatch by record class and state.
use MVC::Keayl::Routing;
routes {
root to => 'home#index';
resources 'articles', {
resources 'comments';
}
namespace 'admin', {
resources 'users', :except<destroy>;
}
get '/search', to => 'search#run',
constraints => { q => /\w+/ };
}
Actions that read like the request.
A controller is a class, each public method an action. The framework builds one instance
per request, runs the before callbacks, dispatches the action, and renders. Strong
parameters whitelist input before it reaches the model, and rescue-from turns
exceptions into responses.
- Per-request state.
self.request,self.response, and mergedself.params. - Filters.
before-action,after-action,around-action, withonly/exceptscoping. - Strong parameters.
require,permit, and the strictexpect. - Implicit render. Return a string, or render JSON, plain, HTML, or a template.
use MVC::Keayl::Controller;
class ArticlesController is MVC::Keayl::Controller {
method authenticate
is before-action(except => <index show>) {
...
}
method index {
self.assign('articles', Article.all);
}
method create {
my $attrs = self.params
.require('article').permit('title', 'body');
my $article = Article.create($attrs);
self.redirect-to("/articles/{$article.id}");
}
}
Templates that compose.
Views resolve by name, format, and variant, render through a pluggable handler (HAML by
default), and cache the compiled result. Wrap a body in a layout, capture named content with
content-for, and break pages into partials rendered one-off, per object, or per
collection. Interpolated values are escaped by default.
-# app/views/articles/index.html.haml
%h1= $title
%ul.articles
!= partial-each('articles/row', $articles)
-# app/views/articles/_row.html.haml
%li
%a{href: "/articles/#{$row.id}"}= $row.title
%span.meta= $row.published-at
One command to install.
Pull MVC::Keayl from zef and draw your first route. The web layer is ready; add a controller and a view and you have a request cycle.