NAME
Mojolicious::Plugin::Restify - Route shortcuts & helpers for REST collections
SYNOPSIS
# Mojolicious example (Mojolicious::Lite isn't supported)
package MyApp;
use Mojo::Base 'Mojolicious';
sub startup {
my $self = shift;
# imports the `collection' route shortcut and `restify' helpers
$self->plugin('Restify');
# add REST collection endpoints manually
my $r = $self->routes;
my $accounts = $r->collection('accounts'); # /accounts
$accounts->collection('invoices'); # /accounts/:accounts_id/invoices
# or add the equivalent REST routes with an ARRAYREF (the helper will
# create chained routes from the path 'accounts/invoices' so you don't need
# to set ['accounts', 'accounts/invoices'])
my $r = $self->routes;
$self->restify->routes($r, ['accounts/invoices']);
# or add the equivalent REST routes with a HASHREF (might be easier to
# visualise how collections are chained together)
my $r = $self->routes;
$self->restify->routes($r, {
accounts => {
invoices => undef
}
});
}
Next create your controller for accounts.
# Restify controller depicting the REST actions for the /accounts collection.
# (The name of the controller is the Mojo::Util::camelized version of the
# collection path.)
package MyApp::Controller::Accounts;
use Mojo::Base 'Mojolicious::Controller';
sub resource_lookup {
my $c = shift;
# To consistenly get the element's ID relative to the resource_lookup
# action, use the helper as shown below. If you need to access an element ID
# from a collection further up the chain, you can access it from the stash.
#
# The naming convention is the name of the collection appended with '_id'.
# E.g., $c->stash('accounts_id').
my $account = your_lookup_account_resource_func($c->restify->current_id);
# By stashing the $account here, it will now be available in the delete,
# read, patch, and update actions. This resource_lookup action is optional,
# but added to every collection by default to help reduce your code.
$c->stash(account => $account);
# must return a positive value to continue the dispatch chain
return 1 if $account;
# inform the end user that this specific resource does not exist
$c->reply->not_found and return 0;
}
sub create { ... }
sub delete { ... }
sub list { ... }
sub read {
my $c = shift;
# account was placed in the stash in the resource_lookup action
$c->render(json => $c->stash('account'));
}
sub patch { ... }
sub update { ... }
1;
DESCRIPTION
Mojolicious::Plugin::Restify is a Mojolicious::Plugin. It simplifies generating all of the Mojolicious::Routes for a typical REST collection endpoint (e.g., /accounts
or /invoices
) and maps the common HTTP verbs (DELETE
, GET
, PATCH
, POST
, PUT
) to underlying controller class methods.
For example, creating a collection called /accounts
would create the routes as shown below. N.B. The over
option in the example below corresponds to the name of a route condition. See "conditions" in Mojolicious::Routes.
# The collection route shortcut below creates the following routes, and maps
# them to controllers of the camelized route's name.
#
# Pattern Methods Name Class::Method Name
# ------- ------- ---- ------------------
# /accounts * accounts
# +/ GET "accounts_list" Accounts::list
# +/ POST "accounts_create" Accounts::create
# +/:accounts_id * "accounts"
# +/ * "accounts_resource_lookup" Accounts::resource_lookup
# +/ DELETE "accounts_delete" Accounts::delete
# +/ GET "accounts_read" Accounts::read
# +/ PATCH "accounts_patch" Accounts::patch
# +/ PUT "accounts_update" Accounts::update
# expects the element id (:accounts_id) for this collection to be a uuid
my $route = $r->collection('accounts', over => 'uuid');
Mojolicious::Plugin::Restify tries not to make too many assumptions, but the author's recent experience writing a REST-based API using Mojolicious has helped shaped this plugin, and might unwittingly express some of his bias.
HELPERS
Mojolicious::Plugin::Restify implements the following helpers.
restify->current_id
my $id = $c->restify->current_id;
Returns the element id at the current point in the dispatch chain.
This is the only way to guarantee the correct element's resource ID in a Mojolicious::Plugin::Restify action. The resource_lookup
action, which is added by default in both "collection" and "restify-routes", is added at different positions of the dispatch chain. As such, the router might not have added the value of any placeholders to the "stash" in Mojolicious::Controller yet.
restify->routes
This helper is a wrapper around the "collection" route shortcut. It facilitates creating REST collections using either an ARRAYREF
or HASHREF
.
It takes a Mojolicious::Routes object, the paths to create, and optionally options which are passed to the "collection" route shortcut.
# /accounts
# /accounts/1234
$self->restify->routes($self->routes, ['accounts'], {over => 'int'});
# /invoices
# /invoices/76be1f53-8363-4ac6-bd83-8b49e07b519c
$self->restify->routes($self->routes, ['invoices'], {over => 'uuid'});
Maybe you want to chain them.
# /accounts
# /accounts/1234
# /accounts/1234/invoices
# /accounts/1234/invoices/76be1f53-8363-4ac6-bd83-8b49e07b519c
$self->restify->routes(
$self->routes,
['accounts', ['accounts/invoices' => {over => 'uuid'}]],
{over => 'int'}
);
- ARRAYREF
-
Using the elements of the array, invokes "collection", passing any route- specific options.
It will automatically create and chain parent routes if you pass a full path e.g.,
['a/very/long/path']
. This is equivalent to the shell commandmkdir -p
.my $restify_routes = [ # /area-codes # /area-codes/:area_codes_id/numbers 'area-codes/numbers', # /news 'news', # /payments ['payments' => {over => 'int'}], # overrides default uuid route condition # /users # /users/:users_id/messages # /users/:users_id/messages/:messages_id/recipients 'users/messages/recipients', ]; $self->restify->routes($self->routes, $restify_routes, {over => 'uuid'});
In its most basic form,
REST
routes are created from aSCALAR
.# /accounts my $restify_routes = ['accounts'];
- HASHREF
-
Using the key/values of the hash, invokes "collection", passing any route- specific options.
It automatically chains routes to each parent, and progressively builds a namespace as it traverses through each key.
N.B., This was implemented before the
ARRAYREF
version, and is arguably a bit more confusing. It might be dropped in a later version to simplify the API.my $restify_routes = { # /area-codes # /area-codes/:area_codes_id/numbers 'area-codes' => { 'numbers' => undef }, # /news 'news' => undef, # /payments 'payments' => [undef, {over => 'int'}], # overrides default uuid route condition # /users # /users/:users_id/messages # /users/:users_id/messages/:messages_id/recipients 'users' => { 'messages' => { 'recipients' => undef } }, }; $self->restify->routes($self->routes, $restify_routes, {over => 'uuid'});
METHODS
Mojolicious::Plugin::Restify inherits all methods from Mojolicious::Plugin and implements the following new ones.
register
$plugin->register(Mojolicious->new);
Register plugin in Mojolicious application.
ROUTE CONDITIONS
Mojolicious::Plugin::Restify implements the following route conditions. These conditions can be used with the over
option in the "collection" shortcut.
Checks are made for the existence of the int
, standard
and uuid
conditions before adding them. This allows you to replace them with your own conditions of the same name by creating them before registering this plugin.
See "Adding-conditions" in Mojolicious::Guides::Routing to add your own.
int
# /numbers/1 # GOOD
# /numbers/0 # GOOD
# /numbers/one # BAD
# /numbers/-1 # BAD
# /numbers/0.114 # BAD (the standard :placeholder notation doesn't allow a '.')
my $r = $self->routes;
$r->collection('numbers', over => 'int');
A Mojolicious route condition (see "conditions" in Mojolicious::Routes) which restricts a route's collection's element id to whole positive integers which are >= 0
.
standard
my $r = $self->routes;
$r->collection('numbers', over => 'standard');
A collection's element resource ID is captured using "Standard-placeholders" in Mojolicious::Guides::Routing. This route condition allows everything the standard placeholder allows, which is similar to the regular expression ([^/.]+)
.
This is the default over option for a "collection".
uuid
# /uuids/8ebef0d0-d6cf-11e4-8830-0800200c9a66 GOOD
# /uuids/8EBEF0D0-D6CF-11E4-8830-0800200C9A66 GOOD
# /uuids/8ebef0d0d6cf11e488300800200c9a66 GOOD
# /uuids/malformed-uuid BAD
my $r = $self->routes;
$r->collection('uuids', over => 'uuid');
A Mojolicious route condition (see "conditions" in Mojolicious::Routes) which restricts a route's collection's element id to UUIDs only (with or without the separating hyphens).
ROUTE SHORTCUTS
Mojolicious::Plugin::Restify implements the following route shortcuts.
collection
my $r = $self->routes;
$r->collection('accounts');
$r->collection('accounts', collection_method_map => {delete => 'delete_collection'});
$r->collection('accounts', controller => 'differentmodule');
$r->collection('accounts', element => 0);
$r->collection('accounts', element_method_map => {get => 'read'});
$r->collection('accounts', over => 'uuid');
$r->collection('accounts', placeholder => '*');
$r->collection('accounts', prefix => 'v1');
$r->collection('accounts', resource_lookup => '0');
A Mojolicious route shortcut which helps create the most common REST routes for a collection endpoint and its associated element.
A collection endpoint (e.g., /accounts
) supports list (GET
) and create (POST
) actions. The collection's element (e.g., /accounts/:accounts_id
) supports delete (DELETE
), read (GET
), patch (PATCH
), and update (PUT
) actions.
By default, every HTTP request to a collection's element is routed through a resource_lookup
action (see "under" in Mojolicious::Routes::Route). This helps reduce the process of looking up a collection's resource to a single location. See "SYNOPSIS" for an example of its use.
options
The following options allow a collection to be fine-tuned.
- collection_method_map
-
$r->collection( 'invoices', { collection_method_map => { get => 'list', post => 'create', # delete => 'delete_collection', # delete all-the-things! # put => 'update_collection' # update all-the-things! } } );
The above represents the default HTTP method mappings for
collections
. It's possible to change the mappings globally (when importing the plugin) or per collection (as above).These HTTP method mappings only apply to the
collection
. e.g.,/invoices
. Please seeelement_method_map
if you want to apply different HTTP mappings to anelement
like/invoices/:id
. - controller
-
# collection doesn't build a namespace for subroutes by default my $accounts = $r->collection('accounts'); # MyApp::Controller::Accounts $accounts->collection('invoices'); # MyApp::Controller::Invoices # collection can build namespaces, but can be difficult to keep track of. Use # the restify helper if namespaces are important to you. # # MyApp::Controller::Accounts my $accounts = $r->collection('accounts'); # MyApp::Controller::Accounts::Invoices my $invoices = $accounts->collection('invoices', controller => 'accounts'); # MyApp::Controller::Accounts::Invoices::Foo $invoices->collection('foo', controller => 'accounts-invoices');
Prepends the controller name (which is automatically generated based on the path name) with this option value if present. Used internally by "restify" to build a perlish namespace from the paths. "collection" does not build a namespace by default.
- element
-
# GET,POST /messages 200 # DELETE,GET,PATCH,PUT,UPDATE /messages/1 200 $r->collection('messages'); # element routes are created by default # GET,POST /messages 200 # DELETE,GET,PATCH,PUT,UPDATE /messages/1 404 $r->collection('messages', element => 0);
Enables or disables chaining an element to the collection. Disabling the element portion of a collection means that only the create and list actions will be created.
- element_method_map
-
$r->collection( 'invoices', { element_method_map => { 'delete' => 'delete', 'get' => 'read', 'patch' => 'patch', 'put' => 'update', } } );
The above represents the default HTTP method mappings. It's possible to change the mappings globally (when importing the plugin) or per collection (as above).
These HTTP method mappings only apply to the
collection
'selement
. e.g.,/invoices/:id
. - over
-
$r->collection('invoices', over => 'int'); $r->collection('invoices', over => 'standard'); $r->collection('accounts', over => 'uuid');
Allows a collection's element to be restricted to a specific data type using Mojolicious' route conditions. "int", "standard" and "uuid" are added automatically if they don't already exist.
- placeholder
-
# /versions/:versions_id { versions_id => '123'} # /versions/#versions_id { versions_id => '123.00'} # /versions/*versions_id { versions_id => '123.00/1'}
The placeholder is used to capture the element id within a route. It can be one of
standard ':'
,relaxed '#'
orwildcard '*'
. You might need to adjust the placholder option in certain scenarios, but thestandard
placeholder should suffice for most normal REST endpoints.$r->collection('/messages', placeholder => ':');
Elements are chained to a collection using the standard placeholder by default. They match all characters except
/
and.
. See "Standard-placeholders" in Mojolicious::Guides::Routing.$r->collection('/relaxed-messages', placeholder => '#');
Placeholders can be relaxed, matching all characters expect
/
. Useful if you need to capture a domain name within a route. See "Relaxed-placeholders" in Mojolicious::Guides::Routing.$r->collection('/wildcard-messages', placeholder => '*');
Or they can be greedy, matching everything, inclusive of
/
and.
. Useful if you need to capture everything within a route. See "Wildcard-placeholders" in Mojolicious::Guides::Routing. - prefix
-
# without a prefix $r->collection('invoices'); say $c->url_for('invoices', invoices_id => 1); # with a prefix $r->collection('invoices', prefix => 'v1'); say $c->url_for('v1_invoices', invoices_id => 1);
Adds a prefix to the automatically generated route name for each collection and element action.
- resource_lookup
-
$r->collection('nolookup', resource_lookup => 0);
Enables or disables adding a
resource_lookup
action to the element of the collection.
element
my $r = $self->routes;
my $news = $r->get('/news')->to('foo#news');
$news->element('news');
A Mojolicious route shortcut called internally by "collection" to add the element routes to a collection. You shouldn't need to call this shortcut directly.
When an element is added to a collection's route, the resource ID is captured using a standard placeholder by default.
CREDITS
In alphabetical order:
Castaway
Dragoș-Robert Neagu
Toratora
COPYRIGHT AND LICENSE
Copyright (C) 2015-2017, Paul Williams.
This program is free software, you can redistribute it and/or modify it under the terms of the Artistic License version 2.0.
AUTHOR
Paul Williams <kwakwa@cpan.org>
SEE ALSO
Mojolicious, Mojolicious::Plugin::REST, Mojolicious::Plugin::RESTRoutes.