NAME
OX::Role - declare roles for your OX applications
VERSION
version 0.14
SYNOPSIS
package MyApp::Role::Auth;
use OX::Role;
has auth => (
is => 'ro',
isa => 'MyApp::Auth',
);
router as {
route '/auth/login' => 'auth.login';
route '/auth/logout' => 'auth.logout';
};
package MyApp;
use OX;
with 'MyApp::Role::Auth';
has root => (
is => 'ro',
isa => 'MyApp::Controller::Root',
);
router as {
route '/' => 'root.index';
};
DESCRIPTION
This module allows you to define roles to be applied to your OX applications. OX roles can define any part of the application that an OX class can, except for middleware and declaring a pre-built router or router class. When you consume the role, all of the services, routes, and mounts will be composed into the application class.
During composition, conflicts between mounts and routes will be checked for, similar to how roles normally detect conflicts between methods and attributes. If two mounts are declared with the same path, a conflict will be generated, and if two routes are declared with the same path (disregarding the names of variable path components), a conflict will also be generated. The consuming class can resolve these types of conflicts by declaring its own mount or route, respectively. If a route is declared which would be shadowed by a mount declared in another role, this generates an unresolvable conflict - you'll need to fix this in the roles themselves.
Note that since the router keyword doesn't happen at compile time, you should most likely put the with
statement for your application roles after the router
block.
FUNCTIONS
as
router as {
...
};
Sugar function for declaring coderefs.
router
router as {
...
};
This function declares the router for your application. By default, it creates a router based on Path::Router. Within the router
body, you can declare routes, middleware, and mounted applications using the route
, wrap
, and mount
keywords described below.
router ['My::Custom::RouteBuilder'] => as {
...
};
By default, actions specified with route
will be parsed by either OX::RouteBuilder::ControllerAction, OX::RouteBuilder::HTTPMethod, or OX::RouteBuilder::Code, whichever one matches the route. If you want to be able to specify routes in other ways, you can specify a list of OX::RouteBuilder classes as the first argument to router
, which will be used in place of the previously mentioned list.
router as {
route '/' => 'root.index';
mount '/admin' => router as {
wrap "MyApp::Middleware::Auth";
route '/' => 'admin.index';
};
};
In addition, router blocks handle nesting properly. If you declare a new router block inside of the main router block, it will allow you to define an entirely separate application which you can mount wherever you want (see mount
below). Nested routers will have full access to the services defined in the role. This can be used, for instance, to apply certain middleware to only parts of the application, or just to organize the application better.
Note that while they are being defined inline, these are still just normal mounts. This means that examining the path
in the request object will only give the path relative to the nested router (the remainder will be in script_name
).
route $path, $action_spec, %params
The route
keyword adds a route to the current router. It is only valid in a router
block. The first parameter to route
is the path for the route to match, the second is an action_spec
to be parsed by an OX::RouteBuilder class, and the remaining parameters are a hash of parameters containing either defaults or validations for the router to use when matching.
route '/' => 'controller.index';
This declares a simple route using the OX::RouteBuilder::ControllerAction route builder. When the application receives a request for /
, the application will resolve the controller
service, and call the index
method on it, passing in an OX::Request instance for the request. The index
method should return either a string, a PSGI response arrayref, or an object that responds to finalize
(probably a Web::Response object).
route '/view/:id' => 'posts.view', (
id => { isa => 'Int' },
name => 'view',
);
This declares a route with parameters. This will resolve the posts
service and call the view
method on it, passing in a request object and the value of id
. If id
was provided but was not an Int
, this route will not match at all. Inside the view
method, the mapping
method will return a hash of (controller => 'posts', action => 'view', id => $id, name => 'view')
.
Also, other parts of the application can call uri_for
with any unique subset of those parameters (such as (name => 'view', id => 1)
) to get the absolute URL path for this route (for instance, "/myapp/view/1"
if this app is mounted at /myapp
).
route '/method' => 'method_controller';
Since this action spec doesn't contain a .
, this will be handled by the OX::RouteBuilder::HTTPMethod route builder. If a user sends a GET
request to /method
, it will resolve the method_controller
service, and call the get
method on it, passing in the request object. Variable path components and defaults and validations work identically to the description above.
route '/get_path' => sub { my $r = shift; return $r->path };
This route will just call the given coderef directly, passing in the request object. Variable path components and defaults and validations work identically to the description above.
route '/custom' => $my_custom_thing;
In addition, if you specified any custom route builders in the router
description, you can pass anything that they can handle into the second argument here as well.
mount
The mount
keyword declares an entirely separate application to be mounted under a given path in your application's namespace. This is different from route
, because the targets are full applications, which handle the entire path namespace under the place they are mounted - they aren't just handlers for one specific path.
mount '/other_app' => 'My::Other::App', (
template_root => 'template_root',
);
If you specify a class name for the target, it will create an app by creating an instance of the class (resolving the parameters as dependencies and passing them into the constructor) and calling to_app
on that instance.
mount '/other_app' => My::Other::App->new;
If you specify an object as the target, it will create the app by calling to_app
on that object.
mount '/other_app' => sub {
my $env = shift;
return [ 200, [], [$env->{PATH_INFO}] ];
};
You can also specify a coderef directly. Note that in this case, unlike specifying a coderef as the route spec for the route
keyword, the coderef is a plain PSGI application, which receives an env hashref and returns a full PSGI response arrayref.
literal
wrap 'Plack::Middleware::Static', (
path => literal(qr{^/(images|js|css)/}),
root => 'static_root',
);
The literal
keyword allows you to declare dependencies on literal values, rather than services. This is useful for situations where the constructor values aren't user-configurable, but are inherent to your app's structure, such as the path
option to Plack::Middleware::Static, or the subrequest
option to Plack::Middleware::ErrorDocument.
AUTHORS
Stevan Little <stevan.little@iinteractive.com>
Jesse Luehrs <doy@tozt.net>
COPYRIGHT AND LICENSE
This software is copyright (c) 2014 by Infinity Interactive.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.