NAME

OX - the hardest working two letters in Perl

VERSION

version 0.14

SYNOPSIS

The following describes the outline of how a model-view-controller application might be configured as an OX application.

package MyApp;
use OX;

has model => (
    is        => 'ro',
    isa       => 'MyApp::Model',
    lifecycle => 'Singleton',
);

has template_root => (
    is     => 'ro',
    isa    => 'Str',
    value  => 'root',
);

has view => (
    is           => 'ro',
    isa          => 'Template',
    dependencies => {
        INCLUDE_PATH => 'template_root'
    },
);

has root => (
    is    => 'ro',
    isa   => 'MyApp::Controller',
    infer => 1,
);

router as {
    route '/'            => 'root.index';
    route '/inc'         => 'root.inc';
    route '/dec'         => 'root.dec';
    route '/reset'       => 'root.reset';
    route '/set/:number' => 'root.set' => (
        number => { isa => 'Int' },
    );
};

For a more comprehensive introduction, you should check out the 2012 OX advent calendar.

DESCRIPTION

OX is a web application framework based on Bread::Board, Path::Router, and PSGI. Bread::Board lets you build your application from a collection of normal Moose objects, organized together in a "container", which allows components to easily interoperate without any additional configuration. Path::Router maps incoming request paths to method calls on the objects in the Bread::Board container. Finally, at compile time, the framework turns your entire application into a simple PSGI coderef, which can be used directly by any PSGI-supporting web server.

The philosophy behind OX is that the building blocks of your web application should just "click" together, without the overhead of an additional plugin system or "glue" layer. The combination of Bread::Board, Path::Router, and the Moose object system provides all that is needed for requests to be mapped to methods and for components to communicate with each other. For example, all configuration information can be provided via roles applied to the application class (affecting application initialization). Similarly, additional runtime features can be added by providing your own request (sub)class.

Additionally, OX provides an easy-to-use "sugar" layer (based on Bread::Board::Declare) that makes writing a web application as easy as writing any Moose class. The OX sugar layer supports the full complement of Moose features (attributes, roles, and more), as well as addiitonal sugar methods for mapping request routes to object methods. (See Bread::Board::Declare, OX::Application::Role::Router::Path::Router, and OX::Application::Role::RouteBuilder for more detailed information.) You're also free to eschew the sugary syntax and build your application manually -- see OX::Application for more information on going that route.

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 'My::Custom::Router' => (
    foo => 'some_service',
);

router(My::Custom::Router->new(%router_args));

If you have declared a router manually elsewhere, you can pass in either the class name or the built router object to router instead of a block. It will be used directly in that case. If you pass a class name, it can take an optional hash of dependencies, which will be resolved and passed into the class's constructor as arguments. Note that parentheses are required if the argument is a literal constructor call, to avoid it being parsed as an indirect method call.

# myapp.psgi
use OX;
router as {
    route '/' => sub { "Hello world" };
};

This function (if called in non-void context) also returns the fully-built PSGI app coderef. This means that you can define an OX class in a .psgi file with the router block as the last statement in the file, and have it be valid.

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 main app. 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.

wrap

The wrap keyword declares a middleware to apply to the application. The wrap statements will be applied in order such that the first wrap statement corresponds to the outermost middleware (just like Plack::Builder).

wrap 'Plack::Middleware::Static' => (
    path => literal(sub { s{^/static/}{} }),
    root => 'static_root',
);

If you specify a class name as the middleware to apply, it will create an instance of the class (resolving the parameters as dependencies and passing them into the constructor) and call wrap on that instance, passing in the application coderef so far and using the result as the new application (this is the API provided by Plack::Middleware).

wrap(Plack::Middleware::StackTrace->new(force => 1));

If you specify an object as the middleware, it will call wrap on that object, passing in the application coderef so far and use the result as the new application. Note that parentheses are required if the argument is a literal constructor call, to avoid it being parsed as an indirect method call.

wrap sub {
    my $app = shift;
    return sub {
        my $env = shift;
        return [302, [Location => '/'], []]
            if $env->{PATH_INFO} eq '/';
        return $app->($env);
    };
};

If you specify a coderef as the middleware, it will call that coderef, passing in the application coderef so far, and use the result as the new application.

wrap_if

wrap_if works identically to wrap, except that it requires an additional initial coderef parameter for the condition under which this middleware should be applied. This condition will be run on every request, and will receive the $env hashref as a parameter, so the condition can depend on variables in the environment. For instance:

wrap_if sub { $_[0]->{REMOTE_ADDR} eq '127.0.0.1' },
    'Plack::Middleware::StackTrace' => (
        force => literal(1),
    );

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.

BUGS

No known bugs.

Please report any bugs to GitHub Issues at https://github.com/iinteractive/OX/issues.

SEE ALSO

Dancer, Catalyst, Web::Simple, Mojolicious for other web frameworks in Perl.

SUPPORT

The IRC channel for this project is #ox on irc.perl.org.

You can find this documentation for this module with the perldoc command.

perldoc OX

You can also look for information at:

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.