The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Mojolicious::Plugin::Routes::Restful - A plugin to generate Routes and a RESTful API for those routes, or just routes or just a RESTful API.

VERSION

Version 0.04

SYNOPSIS

In your Mojo App:

  package RoutesRestful;
  use Mojo::Base 'Mojolicious';

  sub startup {
    my $self = shift;
    my $r = $self->plugin( "Routes::Restful", => {
                   Config => { NAMESPACES => ['Controller'] },
                   PARENT => {
                     project => {
                       API   => {
                         VERBS => {
                           CREATE   => 1,
                           UPDATE   => 1,
                           RETRIEVE => 1,
                           DELETE   => 1
                         },
                       },
                       INLINE => {
                         detail => {
                           API => { 
                           VERBS => { RETRIEVE => 1 } }
                         },
                       },
                       CHILD => {
                         user => {
                           API => {
                             VERBS => {
                               CREATE   => 1,
                               RETRIEVE => 1,
                               UPDATE   => 1,
                               DELETE   => 1
                             }
                           }
                         }
                       }
                     }
                   } 
                 );
          
    }
    1;
    

And, presto, the following content routes are created

  +--------+-----------------------------+-----+-------------------+
  |  Type  |    Route                    | Via | Controller#Action |
  +--------+-----------------------------+-----+-------------------+ 
  | Parent | /project                    | GET | project#show      |
  | Parent | /project/:id                | GET | project#show      |
  | Inline | /project/:id/detail         | GET | project#detail    |
  | Child  | /project/:id/user           | GET | project#user      |
  | Child  | /project/:id/user/:child_id | GET | project#user      |
  +--------+-----------------------------+-----+-------------------+

and the following RESTful API routes

  +--------+-------------------------------+--------+----------------------------------+
  |  Type  |       Route                   | Via    | Controller#Action                |
  +--------+-------------------------------+--------+----------------------------------+
  | Parent | /projects                     | GET    | api-projects#get                 |
  | Parent | /projects/:id                 | GET    | api-projects#get                 |
  | Parent | /projects                     | POST   | api-projects#create              |
  | Parent | /projects/:id                 | PATCH  | api-projects#update              |
  | Parent | /projects/:id                 | DELETE | api-projects#delete              |
  | Inline | /projects/:id/details         | GET    | api-projects#details             |
  | Child  | /projects/:id/users           | GET    | api-projects#users               |
  | Child  | /projects/:id/users/:child_id | GET    | api-users#get parent=projects    |
  | Child  | /projects/:id/users           | POST   | api-users#create parent=projects |
  | Child  | /projects/:id/users/:child_id | PATCH  | api-users#update parent=projects |
  | Child  | /projects/:id/users/:child_id | DELETE | api-users#delete parent=projects |
  +--------+-------------------------------+--------+----------------------------------+

DESCRIPTION

Mojolicious::Plugin::Routes::Restful is a Mojolicious::Plugin that provides a highly configurable route generator for your Mojo App. Simply drop the plugin at the top of your start class with a config hash and you have your routes for your system.

METHODS

Well, none! Like the 'Box Factory' it only generates routes to put in your app.

Notes on Mojo Routes and Routes in General

If you know all about routes, just skip to the next section; otherwise, take a few minutes to go over the basic concepts this doc will use. If you are not fully familiar with Mojolicious::Guides::Routing have a look at Mojolicious::Guides::Routing for some info.

Route

The URL pattern that you are opening up that leads to a 'sub' in a controller 'class' which returns some content from the system.

Action

The 'sub' in the '.pm' file (class) that the route will invoke.

Controller

This is the '.pm' file (class) file that a route will use to find its action 'sub' in.

Parent or Child Resource

The named part of a route. Given this route

  /project/:id/user/:child_id
  

The parent resource is 'project' and the child is 'user', with the parent ':id' identifier between the two and the ':child_id' identifier at the end. Usually just referred to as a resource.

id: and child_id: Identifiers

The part of a route that identifies a single resource. 99.9872% of the time it is an number but it could be anything.

RESTFul Resources

RESTful APIs should always use the plural form of a noun for parent and child resources and a number as an identifier. Normally, RESTful resources point to data and not content. As well, a resource should not used to filter the data.

Resource Entity

The end content that a route will return in response to a request. Can be any form of content.

RESTful Entity

This usually means a specific block of data that is stored someplace that a route will return. In RESTful resources, there is an expectation of certain entity result from a route with a given HTTP verb. The table below lists out the expected results of a well designed RESTFul API and is the pattern that this Plugin enforces.

  +-----------------------+--------+---------------------+-----------------------------------+
  | Route                 | Via    |  Entity Type        |  Example of Result                |
  +-----------------------+--------+---------------------+-----------------------------------+
  | /projects             | GET    | Collection          | My Projects                       |
  | /projects/22          | GET    | Singleton           | Project #22                       |
  | /projects/22/users    | GET    | Collection          | Users in Project #22              |
  | /projects/22/users/44 | GET    | Singleton           | Project User #44                  |
  | /projects             | POST   | Add a Singleton     | New Project #42                   |
  | /projects/22/users    | POST   | Add a Singleton     | New User #44 added to Project #22 |
  | /projects/22          | PUT    | Replace a Singleton | Project #22 replaced              |
  | /projects/22/users/44 | PUT    | Replace a Singleton | User #44 replaced                 |
  | /projects/22          | PATCH  | Update a Singleton  | Project #22 Updated               |
  | /projects/22/users/44 | PATCH  | Update a Singleton  | Project User #44 Updated          |
  | /projects/22          | DELETE | Delete a Singleton  | Project #22 Deleted               |
  | /projects/22/users/44 | DELETE | Singleton           | Project User #44 Deleted          |
  +-----------------------+--------+---------------------+-----------------------------------+
  

You might notice that collection DELELTE, PUT and PATCH is not present. This is by design.

HTTP Via Verbs

In the good old days we only had two of these: 'GET' and 'POST'. Now it seems a new one comes out every month. In this doc we simply use the term Via for this in most places.

CONFIGURATION

You define which routes and the behaviour of your routes with a simple config hash in the startup section of your app. The plugin returns the route object it created so you will have it if you need it for other operations, such as add in a bunch of collective DELETE routes.

CONFIG

This controls the global settings.

NAMESPACES

Used to hold the namespaces for all routes you generate. Does the same thing as

    $r->namespaces(['MyApp::MyController','MyApp::::Controller::Ipa::Projects']);
    

It must be an array ref of module Class names as they would appear in a 'use' statement. These are important, as your app may not find your class if its namespace do not appear here.

ROUTE

With this optional attribute you can pass in a route that will have all the generated as its children. This is useful if you want to use a bridge or callback on all your generated routes.

Resource Types PARENT, CHILD, INLINE

There is nothing in Mojolicious stopping you from creating a big goofy chained resource like 'all/the/bad/code/perl/catalyst'; if that is what you want, then find yourself another Plugin. This Plugin enforces a simple two tier model, with as many 1st level 'PARENT' resources as you like, and under each you can have as many 2nd level 'INLINE' and 'Child' types you want.

Parent and Child in the Stash

INLINE and CHILD routes always have the values of parent and child in the stash as well as id and child_id if available.

Resource Attributes

All three of resource types have Attributes that allow you to customize your routes to some degree. Most of the attributes are common to all three and some only apply to certain types. They are all optional.

API Attribute

All types can have the API attribute which is used to define your RESTful routes. The idea being that if you are creating a route to content you may want a RESTful 'route' to access data for that content, so you might as well do it at the same time as the route.

DEBUG Attribute

You can add in the DEBUG attribute at the any attribute level to get the info on the route that is being generated.

PARENT Resource Type

By default, a Parent resource uses its key as the resource and controller name, 'show' as the action and GET as the HTTP verb.

So given this hash

  PARENT => {
            project => {},
            user    => {}
          }

these routes would be created

  +--------+-----------------------------+-----+-------------------+
  |  Type  |    Route                    | Via | Controller#Action |
  +--------+-----------------------------+-----+-------------------+ 
  | Parent | /project                    | GET | project#show      |
  | Parent | /project/:id                | GET | project#show      |
  | Parent | /user                       | GET | user#show         |
  | Parent | /user/:id                   | GET | user#show         |
  +--------+-----------------------------+-----+-------------------+

PARENT Attributes

The world is a complex place and there is never a simple solution that covers all the bases, so this plugin includes a number of attributes that you can use to customize your routes to suite your site's needs.

ACTION

You can override the default 'show' action by with this attribute, so

  PARENT => {
            project => {ACTION=>'list'},
          }

would get you

  +--------+-----------------------------+-----+-------------------+
  |  Type  |    Route                    | Via | Controller#Action |
  +--------+-----------------------------+-----+-------------------+ 
  | Parent | /project                    | GET | project#list      |
  | Parent | /project/:id                | GET | project#list      |
  +--------+-----------------------------+-----+-------------------+

The value must to be a valid SCALAR and a valid perl sub name.

CONTROLLER

One can override the use of 'key' as the controller name by using this modifier, so

  PARENT => {
            project => {ACTION=>'list'
                        CONTROLLER=>'myapp-pm'},
          }

  +--------+-----------------------------+-----+-------------------+
  |  Type  |    Route                    | Via | Controller#Action |
  +--------+-----------------------------+-----+-------------------+ 
  | Parent | /project                    | GET | myapp-pm#list     |
  | Parent | /project/:id                | GET | myapp-pm#list     |
  +--------+-----------------------------+-----+-------------------+
  

The value must to be a valid SCALAR and a valid perl 'class' name. You should use the same naming convention as found in Mojolicious, lower-snake-case but it will also take '::' as well.

NO_ID

You may not want to have an :id on a 'PARENT' resource, so you can use this modifier to drop that route

  PARENT => {
            project => {ACTION=>'all_projects'
                         CONTROLLER=>'pm'
                         NO_ID=>1 },
          }

would get you only

  +--------+-----------------------------+-----+-------------------+
  |  Type  |    Route                    | Via | Controller#Action |
  +--------+-----------------------------+-----+-------------------+ 
  | Parent | /project                    | GET | pm#all_projects   |
  +--------+-----------------------------+-----+-------------------+

The key needs only to be defined.

NO_ROOT

Sometimes one might not want to open up a 'PARENT' resource without an :id, so you can use this modifier to drop that route

  PARENT => {
            project => {ACTION=>'list'
                         CONTROLLER=>'pm'
                         NO_ROOT=>1 },
          }

would get you only

  +--------+-----------------------------+-----+-------------------+
  |  Type  |    Route                    | Via | Controller#Action |
  +--------+-----------------------------+-----+-------------------+ 
  | Parent | /project/:id                | GET | pm#list           |
  +--------+-----------------------------+-----+-------------------+

The key needs only to be defined. Be aware that if you use both 'NO_ID' and 'NO_ROOT' you will get no routes.

STASH

Need some static data on all items along a route? Well, with this modifier you can. So given this hash

  PARENT => {
            project => {STASH=>{selected_tab=>'project'}},
            user    => {STASH=>{selected_tab=>'user'}}
          }
          

you would get the same routes as with the first example but the 'selected_tab' variable will be available in the stash. So you could use it on your controller to pass the current navigation state into the content pages say, as in this case, to set up a the 'Selected Tab' in a view.

The value must be a Hashref with at least one key pair defined.

VIA

By default, all route types use the GET http method. You can change this to any other valid combination of HTTP methods. As this plugin has a restful portion, this is probably not advisable.

  PARENT => {
            user    => {VIA    =>[qw(POST PUT),
                        ACTION =>'update']}
          }

would yield these routes

  +--------+-----------------------------+------+-------------------+
  |  Type  |    Route                    | Via  | Controller#Action |
  +--------+-----------------------------+------+-------------------+ 
  | Parent | /user                       | POST | user#update       |
  | Parent | /user/:id                   | POST | user#update       |
  | Parent | /user                       | PUT  | user#update       |
  | Parent | /user/:id                   | PUT  | user#update       |
  +--------+-----------------------------+------+-------------------+

Note here how the 'action' of the user route was changed to 'update' as it would not be a very good idea to have a sub in your controller called 'show' that updates an entity.

The value must be an Arrayref of valid HTTP methods.

API_ONLY

Sometimes you want just the RESTful API so instead of using 'NO_ID' and 'NO_ROOT', use the 'API_ONLY' and get no routes.

  PARENT => {
            project => { API_ONLY=>1 },
          }

Would get you no routes!

The key needs only to be defined.

INLINE Type

An INLINE route is one that usually points to only part of a single content entity, or perhaps a collection of that entity or even a number of child entities under the parent entity. Using an example 'Project' page it could be made up of a number panels, pages, tabs etc. each containing only part of the whole project.

Below we see the three panels of a 'Project' page

  +----------+---------+----------+
  | Abstract | Details | Admin    | 
  +          +---------+----------+
  |                               |
  | Some content here             |
  ...

In this case 'Abstract' is a single large content item from a single project entity, 'Details' has a number of smaller single content items (Name, Long Description, etc.) and maybe a few collections such as 'Users' or 'Contacts'. The final page, 'Admin', leads to a separate admin entity.

By default, an INLINE resource uses its key as the resource, its parent resource as its controller, its key as the action, and GET as the HTTP verb. Also, the parent and child resource are passed placed in the stash along with and other STASH values from the PARENT.

So to create the routes for the example page above one would use this hash

  PARENT => {
            project => {
              STASH =>{page=>'project'},
              INLINE => { abstract=>{
                                   STASH=>{tab=>'abstract'}
                                   },
                          detail=>{
                                   STASH=>{tab=>'detail'},
                                  },
                          admin=>{
                                   STASH=>{tab=>'admin'},
                                   }
                                 }
               },
          }
          

which would give you these routes

  +--------+-----------------------------+-----+-------------------+---------------------------------+
  |  Type  |    Route                    | Via | Controller#Action | Stashed Values                  |
  +--------+-----------------------------+-----+-------------------+---------------------------------+
  | Parent | /project                    | GET | project#show      | page = project                  |
  | Parent | /project/:id                | GET | project#show      | page = project                  |
  | Inline | /project/:id/abstract       | GET | project#abstract  | tab  = abstract, page = project |
  | Inline | /project/:id/detail         | GET | project#detail    | tab  = detail, page = project   |
  | Inline | /project/:id/admin          | GET | project#admin     | tab  = admin, page = project    |
  +--------+-----------------------------+-----+-------------------+---------------------------------+
 

On the content pages you would use the 'stashed' page and tab values to select the current tab.

So INLINE by default is limited in scope to the parent's level, in this case the project with the correct id, and using the parents controller the action always being the key of the inline_route.

The value must a Hashref with at least one key pair defined.

INLINE Attributes

The following attributes are available to INLINE types and work in the same way as the PARENT attributes.

  • ACTION

  • CONTROLLER

  • NO_ID

  • API_ONLY

  • VIA

CHILD Type

A CHILD is one that will always follow the parent to child entity pattern. It should always point to either a collection of child entities when only the parent :id is present, and a single child entity when the :child_id is present.

By default, a CHILD resource uses its key as the resource, its parent resource as its controller, its key as the action and GET as the HTTP verb.

So this hash

  PARENT => {
            project => {
              CHILD => { user=>{},
                         contact=>{}
                        }
               },
          }

would result in the following routes

  +--------+--------------------------------+-----+-------------------+-----------------------------------+
  |  Type  |    Route                       | Via | Controller#Action | Stashed Values                    |
  +--------+--------------------------------+-----+-------------------+-----------------------------------+
  | Parent | /project                       | GET | project#show      | parent = project                  |
  | Parent | /project/:id                   | GET | project#show      | parent = project                  |
  | Child  | /project/:id/user              | GET | projects#user     | parent = project, child = user    |
  | Child  | /project/:id/user/:child_id    | GET | projeect#user     | parent = project, child = user    |
  | Child  | /project/:id/contact           | GET | projects#contact  | parent = project, child = contact |
  | Child  | /project/:id/contact/:child_id | GET | projeect#contact  | parent = project, child = contact |
  +--------+--------------------------------+-----+-------------------+-----------------------------------+

Notice how the stash has the parent controller 'project' and the action child 'user' this works in the same manner as INLINE types.

The value must be a Hashref with at least one key pair defined.

The following CHILD attributes are available and work in the same way as the on the INLINE and PARENT attributes.

  • ACTION

  • CONTROLLER

  • API_ONLY

  • VIA

API Attribute

All three route types can have the 'API' attribute, which is used to open the resource to the RESTful API of your system. This module takes an 'open only when asked' design pattern, meaning that if you do not explicitly ask for an API resource, it will not be created.

It follows the tried and true CRUD pattern but with a an extra 'R' for 'Replace' giving us CRRUD which maps to the following HTTP Methods: 'POST', 'GET','PUT','PATCH' and 'DELETE'.

VERBS

The VERBS modifier is used to open parts of your API. It must be a Hashref with the following keys;

CREATE

This opens the 'POST' method of your API resource and always points to a 'create' sub in the resource controller.

RETRIEVE

This opens the 'GET' method of your API resource and always points to a 'get' sub in the resource controller.

REPLACE

This opens the 'PUT' method of your API resource and always points to a 'replace' sub in the resource controller.

UPDATE

This opens the 'PATCH' method of your API resource and always points to an 'update' sub in the resource controller.

DELETE

This opens the 'DELETE' method of your API resource and always points to an 'delete' sub in the resource controller.

PARENT Types and Verbs

All API verbs are available to a parent resource, and by default the key is used as the resource and controller name, while the via and action are set by the HTTP verb.

So for the following hash

  PARENT => {
                     project => {
                       API   => {
                         VERBS => {
                           CREATE   => 1,
                           UPDATE   => 1,
                           RETRIEVE => 1,
                           DELETE   => 1
                         },
                       },
              }
              

you would get the following api routes

  +--------+-------------------------------+--------+---------------------+
  |  Type  |       Route                   | Via    | Controller#Action   |
  +--------+-------------------------------+--------+---------------------+
  | Parent | /projects                     | GET    | api-projects#get    |
  | Parent | /projects/:id                 | GET    | api-projects#get    |
  | Parent | /projects                     | POST   | api-projects#create |
  | Parent | /projects/:id                 | PATCH  | api-projects#update |
  | Parent | /projects/:id                 | DELETE | api-projects#delete |
  +--------+-------------------------------+--------+---------------------+

As the REPLACE verb was not added to the hash, the route via http PUT was not created. Also note, the PARENT resource has been change to a plural, via Lingua::EN::Inflect::PL, and the controller has had the default 'api' namespace added to the plural form of the PARENT resource.

The value must a Hashref with at least one of the valid VERB keys defined.

RESOURCE

Sometimes you may not want to use the default plural form PL. Say for example if your specification requires you use the first letter abbreviated form of 'Professional Engineers of New Islington' tacking an 's' on the end may not be what the client wants.

So with this attribute used in this hash

  PARENT => {
             apparatus => {
                    API   => {
                         RESOURCE =>'apparatus'
                         VERBS => {
                           RETRIEVE => 1,
                         },
                       },
              }

you would get the following

  +--------+-------------------------------+--------+---------------------+
  |  Type  |       Route                   | Via    | Controller#Action   |
  +--------+-------------------------------+--------+---------------------+
  | Parent | /apparatus                    | GET    | api-apparatus#get   |
  | Parent | /apparatus/:id                | GET    | api-apparatus#get   |
  +--------+-------------------------------+--------+---------------------+

Note how it set both the route resource and the controller name.

The value must be a valid SCALAR.

CONTROLLER

You may want to change the controller for some reason; this modifier lets you do that. So

  PARENT => {
             apparatus => {
                    API   => {
                         RESOURCE =>'apparatus'
                         CONTROLLER=>'user_apps'
                         VERBS => {
                           RETRIEVE => 1,
                         },
                       },

would give you

  +--------+-------------------------------+--------+--------------------+
  |  Type  |       Route                   | Via    | Controller#Action  |
  +--------+-------------------------------+--------+--------------------+
  | Parent | /apparatus                    | GET    | api-user_apps#get  |
  | Parent | /apparatus/:id                | GET    | api-user_apps#get  |
  +--------+-------------------------------+--------+--------------------+
  

The value must to be a valid SCALAR and a valid perl 'class' name. You should use the same naming convention as found in Mojolicious, lower-snake-case but it will also take '::' as well.

STASH

Like all the other route types, you can add extra static data on all items along a route with this modifier. The value must be a Hashref with at least one key pair defined.

INLINE Types and API Verbs

INLINE API routes are limited to only two verbs 'RETRIEVE' and 'UPDATE'; and by default, its key is used as the resource, the controller is the PARENT resource, the via is set by the VERB, and the action is Key.

Technically speaking, this type of route breaks the RESTFul speculation as no specific path to the Child and its identifier could be present, so it really should be a PUT replace method rather than an PATCH update method. I left it in as it is useful to have about, for retrieval of partial data sets of a parent entity using a sub in the parent's controller, and updating large singleton sets of a Parent. Just do not use them if you do not like them.

For example the following

  PARENT => {
             project => {
                    API   => {
                         VERBS => {
                           RETRIEVE => 1,
                         },
                       },
                    INLINE => 
                       { resume=>{
                          API => {verbs=>{RETRIEVE => 1,
                                  UPDATE => 1,
                                  }
                                }
                               }
    
         }
         

would give you the following API routes

  +--------+-----------------------+-------+--------------------  +------------------------------------+
  |  Type  |    Route              | Via   | Controller#Action    | Stashed Values                     |
  +--------+-----------------------+-------+----------------------+------------------------------------+
  | Parent | /projects             | GET   | api-projects#get     | parent = projects                  |
  | Parent | /projects/:id         | GET   | api-projects#get     | parent = projects                  |
  | INLINE | /projects/:id/resumes | GET   | api-projects#resumes | parent = projects, child = resumes |
  | INLINE | /projects/:id/resumes | PATCH | api-projects#resumes | parent = projects, child = resumes |
  +--------+-----------------------+-------+----------------------+------------------------------------+
 

The value must be a Hashref with at least one of the valid VERB keys defined. It only process 'RETEIVE' and 'UPDATE' verbs.

Other Attributes

RESOURCE and ACTION

Both can be used with INLINE routes.

So this hash

  PARENT => {
             project => {
                    API   => {
                         VERBS => {
                           RETRIEVE => 1,
                         },
                       },
                    INLINE => 
                       { resume=>{
                          API => {RESOURCE => 'resume',
                                  ACTION=>'get_or_update_resume',
                                  VERBS=>{RETRIEVE => 1,
                                          UPDATE   => 1}
                                  }
                               }
    
         }
         

would give you the following API routes

  +--------+----------------------+-------+-----------------------------------+------------------------------------+
  |  Type  |    Route             | Via   | Controller#Action                 | Stashed Values                     |
  +--------+----------------------+-------+-----------------------------------+------------------------------------+
  | Parent | /projects            | GET   | api-projects#get                  | parent = projects                  |
  | Parent | /projects/:id        | GET   | api-projects#get                  | parent = projects                  |
  | Child  | /projects/:id/resume | GET   | api-projects#get_or_update_resume | parent = projects, child = resumes |
  | Child  | /projects/:id/resume | PATCH | api-projects#get_or_update_resume | parent = projects, child = resumes |
  +--------+----------------------+-------+-----------------------------------+------------------------------------+

By the way, it is not very good RESTful design to have a singular noun as a resource, or to imply an update to a child with the PATCH method without an identifier for that child.

The value of RESOURCE and ACTION must be a valid SCALAR.

STASH

Like all the other route types, you can add extra static data on all items along a route with this modifier. The value must be a Hashref with at least one key defined.

CHILD Types and API Verbs

CHILD type routes can utilize all verbs. The resource is by default the key value. When the GET verb is used with only the :id of the parent, then the Parent controller is used, and the action will be the Key of the Child. For all of the other routes, the key is the controller name while the via and action are set by the HTTP verb.

So this hash

  PARENT => {
             project =>{
                         API   => {
                           VERBS => {
                             RETRIEVE => 1,
                           },
                         },
                       },
                CHILD => {
                         user => {
                           API => {
                             VERBS => {
                               CREATE   => 1,
                               RETRIEVE => 1,
                               REPLACE  => 1,
                               UPDATE   => 1,
                               DELETE   => 1
                             }
                           }
                         }
                       }
                     }
                   

would generate these routes

  +--------+-------------------------------+--------+--------------------+----------------------------------+
  |  Type  |    Route                      | Via    | Controller#Action  | Stashed Values                   |
  +--------+-------------------------------+--------+--------------------+----------------------------------+
  | Parent | /projects                     | GET    | api-projects#get   | parent = projects                |
  | Parent | /projects/:id                 | GET    | api-projects#get   | parent = projects                |
  | Child  | /projects/:id/users           | GET    | api-projects#users | parent = projects, child = users |
  | Child  | /projects/:id/users           | POST   | api-users#create   | parent = projects, child = users |
  | Child  | /projects/:id/users/:child_id | GET    | api-users#get      | parent = projects, child = users |
  | Child  | /projects/:id/users/:child_id | PUT    | api-users#replace  | parent = projects, child = users |
  | Child  | /projects/:id/users/:child_id | PATCH  | api-users#update   | parent = projects, child = users |
  | Child  | /projects/:id/users/:child_id | DELETE | api-users#delete   | parent = projects, child = users |
  +--------+-------------------------------+--------+--------------------+----------------------------------+

The value must a Hashref with at least one of the valid VERB keys defined.

Other Attributes

RESOURCE and CONTROLLER

You can use both the 'RESOURCE' and 'CONTROLLER' attributes in a sub_route. The only caveat being that you cannot change the controller and action on the RETRIEVE Verb without :child_id.

So given this hash

  PARENT => {
                    project => {
                       API   => {
                         VERBS => {
                           RETRIEVE => 1,
                         },
                       },
                       },
                       CHILD => {
                         user => {
                           API => {
                             CONTROLLER => 'my_users',
                             RESOURCE   => 'user',
                             VERBS => {
                               CREATE   => 1,
                               RETRIEVE => 1,
                               REPLACE  => 1,
                               UPDATE   => 1,
                               DELETE   => 1
                             }
                           }
                         }
                       }
                     }
                   

you would have only these routes

  +--------+------------------------------+--------+----------------------+---------------------------------+
  |  Type  |    Route                     | Via    | Controller#Action    | Stashed Values                  |
  +--------+------------------------------+--------+----------------------+---------------------------------+
  | Parent | /projects                    | GET    | api-projects#get     | parent = projects               |
  | Parent | /projects/:id                | GET    | api-projects#get     | parent = projects               |
  | Child  | /projects/:id/user           | GET    | api-projects#user    | parent = projects, child = user |
  | Child  | /projects/:id/user           | POST   | api-my_users#create  | parent = projects, child = user |
  | Child  | /projects/:id/user/:child_id | GET    | api-my_users#get     | parent = projects, child = user |
  | Child  | /projects/:id/user/:child_id | PUT    | api-my_users#replace | parent = projects, child = user |
  | Child  | /projects/:id/user/:child_id | PATCH  | api-my_users#update  | parent = projects, child = user |
  | Child  | /projects/:id/user/:child_id | DELETE | api-my_users#delete  | parent = projects, child = user |
  +--------+-----------------------+------+--------+----------------------+---------------------------------+

The value of RESOURCE and CONTROLLER must be a valid SCALAR.

STASH

Like all the other route types, you can add extra static data on all items along a route with this modifier. The value must be a Hashref with at least one key pair defined.

Global API Attributes.

There are a few Global API attributes that can be added to CONFIG hashref with an API Hashref. hash.

VERSION

Sometimes there is a requirement to include a version identifier for your APIs, and this is normally done with a version prefix. Using this attribute, you can add a version prefix to all our your API routes.

So with this hash

  CONFIG => {API=>{VERSION=>'V_1_1'}},
  PARENT => {
            project => {
                       API   => {
                         VERBS => {
                           RETRIEVE => 1,
                         },
                       },
                       },
                       CHILD => {
                         user => {
                           API => {
                             CONTROLLER => 'my_users',
                             RESORUCE   => 'user',
                             VERBS => {
                               CREATE   => 1,
                               RETRIEVE => 1,
                               REPLACE  => 1,
                               UPDATE   => 1,
                               DELETE   => 1
                             }
                           }
                         }
                       }
            }
                     

would have only these routes

  +--------+-----------------------------------+--------+----------------------+---------------------------------+
  |  Type  |    Route                          | Via    | Controller#Action    | Stashed Values                  |
  +--------+-----------------------------------+--------+----------------------+---------------------------------+
  | Parent | V_1_1/projects                    | GET    | api-projects#get     | parent = projects               |
  | Parent | V_1_1/projects/:id                | GET    | api-projects#get     | parent = projects               |
  | Child  | V_1_1/projects/:id/user           | GET    | api-projects#user    | parent = projects, child = user |
  | Child  | V_1_1/projects/:id/user           | POST   | api-my_users#create  | parent = projects, child = user |
  | Child  | V_1_1/projects/:id/user/:child_id | GET    | api-my_users#get     | parent = projects, child = user |
  | Child  | V_1_1/projects/:id/user/:child_id | PUT    | api-my_users#replace | parent = projects, child = user |
  | Child  | V_1_1/projects/:id/user/:child_id | PATCH  | api-my_users#update  | parent = projects, child = user |
  | Child  | V_1_1/projects/:id/user/:child_id | DELETE | api-my_users#delete  | parent = projects, child = user |
  +--------+-----------------------------------+--------+----------------------+---------------------------------+

The value must be a valid SCALAR.

RESOURCE_PREFIX

You can also add a global prefix as well if you want. It always comes after the VERSION.

So this hash

  CONFIG => {API=>{VERSION=>'V_1_1',
                   RESOURCE_PREFIX=>'beta' }
            },
  PARENT => {
                    project => {
                       API   => {
                         VERBS => {
                           RETRIEVE => 1,
                         },
                       },
                       },
                       CHILD => {
                         user => {
                           API => {
                             CONTROLLER => 'my_users',
                             RESORUCE   => 'user',
                             VERBS => {
                               CREATE   => 1,
                               RETRIEVE => 1,
                               REPLACE  => 1,
                               UPDATE   => 1,
                               DELETE   => 1
                             }
                           }
                         }
                       }
                     }
                     

would generate these routes

  +--------+----------------------------------------+--------+----------------------+---------------------------------+
  |  Type  |    Route                               | Via    | Controller#Action    | Stashed Values                  |
  +--------+----------------------------------------+--------+----------------------+---------------------------------+
  | Parent | beta/V_1_1/projects                    | GET    | api-projects#get     | parent = projects               |
  | Parent | beta/V_1_1/projects/:id                | GET    | api-projects#get     | parent = projects               |
  | Child  | beta/V_1_1/projects/:id/user           | GET    | api-projects#user    | parent = projects, child = user |
  | Child  | beta/V_1_1/projects/:id/user           | POST   | api-my_users#create  | parent = projects, child = user |
  | Child  | beta/V_1_1/projects/:id/user/:child_id | GET    | api-my_users#get     | parent = projects, child = user |
  | Child  | beta/V_1_1/projects/:id/user/:child_id | PUT    | api-my_users#replace | parent = projects, child = user |
  | Child  | beta/V_1_1/projects/:id/user/:child_id | PATCH  | api-my_users#update  | parent = projects, child = user |
  | Child  | beta/V_1_1/projects/:id/user/:child_id | DELETE | api-my_users#delete  | parent = projects, child = user |
  +--------+----------------------------------------+--------+----------------------+---------------------------------+

The value must be a valid SCALAR.

PREFIX

If you really do not like 'API' as the lead part of your api namespace you can over-ride that with this attribute as in the hash below

  CONFIG => {API=>{PREFIX=>'open'}},
  PARENT => {
            project => {
                       API   => {
                         VERBS => {
                           RETRIEVE => 1,
                         },
                       },
                       CHILD => {
                         user => {
                           API => {
                             CONTROLLER => 'my_users',
                             RESORUCE   => 'user',
                             VERBS => {
                               CREATE   => 1,
                               RETRIEVE => 1,
                               REPLACE  => 1,
                               UPDATE   => 1,
                               DELETE   => 1
                             }
                           }
                         }
                       }
            }
                     

would have only these routes

  +--------+-----------------------------+--------+-----------------------+---------------------------------+
  |  Type  |    Route                    | Via    | Controller#Action     | Stashed Values                  |
  +--------+------------------------- ---+--------+-----------------------+---------------------------------+
  | Parent | projects                    | GET    | open-projects#get     | parent = projects               |
  | Parent | projects/:id                | GET    | open-projects#get     | parent = projects               |
  | Child  | projects/:id/user           | GET    | open-projects#user    | parent = projects, child = user |
  | Child  | projects/:id/user           | POST   | open-my_users#create  | parent = projects, child = user |
  | Child  | projects/:id/user/:child_id | GET    | open-my_users#get     | parent = projects, child = user |
  | Child  | projects/:id/user/:child_id | PUT    | open-my_users#replace | parent = projects, child = user |
  | Child  | projects/:id/user/:child_id | PATCH  | open-my_users#update  | parent = projects, child = user |
  | Child  | projects/:id/user/:child_id | DELETE | open-my_users#delete  | parent = projects, child = user |
  +--------+-----------------------+------+--------+----------------------+---------------------------------+

The value must to be a valid SCALAR and a valid perl 'class' name. You should use the same naming convention as found in Mojolicious, lower-snake-case but it will also take '::' as well.

AUTHOR

John Scoles, <byterock at hotmail.com>

BUGS / CONTRIBUTING

Please report any bugs or feature requests through the web interface at https://github.com/byterock/.

SUPPORT

You can find documentation for this module with the perldoc command. perldoc Mojolicious::Plugin::Authorization You can also look for information at:

ACKNOWLEDGEMENTS

LICENSE AND COPYRIGHT

Copyright 2016 John Scoles. This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License. See http://dev.perl.org/licenses/ for more information.