File Coverage

blib/lib/CGI/Application/Plugin/REST.pm
Criterion Covered Total %
statement 217 217 100.0
branch 92 92 100.0
condition n/a
subroutine 18 18 100.0
pod 5 5 100.0
total 332 332 100.0


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