File Coverage

blib/lib/CGI/Application/Plugin/REST.pm
Criterion Covered Total %
statement 217 217 100.0
branch 89 90 98.8
condition 3 3 100.0
subroutine 19 19 100.0
pod 6 6 100.0
total 334 335 99.7


line stmt bran cond sub pod time code
1              
2             =head1 NAME
3              
4             CGI::Application::Plugin::REST - Helps implement RESTful architecture in CGI applications
5              
6             =head1 SYNOPSIS
7              
8             package WidgetView;
9             use base qw( CGI::Application );
10             use CGI::Application::Plugin::REST qw( rest_route rest_param );
11              
12             sub setup {
13             my ($self) = @_;
14              
15             $self->rest_route({
16             '/widget' => {
17             'GET' => 'showlist',
18             'POST' => {
19             'application/xml' => 'new_widget',
20             },
21             },
22             '/widget/:id' => {
23             'GET' => 'showdetail',
24             },
25             };
26             }
27              
28             sub new_widget {
29             my ($self) = @_;
30              
31             # make a new widget
32             }
33              
34             sub showdetail {
35             my ($self) = @_;
36              
37             my $id = $self->rest_param('id');
38              
39             # display the widget with the id $id.
40             }
41              
42             sub showlist {
43             my ($self) = @_;
44              
45             # show the entire list of widgets.
46             }
47              
48             1;
49              
50             =head1 ABSTRACT
51              
52             If you use the L framework, this plugin will help you create
53             a RESTful (that's the common term for "using REST") architecture by
54             abstracting out a lot of the busy work needed to make it happen.
55              
56             =cut
57              
58             package CGI::Application::Plugin::REST;
59              
60 5     5   553742 use warnings;
  5         9  
  5         154  
61 5     5   22 use strict;
  5         6  
  5         192  
62 5     5   21 use Carp qw( croak );
  5         8  
  5         253  
63 5     5   464 use English qw/ -no_match_vars /;
  5         2862  
  5         30  
64 5     5   3971 use REST::Utils qw/ media_type request_method /;
  5         6980  
  5         10666  
65              
66             =head1 VERSION
67              
68             This document describes CGI::Application::Plugin::REST Version 0.3
69              
70             =cut
71              
72             our $VERSION = '0.3';
73              
74             our @EXPORT_OK =
75             qw/ rest_error_mode rest_param rest_resource rest_route rest_route_info
76             rest_route_prefix /;
77              
78             our %EXPORT_TAGS = ( 'all' => [@EXPORT_OK] );
79              
80             =head1 DESCRIPTION
81              
82             REST stands for REpresentational State Transfer. It is an architecture for web
83             applications that tries to leverage the existing infrastructure of the World
84             Wide Web such as URIs, MIME media types, and HTTP instead of building up
85             protocols and functions on top of them.
86              
87             This plugin contains a number of functions to support the various REST
88             concepts. They try to use existing L functionality
89             wherever possible.
90              
91             C'ing this plugin will intercept L's standard dispatch
92             mechanism. Instead of being selected based on a query parameter like C,
93             the run mode will be determined by comparing URI patterns defined in your app
94             with the L method. (Referred from here on, as "routes".)
95             Optionally, specific HTTP methods or MIME media types can be defined in a
96             route too. One by one, each entry in the reverse asciibetically sorted table
97             of defined routes is compared to the incoming HTTP request and the first
98             successful match is selected. The run mode mapped to that route is then
99             called.
100              
101             This is done via overriding L's C function so
102             it should be compatible with other L plugins.
103              
104             =head2 DevPopup Support
105              
106             If you are using L (i.e. the environment
107             variable C is set,) C'ing this module will register a
108             callback which will add debug information about the current route (See L),
109             parameters (See L) etc.
110              
111             =head1 FUNCTIONS
112              
113             The following functions are available. None of them are exported by default.
114             You can use the C<:all> tag to import all public functions.
115              
116             =cut
117              
118             # Plug in to CGI::Application and setup our callbacks
119             #
120             sub import {
121 6     6   541 my $caller = scalar caller;
122              
123             $caller->add_callback(
124             'init',
125             sub {
126 50     50   324793 my ($self) = @_;
127 50         180 $self->mode_param( \&_rest_dispatch );
128              
129 50         441 return;
130             }
131 6         60 );
132 6 100       88 if ( exists $ENV{'CAP_DEVPOPUP_EXEC'} ) {
133 1         2 $caller->add_callback( 'devpopup_report', \&_rest_devpopup );
134             }
135 6         2762 goto &Exporter::import;
136             }
137              
138             # Callback for CGI::Application::Plugin::DevPopup which provides debug info.
139             #
140             sub _rest_devpopup {
141 3     3   1893 my ( $self, $outputref ) = @_;
142              
143 3         5 my $report = "\n"; ", '\n" }; '; \n"
144 3         4 foreach my $key ( sort keys %{ $self->{'__r_params'} } ) {
  3         19  
145 15         11 my $name = $key;
146 15         18 $name =~ s/_/ /gmsx;
147 15         41 $report .= join q{},
148             (
149             "
$name: ',
150             $self->{'__r_params'}->{$key}, "
151             );
152             }
153              
154             # This bit is complicated but necessary as rest_param needs a
155             # nested table.
156 3         9 my @params = rest_param($self);
157 3         5 my $rows = scalar @params;
158 3         8 $report .= qq{
parameters:
159 3         5 foreach my $param (@params) {
160 6 100       9 if ( $param ne $params[0] ) {
161 4         5 $report .= '
162             }
163 6         9 $report .= join q{},
164             (
165             qq{$param: },
166             rest_param( $self, $param ),
167             "
168             );
169             }
170 3         5 $report .= "
\n";
171              
172 3         11 $self->devpopup->add_report(
173             title => 'CGI::Application::Plugin::REST',
174             summary => 'Information on the current REST dispatch',
175             report => $report,
176             );
177              
178 3         112 return;
179             }
180              
181             # mode_param() callback to set the run mode based on the request URI.
182             #
183             sub _rest_dispatch {
184 40     40   22638 my ($self) = @_;
185              
186 40         116 my $q = $self->query;
187 40         961 my $path = $q->path_info;
188              
189             # get the module name from the table
190 40 100       7660 if ( !exists $self->{'__rest_dispatch_table'} ) {
191 1         11 $self->header_add( -status => '500 No Dispatch Table' );
192 1         44 return rest_error_mode( $self, $EVAL_ERROR );
193             }
194              
195             # look at each rule and stop when we get a match
196 39         60 foreach my $rule ( reverse sort keys %{ $self->{'__rest_dispatch_table'} } )
  39         278  
197             {
198 166         192 my @names = ();
199              
200             # $rule will be transformed later so save the original form first.
201 166         154 my $origrule = $rule;
202 166         225 $rule = rest_route_prefix($self) . $rule;
203              
204             # translate the rule into a regular expression, but remember where
205             # the named args are.
206             # '/:foo' will become '/([^\/]*)'
207             # and
208             # '/:bar?' will become '/?([^\/]*)?'
209             # and then remember which position it matches
210 166         380 $rule =~ s{
211             (^ | /) # beginning or a /
212             (: ([^/?]+) ([?])?) # stuff in between
213             }{
214 57         103 push(@names, $3);
215 57 100       196 $1 . ( $4 ? '?( [^/]* )?' : '([^/]*)')
216             }egsmx;
217              
218             # '/*' onwards will become '(.*)\$'
219 166 100       333 if ( $rule =~ m{/[*] .* $}msx ) {
220 25         77 $rule =~ s{(/[*] .* )$}{/(.*)\$}msx;
221 25         44 push @names, 'dispatch_uri_remainder';
222             }
223              
224             # if we found a match, then run with it
225 166 100       1994 if ( my @values = ( $path =~ m{^$rule$}msx ) ) {
226              
227 38         72 $self->{'__match'} = $path;
228 38         72 my $table = $self->{'__rest_dispatch_table'}->{$origrule};
229              
230             # next check request method.
231 38         160 my $method = request_method($q);
232              
233 38 100       9115 if ( exists $table->{$method} ) {
    100          
234 28         62 $table = $table->{$method};
235             }
236             elsif ( exists $table->{q{*}} ) {
237 9         20 $table = $table->{q{*}};
238             }
239             else {
240 1         8 $self->header_add(
241             -status => "405 Method '$method' Not Allowed",
242 1         6 -allow => ( join q{, }, sort keys %{$table} ),
243             );
244 1         43 return rest_error_mode( $self, $EVAL_ERROR );
245             }
246              
247             # then check MIME media type
248 37         46 my @types = keys %{$table};
  37         103  
249 37         133 my $preferred = media_type( $q, \@types );
250 37 100 100     10449 if ( !defined $preferred || $preferred eq q{} ) {
251 6         10 $preferred = q{*/*};
252             }
253 37         62 my $rm_name = $table->{$preferred};
254              
255 37 100       74 if ( !defined $rm_name ) {
256 2         19 $self->header_add( -status => '415 Unsupported Media Type' );
257 2         86 return rest_error_mode( $self, $EVAL_ERROR );
258             }
259              
260 35         38 my $sub;
261 35 100       64 if ( ref $rm_name eq 'CODE' ) {
262 2         6 $sub = $self->$rm_name;
263             }
264             else {
265 33         49 $sub = eval { return $self->can($rm_name); };
  33         166  
266             }
267 35 100       691 if ( !defined $sub ) {
268 1         5 $self->header_add(
269             -status => "501 Function '$rm_name' Doesn't Exist" );
270 1         38 return rest_error_mode( $self, $EVAL_ERROR );
271             }
272              
273 34         118 $self->param( 'rm', $rm_name );
274              
275 34         594 my %named_args;
276              
277 34 100       78 if (@names) {
278 15         56 @named_args{@names} = @values;
279 15         51 rest_param( $self, %named_args );
280             }
281              
282 34         182 $self->{'__r_params'} = {
283             'path_received' => $path,
284             'rule_matched' => $origrule,
285             'runmode' => $rm_name,
286             'method' => $method,
287             'mimetype' => $preferred,
288             };
289              
290 34         137 return $rm_name;
291             }
292             }
293              
294 1         7 $self->header_add( -status => '404 No Route Found' );
295 1         44 return rest_error_mode( $self, $EVAL_ERROR );
296             }
297              
298             =head2 rest_error_mode()
299              
300             This function gets or sets the run mode which is called if an error occurs
301             during the dispatch process. In this run mode, you can do whatever error
302             processing or clean up is needed by your application.
303              
304             If no error mode is defined, the start mode will be returned.
305              
306             Example 1:
307              
308             $self->rest_error_mode('my_error_mode');
309             my $em = $self->rest_error_mode; # $em equals 'my_error_mode'.
310              
311             Why isn't the standard L error mode mechanism used? The
312             problem is that at the point L
313             plugs into the dispatch process, the error mode has not been defined. You
314             might also want to use L in your own code to do a different
315             sort of handling for errors in your REST API (which will typically only
316             require setting the HTTP status code) as opposed to handling for end user
317             errors.
318              
319             Your rest_error_mode handler function will receive as a parameter the value of C<$@>
320             if any.
321              
322             =cut
323              
324             sub rest_error_mode {
325 54     54 1 1844 my ( $self, $error_mode ) = @_;
326              
327             # First use? Create new __rest_error_mode
328 54 100       193 if ( !exists( $self->{'__rest_error_mode'} ) ) {
329 48         96 $self->{'__rest_error_mode'} = $self->start_mode;
330             }
331              
332             # If data is provided, set it.
333 54 50       406 if ( defined $error_mode ) {
334 54         90 $self->{'__rest_error_mode'} = $error_mode;
335 54         147 $self->run_modes( [$error_mode] );
336             }
337              
338 54         622 return $self->{'__rest_error_mode'};
339             }
340              
341             =head2 rest_param()
342              
343             The C function is used to retrieve or set named parameters
344             defined by the L function. it can be called in three ways.
345              
346             =over 4
347              
348             =item with no arguments.
349              
350             Returns a sorted list of the defined parameters in list context or the number
351             of defined parameters in scalar context.
352              
353             my @params = $self->rest_param();
354             my $num_params = $self->rest_param();
355              
356             =item with a single scalar argument.
357              
358             The value of the parameter with the name of the argument will be returned.
359              
360             my $color = $self->rest_param('color');
361              
362             =item with named arguments
363              
364             Although you will mostly use this function to retrieve parameters, they can
365             also be set for one or more sets of keys and values.
366              
367             $self->rest_param(filename => 'logo.jpg', height => 50, width => 100);
368              
369             You could also use a hashref.
370              
371             my $arg_ref = { filename => 'logo.jpg', height => 50, width => 100 };
372             $self->rest_param($arg_ref);
373              
374             The value of a parameter need not be a scalar, it could be any any sort of
375             reference even a coderef.
376              
377             $self->rest_param(number => \&pick_a_random_number);
378              
379             In this case, the function does not return anything.
380              
381             =back
382              
383             =cut
384              
385             sub rest_param {
386 54     54 1 1948 my ( $self, @args ) = @_;
387              
388 54 100       117 if ( !exists $self->{'__rest_params'} ) {
389 17         38 $self->{'__rest_params'} = {};
390             }
391              
392 54         50 my $num_args = scalar @args;
393 54 100       74 if ($num_args) {
394 49 100       144 if ( ref $args[0] eq 'HASH' ) { # a hashref
    100          
    100          
395 1         3 %{ $self->{'__rest_params'} } =
  1         2  
396 1         1 ( %{ $self->{'__rest_params'} }, %{ $args[0] } );
  1         2  
397             }
398             elsif ( $num_args % 2 == 0 ) { # a hash
399 15         37 %{ $self->{'__rest_params'} } =
  15         26  
400 15         16 ( %{ $self->{'__rest_params'} }, @args );
401             }
402             elsif ( $num_args == 1 ) { # a scalar
403 32 100       85 if ( exists $self->{'__rest_params'}->{ $args[0] } ) {
404 26         240 return $self->{'__rest_params'}->{ $args[0] };
405             }
406             }
407             else {
408 1         154 croak('Odd number of arguments passed to rest_param().');
409             }
410             }
411             else {
412             return wantarray
413 4         17 ? sort keys %{ $self->{'__rest_params'} }
  1         4  
414 5 100       36 : scalar keys %{ $self->{'__rest_params'} };
415             }
416 22         43 return;
417             }
418              
419             =head2 rest_resource()
420              
421             This function will set up a complete REST API for a collection of items with all
422             the CRUD (Create, Read, Update, Delete) operations in one call. A collection
423             could be rows in a database, files etc. The only assumption is that each item
424             has a unique identifier.
425              
426             Example 1: basic usage of rest_resource()
427              
428             $self->rest_resource('widget');
429              
430             is exactly equal to the following invocation of L:
431              
432             $self->rest_route(
433             '/widget' => {
434             'GET' => 'widget_index',
435             'POST' => 'widget_create',
436             'OPTIONS' => 'widget_options',
437             },
438             '/widget/:id' => {
439             'DELETE' => 'widget_destroy',
440             'GET' => 'widget_show',
441             'PUT' => 'widget_update',
442             },
443             '/widget/:id/edit' => {
444             'GET' => 'widget_edit',
445             },
446             '/widget/new' => {
447             'GET' => 'widget_new',
448             },
449             );
450              
451             You are responsible for defining the widget_index, widget_create etc. run
452             modes in your app.
453              
454             =over 4
455              
456             =item *_create
457              
458             Should be used to add a new item to the collection.
459              
460             =item *_destroy
461              
462             Should be used to remove the item with the id C<:id> from the collection.
463              
464             =item *_edit
465              
466             Should return a temporary copy of the resource with the id C<:id> which can be
467             changed by the user and then sent to C<*_update>.
468              
469             =item *_index
470              
471             Should be used to list the resources in the collection.
472              
473             =item *_new
474              
475             Should be used to return an input mechanism (such as an HTML form) which can be
476             filled in by the user and sent to C<*_create> to add a new resource to the
477             collection.
478              
479             =item *_show
480              
481             Should be used to display resource with the id C<:id>.
482              
483             =item *_update
484              
485             Should be used to alter the existing resource with the id C<:id>.
486              
487             =item *_options
488              
489             Should be used to retrieve metadata that describes the resource's available
490             interactions.
491              
492             =back
493              
494             Various aspects of the generated routes can be customized by passing this
495             method a hash (or hashref) of parameters instead of a scalar.
496              
497             =over 4
498              
499             =item resource
500              
501             This parameter is required. It is used to form the URI the route will match
502             to.
503              
504             HINT: use L for more complicated URIs.
505              
506             =item identifier
507              
508             This parameter sets the name assigned to the unique identifier of an item in
509             the collection which is used in some generated routes. It can be retrieved
510             with L. It defaults to C.
511              
512             =item prefix
513              
514             This parameter is prepended to an action to form a run mode name. It defaults
515             to C.
516              
517             =item in_types, out_types
518              
519             Both these parameters represent arrayrefs of MIME media types. C
520             defines acceptable MIME media types for data incoming to your API (i.e.
521             Cs and Cs) and C does the same for outgoing data (i.e.
522             Cs and C.) C requests do not need MIME media types so
523             they are not covered.
524              
525             The reason there are two separate parameters is that typically the number of
526             data formats a REST API will serve is different to the number and kind of
527             incoming data formats.
528              
529             Both of these parameters default to '*/*' i.e. any MIME media type is accepted.
530              
531             =back
532              
533             Example 2: advanced usage of rest_resource()
534              
535             $self->rest_resource(resource => 'fidget', prefix => 'foo',
536             identifier => 'num', in_types => [ 'application/xml' ],
537             out_types => [ 'text/html', 'text/plain' ], );
538              
539             is equal to the following invocation of L:
540              
541             $self->rest_route(
542             '/fidget' => {
543             'GET' => {
544             'text/html' => 'foo_index',
545             'text/plain' => 'foo_index',
546             },
547             'POST' => {
548             'application/xml' => 'foo_create',
549             },
550             },
551             '/fidget/:num' => {
552             'DELETE' => {
553             '*/*' => 'foo_destroy',
554             },
555             'GET' => {
556             'text/html' => 'foo_show',
557             'text/plain' => 'foo_show',
558             },
559             'PUT' => {
560             'application/xml' => 'foo_update',
561             },
562             },
563             '/fidget/:num/edit' => {
564             'GET' => {
565             'text/html' => 'foo_edit',
566             'text/plain' => 'foo_edit',
567             },
568             },
569             '/fidget/new' => {
570             'GET' => {
571             'text/html' => 'foo_new',
572             'text/plain' => 'foo_new',
573             },
574             },
575             );
576              
577             If you need more complicated mappings then this, use L.
578              
579             L returns the map of routes and handlers that was created.
580              
581             =cut
582              
583             sub rest_resource {
584 47     47 1 15206 my ( $self, @args ) = @_;
585              
586 47         48 my ( $resource, $prefix, $id, $in_types, $out_types );
587 47         51 my $num_args = scalar @args;
588              
589 47 100       80 if ($num_args) {
590 46 100       174 if ( ref $args[0] eq 'HASH' ) {
    100          
    100          
591 3         5 ( $resource, $prefix, $id, $in_types, $out_types ) =
592             _resource_options( $args[0] );
593             }
594             elsif ( $num_args % 2 == 0 ) { # a hash
595 21         70 my %args = @args;
596 21         39 ( $resource, $prefix, $id, $in_types, $out_types ) =
597             _resource_options( \%args );
598             }
599             elsif ( $num_args == 1 ) { # a scalar
600 21         76 ( $resource, $prefix, $id, $in_types, $out_types ) =
601             _resource_options( { resource => $args[0] } );
602             }
603             }
604             else {
605 1         183 croak "argument must be a scalar, hash, or hashref\n";
606             }
607              
608 44 100       106 if ( !$resource ) {
609 2         224 croak "Must specify resource name\n";
610             }
611              
612 42 100       73 if ( !$prefix ) {
613 21         30 $prefix = $resource;
614             }
615              
616 42 100       74 if ( !$id ) {
617 21         32 $id = 'id';
618             }
619              
620 42         118 my $routes = {
621             "/$resource" => {
622             'GET' => _make_resource_route( $prefix . '_index', $out_types ),
623             'POST' => _make_resource_route( $prefix . '_create', $in_types ),
624             'OPTIONS' =>
625             _make_resource_route( $prefix . '_options', $out_types ),
626             },
627             "/$resource/:$id" => {
628             'DELETE' => _make_resource_route( $prefix . '_destroy', [q{*/*}] ),
629             'GET' => _make_resource_route( $prefix . '_show', $out_types ),
630             'PUT' => _make_resource_route( $prefix . '_update', $in_types ),
631             },
632             "/$resource/:$id/edit" =>
633             { 'GET' => _make_resource_route( $prefix . '_edit', $out_types ), },
634             "/$resource/new" =>
635             { 'GET' => _make_resource_route( $prefix . '_new', $out_types ), },
636             };
637              
638 42         95 rest_route( $self, $routes );
639              
640 42         199 return $routes;
641             }
642              
643             sub _resource_options {
644 45     45   48 my ($args) = @_;
645              
646 45         56 my ( $resource, $prefix, $id, $in_types, $out_types );
647              
648 45         53 $resource = $args->{resource};
649 45         42 $prefix = $args->{prefix};
650 45         51 $id = $args->{identifier};
651 45 100       75 if ( exists $args->{in_types} ) {
652 22 100       56 if ( ref $args->{in_types} ne 'ARRAY' ) {
653 1         122 croak "in_types must be an arrayref\n";
654             }
655 21         26 $in_types = $args->{in_types};
656             }
657             else {
658 23         44 $in_types = [q{*/*}];
659             }
660 44 100       75 if ( exists $args->{out_types} ) {
661 22 100       51 if ( ref $args->{out_types} ne 'ARRAY' ) {
662 1         151 croak "out_types must be an arrayref\n";
663             }
664 21         24 $out_types = $args->{out_types};
665             }
666             else {
667 22         32 $out_types = [q{*/*}];
668             }
669              
670 43         120 return ( $resource, $prefix, $id, $in_types, $out_types );
671             }
672              
673             sub _make_resource_route {
674 336     336   278 my ( $rm, $types ) = @_;
675              
676 336         269 my $ret = {};
677              
678 336         240 foreach my $type ( @{$types} ) {
  336         338  
679 336         544 $ret->{$type} = $rm;
680             }
681              
682 336         927 return $ret;
683             }
684              
685             =head2 rest_route()
686              
687             When this function is given a hash or hashref, it configures the mapping of
688             routes to handlers (run modes within your L).
689              
690             It returns the map of routes and handlers.
691              
692             =head4 Routes
693              
694             Assume for the purpose of the following examples that your instance script has
695             a base URI of C
696              
697             HINT: Your web server might not execute CGI scripts unless they have an
698             extension of .cgi so your actual script might be C.
699             However it is considered unRESTful to include infrastructural details in your
700             URLs. Use your web servers URL rewriting features (i.e. mod_rewrite in
701             Apache) to hide the extension.
702              
703             A route looks like a URI with segments seperated by /'s.
704              
705             Example 1: a simple route
706              
707             /foo
708              
709             A segment in a route is matched literally. So if a request URI matches
710             http://localhost/foo, the run mode that handles the route in example 1 will
711             be used.
712              
713             If you want to match the URI base itself, you can do it like this:
714              
715             Example 2: route to a URI base
716              
717             /
718              
719             This matches C. Some people don't like the trailing slash;
720             they can be accomodated by using an empty string as the route as in Example 3.
721              
722             Example 3: route to a URI base without the trailing /
723             ''
724              
725             This matches C.
726              
727             Routes can have more complex specifications.
728              
729             Example 4: a more complex route
730              
731             /bar/:name/:id?/:email
732              
733             If a segment of a route is prefixed with a :, it is not matched literally but
734             treated as a parameter name. The value of the parameter is whatever actually
735             got matched. If the segment ends with a ?, it is optional otherwise it is
736             required. The values of these named parameters can be retrieved with the
737             L method.
738              
739             In example 2, http://localhost/bar/jaldhar/76/jaldhar@braincells.com would
740             match. C would return 'jaldhar', C
741             would return 76, and C would return
742             'jaldhar@braincells.com'.
743              
744             If the request URI was http://localhost/bar/jaldhar/jaldhar@braincells.com/,
745             C would return 'jaldhar@braincells.com' and
746             C would return 'jaldhar'. C would return
747             undef.
748              
749             If the request URI was http://localhost/bar/jaldhar/76 or
750             http://localhost/jaldhar/, there would be no match at all because the required
751             parameter ':email' is missing.
752              
753             Note: Each named parameter is returned as a scalar. If you want ':email' to
754             actually be an email address, it is up to your code to validate it before use.
755              
756             Example 5: a wild card route
757              
758             /baz/string/*
759              
760             If the route specification contains /*, everything from then on will be
761             put into the special parameter 'dispatch_uri_remainder' which you can retrieve
762             with L just like any other parameter. Only one wildcard can
763             be specified per route. Given the request URI
764             http://localhost/baz/string/good, C
765             would return 'good', with http://localhost/baz/string/evil it would return
766             'evil' and with http://localhost/baz/string/lawful/neutral/ it would return
767             'lawful/neutral/'.
768              
769             =head4 Handlers
770              
771             The most basic handler is a scalar or coderef.
772              
773             Example 4: Basic Handlers
774              
775             my $routes = {
776             '/foo' => 'wibble',
777             '/bar/:name/:id?/:email' => \&wobble,
778             '/baz/string/*/' => 'woop',
779             };
780             $self->rest_route($routes);
781              
782             In example 4, a request to C will be dispatched to
783             C. (It is upto you to make sure such a method exists.) A request
784             to C will dispatch
785             to C. A request to C will raise an error.
786              
787             Example 5: More complex handlers
788              
789             $self->rest_route(
790             '/quux' => {
791             'GET' => 'ptang',
792             'DELETE' => 'krrang',
793             },
794             '/edna' => {
795             'POST' => 'blip',
796             '*' => 'blop',
797             },
798             '/grudnuk' => {
799             'GET' => {
800             'application/xml' => 'zip',
801             '*/*' => 'zap',
802             },
803             PUT => {
804             'application/xml' => 'zoom',
805             },
806             },
807             );
808              
809             If the handler is a hashref, the keys of the second-level hash are HTTP
810             methods and the values if scalars or coderefs, are run modes. Supported
811             methods are C, C, C, C, C, and C. The key can also be C<*>
812             which matches all methods not explicitly specified. If a valid method cannot
813             be matched, an error is raised and the HTTP status of the response is set to
814             405. (See L<"DIAGNOSTICS">.)
815              
816             In example 5, a C request to http://localhost/quux will be dispatched to
817             C. A C to http://localhost/quux will dispatch to C.
818             An C) C, C or C will cause an error.
819              
820             A C request to http://localhost/edna will dispatch to C
821             while any other type of request to that URL will dispatch to C
822              
823             The values of the second-level hash can also be hashes. In this case the keys
824             of the third-level hash represent MIME media types. The values are run modes.
825             The best possible match is made use C from L.
826             according to the HTTP Accept header sent in the request. If a valid MIME
827             media type cannot be matched C<*/*> is tried as a last resort. If there is no
828             handler for even that, an error is raised and the HTTP status of the response
829             is set to 415. (See L<"DIAGNOSTICS">)
830              
831             In example 5, a C request to http://localhost/grudnuk with MIME
832             media type application/xml will dispatch to C. If the same request is
833             made with any other MIME media type, the method C will be called
834             instead. A C request made to the same URL with MIME media type
835             application/xml will dispatch to C. Any other combination of HTTP
836             methods or MIME media types will cause an error to be raised.
837              
838             If no URI can be matched, an error is raised and the HTTP status of the
839             response is set to 404 (See L<"DIAGNOSTICS">.)
840              
841             =cut
842              
843             sub rest_route {
844 90     90 1 36757 my ( $self, @routes ) = @_;
845              
846 90         140 my $rr_m = $self->{'__rest_dispatch_table'};
847              
848 90         95 my $num_routes = scalar @routes;
849 90 100       175 if ($num_routes) {
850 89 100       231 if ( ref $routes[0] eq 'HASH' ) { # Hashref
    100          
851 61         130 _method_hashref( $self, $routes[0] );
852             }
853             elsif ( ( $num_routes % 2 ) == 0 ) { # Hash
854 27         91 while ( my ( $rule, $dispatch ) = splice @routes, 0, 2 ) {
855 124         234 _method_hashref( $self, { $rule => $dispatch } );
856             }
857             }
858             else {
859 1         181 croak(
860             'Odd number of elements passed to rest_route(). Not a valid hash'
861             );
862             }
863             }
864              
865 86         169 return $self->{'__rest_dispatch_table'};
866             }
867              
868             sub _method_hashref {
869 185     185   183 my ( $self, $routes ) = @_;
870              
871 185         159 foreach my $rule ( keys %{$routes} ) {
  185         378  
872              
873 330         286 my @methods;
874 330         410 my $route_type = ref $routes->{$rule};
875 330 100       543 if ( $route_type eq 'HASH' ) {
    100          
    100          
876 270         215 @methods = keys %{ $routes->{$rule} };
  270         557  
877             }
878             elsif ( $route_type eq 'CODE' ) {
879 19         56 $routes->{$rule} = { q{*} => $routes->{$rule} };
880 19         30 push @methods, q{*};
881             }
882             elsif ( $route_type eq q{} ) { # scalar
883 40         88 $routes->{$rule} = { q{*} => $routes->{$rule} };
884 40         55 push @methods, q{*};
885             }
886             else {
887 1         167 croak "$rule (", $routes->{$rule},
888             ') has an invalid route definition';
889             }
890              
891 329         549 my @request_methods =
892             ( 'GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS', q{*}, );
893 329         337 foreach my $req (@methods) {
894 573 100       541 if ( scalar grep { $_ eq $req } @request_methods ) {
  4011         4161  
895 572         601 my $subroute = $routes->{$rule}->{$req};
896 572         702 _mime_hashref( $self, $subroute, $rule, $req );
897             }
898             else {
899 1         94 croak "$req is not a valid request method\n";
900             }
901             }
902             }
903              
904 182         461 return;
905             }
906              
907             sub _mime_hashref {
908 572     572   518 my ( $self, $subroute, $rule, $req ) = @_;
909              
910 572         509 my $subroute_type = ref $subroute;
911 572 100       886 if ( $subroute_type eq 'HASH' ) {
    100          
    100          
912 393         281 foreach my $type ( keys %{$subroute} ) {
  393         614  
913 431         776 my $func = $subroute->{$type};
914 431         748 $self->{'__rest_dispatch_table'}->{$rule}->{$req}->{$type} = $func;
915 431         812 $self->run_modes( [$func] );
916             }
917             }
918             elsif ( $subroute_type eq 'CODE' ) {
919 19         22 my $func = $subroute;
920 19         52 $self->{'__rest_dispatch_table'}->{$rule}->{$req}->{q{*/*}} = $func;
921 19         68 $self->run_modes( [$func] );
922             }
923             elsif ( $subroute_type eq q{} ) { # scalar
924 159         131 my $func = $subroute;
925 159         356 $self->{'__rest_dispatch_table'}->{$rule}->{$req}->{q{*/*}} = $func;
926 159         391 $self->run_modes( [$func] );
927             }
928             else {
929 1         170 croak "$subroute is an invalid route definition";
930             }
931              
932 571         6458 return;
933             }
934              
935             =head2 rest_route_info()
936              
937             This function can be called in a route handler. It returns a reference to a
938             hash which contains some information about the current route.
939              
940             =over 4
941              
942             =item path_received
943              
944             The value of the C environment variable.
945              
946             =item rule_matched
947              
948             The rule that was successfully matched to determine this route.
949              
950             =item runmode
951              
952             The name of the function being called by this route.
953              
954             =item method
955              
956             The HTTP method that was matched by this route.
957              
958             =item mimetype
959              
960             The MIME media type that was matched by this route.
961              
962             =back
963              
964             Example 1:
965              
966             $self->rest_route(
967             '/foo' => {
968             'GET' => 'bar',
969             },
970             );
971              
972             ...
973              
974             sub bar() {
975             my ($self) = @_;
976              
977             my $info = $self->rest_route_info;
978              
979             say $info->{method}; # prints 'GET'
980             }
981              
982             =cut
983              
984             sub rest_route_info {
985 1     1 1 109 my ($self) = @_;
986              
987 1         4 return $self->{'__r_params'};
988             }
989              
990             =head2 rest_route_prefix()
991              
992             Use this function to set a prefix for routes to avoid unnecessary repetition
993             when you have a number of similar ones.
994              
995             Example 1:
996              
997             # matches requests to /zing
998             $self->rest_route(
999             '/zing' => {
1000             'GET' => 'zap',
1001             },
1002             );
1003              
1004             $self->rest_route_prefix('/app')
1005             # from now on requests to /app/zing will match instead of /zing
1006              
1007             my $prefix = $self->rest_route_prefix # $prefix equals '/app'
1008              
1009             =cut
1010              
1011             sub rest_route_prefix {
1012 167     167 1 154 my ( $self, $prefix ) = @_;
1013              
1014             # First use? Create new __rest_route_prefix
1015 167 100       296 if ( !exists( $self->{'__rest_route_prefix'} ) ) {
1016 39         86 $self->{'__rest_route_prefix'} = q{};
1017             }
1018              
1019             # If data is provided, set it.
1020 167 100       242 if ( defined $prefix ) {
1021              
1022             # make sure no trailing slash is present on the root.
1023 1         4 $prefix =~ s{/$}{}msx;
1024 1         3 $self->{'__rest_route_prefix'} = $prefix;
1025             }
1026              
1027 167         284 return $self->{'__rest_route_prefix'};
1028              
1029             }
1030              
1031             =head1 OTHER DISPATCH PLUGINS COMPARED
1032              
1033             There are several other modules that allow L to dispatch to
1034             a run mode based on the C environment variable instead of the
1035             traditional CGI parameter. They each take a markedly different approach to
1036             implementation. Here is a comparison.
1037              
1038             Executive summary: L is the best :-)
1039              
1040             =head2 L Itself
1041              
1042             You can set the run mode with the C option to C.
1043             This is limited to one segment (i.e. between C's) of the path info.
1044              
1045             Dispatch based on HTTP method or MIME media type is not supported.
1046              
1047             =head2 L
1048              
1049             This module has influenced most of the other dispatchers including this one.
1050             It replaces L as the base class for your application.
1051              
1052             It has extensive capabilities for matching path info. It can capture variable
1053             segments in the URI with : ? and * tokens. They are retrievable in run
1054             modes as L parameters (i.e. via C<$self-Eparam()>.
1055              
1056             You can also dispatch by HTTP method but not by MIME media type. The HTTP
1057             method is determined by looking at the C environment
1058             variable only. Methods called C and C append the
1059             the HTTP method (all upper case and all lower case respectively) to a run mode
1060             that is determined by a dispatch rule which provides a limited version of
1061             L's L function.
1062              
1063             =head2 L
1064              
1065             This module adds an attribute handler to run modes of your choice which enable
1066             parsing of the path info with regular expressions and dispatch to the run mode
1067             matched. Capturing parentheses in the regex can be accessed via the
1068             C method.
1069              
1070             Dispatch based on HTTP method or MIME media type is not supported.
1071              
1072             =head2 L
1073              
1074             This module installs a prerun hook that matches path info segments with support
1075             for capturing variable with the : ? and * tokens. They are retrievable in run
1076             modes as L parameters (i.e. via C<$self-Equery-Eparam()>
1077              
1078             Dispatch based on HTTP method or MIME media type is not supported.
1079              
1080             =head1 DIAGNOSTICS
1081              
1082             During the dispatch process, errors can occur in certain circumstances. If an
1083             error occurs the appropriate HTTP status is set and execution passes to the
1084             run mode set by L. Here is a list of status codes and
1085             messages.
1086              
1087             =over 4
1088              
1089             =item * 404 No Route Found
1090              
1091             None of the specified routes matched the request URI.
1092              
1093             =item * 405 Method '$method' Not Allowed
1094              
1095             The route you specified with L does not allow this HTTP
1096             request method. An HTTP C header is added to the response specifying
1097             which methods can be used.
1098              
1099             =item * 415 Unsupported Media Type
1100              
1101             None of the MIME media types requested by the client can be returned by this
1102             route and there is no handler for C<*/*>.
1103              
1104             =item * 500 No Dispatch Table
1105              
1106             This error can occur if L was not called.
1107              
1108             =item * 500 Application Error
1109              
1110             The function that was called for this run_mode C'd somewhere.
1111              
1112             =item * 501 Function '$function_name' Doesn't Exist
1113              
1114             The function that you wanted to call from L for this run_mode
1115             doesn't exist in your application.
1116              
1117             =back
1118              
1119             =head1 BUGS AND LIMITATIONS
1120              
1121             There are no known problems with this module.
1122              
1123             Please report any bugs or feature requests to
1124             C, or through the web interface at
1125             L.
1126             I will be notified, and then you'll automatically be notified of progress on
1127             your bug as I make changes.
1128              
1129             =head1 SEE ALSO
1130              
1131             =over 4
1132              
1133             =item * L:
1134              
1135             The application framework this module plugs into.
1136              
1137             =item * L:
1138              
1139             L uses my L module behind the
1140             scenes.
1141              
1142             =item * L:
1143              
1144             This module by Matthew O'Connor gave me some good ideas.
1145              
1146             =item * L:
1147              
1148             Roy Fieldings' doctoral thesis in which the term REST was first defined.
1149              
1150             =item * L
1151              
1152             "The Restful Web" columns by Joe Gregorio have been very useful to me in
1153             understanding the ins and outs of REST.
1154              
1155             =back
1156              
1157             =head1 THANKS
1158              
1159             Much of the code in this module is based on L
1160             by JuliEn Porta who in turn credits Michael Peter's L.
1161              
1162             =head1 AUTHOR
1163              
1164             Jaldhar H. Vyas, C<< >>
1165              
1166             =head1 LICENSE AND COPYRIGHT
1167              
1168             Copyright (c) 2010 Consolidated Braincells Inc., all rights reserved.
1169              
1170             This distribution is free software; you can redistribute it and/or modify it
1171             under the terms of either:
1172              
1173             a) the GNU General Public License as published by the Free Software
1174             Foundation; either version 2, or (at your option) any later version, or
1175              
1176             b) the Artistic License version 2.0.
1177              
1178             The full text of the license can be found in the LICENSE file included
1179             with this distribution.
1180              
1181             =cut
1182              
1183             1; # End of CGI::Application::Plugin::REST
1184              
1185             __END__