2012 Feed

Under the Hood

OX - 2012-12-19

OX::Application

Although OX provides a useful sugar layer for writing your applications, it is really just a thin layer on top of an extensible internal structure. Much like Moose and Bread::Board, learning how the internals work can open up a lot more power to your applications.

Bread::Board::Container

An OX application is, fittingly, an instance of OX::Application. OX::Application is just a Bread::Board container class with a service named App, representing the final PSGI application coderef (in fact, $app->to_app is just a wrapper around $app->resolve(service => 'App')). This means that you can do anything with your OX applications that you would with any other Bread::Board container, such as include them in other Bread::Board containers, if your app is larger than just a web application.

The App service is a block injection service, whose body just calls the build_app method on the application object itself. This means that defining your application is as simple as writing a build_app method. The App service also gets its dependencies via the app_dependencies method. The service itself, in Bread::Board syntax, basically looks like this:


1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
10: 
11: 
12: 

 

service App => (
    block => sub {
        my $s = shift;
        my $app = $self->build_app($s);
        # apply middleware to $app using $s->param('Middleware')
        return $app
    },
    dependencies => {
        Middleware => 'Middleware',
        %{ $self->app_dependencies },
    }
);

 

As you can see, this supports declaring middleware via an additional Middleware service. This service is expected to create an arrayref of middleware, and is created similarly to the App service, via build_middleware and middleware_dependencies methods.

Features added by the OX sugar

In addition, the OX sugar provides a default implementation of build_app that uses an additional Router service. The Router service again is created via methods on the application class, via build_router and router_dependencies. Since the router is an object, creating the object itself is usually always done the same way, the only difference being the routes that are added to it. To make this process easier, the Router service is created slightly differently - build_router has a default implementation that constructs a router object using the router_class method and the dependencies specified in router_dependencies, and then it calls configure_router, passing the constructed router object in.

The default build_app implementation then calls the app_from_router method to turn the router object into a PSGI application. It also replaces the app with a Plack::App::URLMap which has the initial app mounted at '/' if any mounts were declared.

Roles as plugins

The upshot to this is, OX has no explicit plugin interface. Most plugin-like behavior can already be provided via middleware (as we've already seen), and anything that can't be done that way can instead be done by just writing normal roles to be applied to the application class.

For instance, if you (for some reason) want to interpret your requests and responses as Windows-1252 instead of UTF-8, you can do this:


1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 

 

package MyApp::Request;
use Moose;

extends 'OX::Request';

sub default_encoding { 'Windows-1252' }

package MyApp::Role::Windows1252;
use Moose::Role;

sub request_class { 'MyApp::Request' }

package MyApp;
use OX;

with 'MyApp::Role::Windows1252';

# ...

 

Here, we override the request class for the application to use our custom request class, which has a different value for default_encoding. Any of the other methods mentioned in this article can also be overridden in similar ways. See the documentation for OX::Application for more details, as well as OX::Application::Role::Router and OX::Application::Role::Request which are applied to your class by default when you use OX.

Gravatar Image This article contributed by: Jesse Luehrs <jesse.luehrs@iinteractive.com>