Custom Route Builders
Route builders
As mentioned previously, route builders handle parsing the action spec to determine what code to run when a route is matched. Although OX comes with some default route builders, you can also write your own to handle other kinds of action specs.
A note beforehand: this API is a lot less finalized and more experimental than many of the other parts discussed so far, and may change in the future. It is a useful piece of functionality however, and still worth discussing even if the details may change later.
OX::RouteBuilder
A route builder is implemented via a class which consumes the OX::RouteBuilder role. This role requires two methods:
parse_action_spec
This is a class method which is called when OX attempts to parse an action spec. If the action spec should be handled by this route builder, this method should return a value containing the parsed data (to be used later). Otherwise, it should return
undef
. OX will call theparse_action_spec
method on all enabled route builders to find the one that doesn't returnundef
, and that one will be instantiated with the route data.compile_routes
This method is called on the instantiated route builder, and is used to turn the route description into actual route data. You can access the route data via the
path
,route_spec
, andparams
attributes.path
is the path given as the first argument of theroute
statement,route_spec
is the value thatparse_action_spec
returned, andparams
is a hashref of the parameters given at the end of the route statement. The parameters can be split into defaults and validations by calling theextract_defaults_and_validations
method. Thecompile_routes
method should return a hashref containing keys forpath
,target
,defaults
, andvalidations
.
This is perhaps more clear with an example. Here is a route builder which parses action specs of the form:
1: | { |
In other words, a hashref mapping HTTP methods to code to run for those methods.
1: | package MyApp::RouteBuilder::HTTPMethodCode; |
So going through this a piece at a time, we first have the parse_action_spec
method. This will match any action spec which is a hashref, as in the case of a route that looks like
1: | route '/posts' => { GET => sub { ... }, POST => sub { ... } }; |
It returns the action spec as-is to be used later, since nothing about it needs to be parsed out.
Next, we have the compile_routes
method. The first thing it does is extract the defaults and validations from the given params. Validations are params whose values are hashrefs with an isa
key, and defaults are params whose values are strings. If you want to add any additional defaults, you can also do that at this point (adding a value for name
is often a useful thing to do in cases where there is a natural default to use for it, since it will make using uri_for
easier, as described in a previous article).
It then creates the target coderef. This is the code which will be called as the action for the route. It receives an OX::Request object as its only parameter, and returns some kind of response. Here, it looks at the request to see what HTTP method it was given, and sees if there is a corresponding action to use for it. If there is, it calls that action with the request object. If there isn't, it looks for a key of '*'
as a fallback. If that doesn't exist either, it returns a valid 405 Method Not Allowed
error response.
Finally, it returns the hashref containing all of the data it has assembled.
Using custom route builders
So now we have our custom route builder, but how do we use it? By default if nothing is specified, OX uses the OX::RouteBuilder::Code, OX::RouteBuilder::ControllerAction, and OX::RouteBuilder::HTTPMethod route builders. To specify an alternate set of route builders to use, you provide them in an arrayref as the first argument to the router
block:
1: | router [ |
Note that this replaces the default list rather than appending to it. This way, you can provide your own interpretation of things that the default route builders would otherwise handle, without things getting confused.
Nested routers
In the case of nested routers, the default set of route builders is taken from the enclosing router if nothing is specified, rather than always using the base default. For instance:
1: | router [ |
Here, the second route
statement works properly, because the router mounted at /posts
uses the same list of route builders as its parent.