Merge branch 'master' of git://github.com/bobtfish/catalyst-action-rest
Arthur Axel 'fREW' Schmidt [Sat, 25 Jul 2009 10:07:40 +0000 (05:07 -0500)]
Conflicts:
lib/Catalyst/Action/REST.pm

14 files changed:
.gitignore [new file with mode: 0644]
Changes
MANIFEST.SKIP
Makefile.PL
README
lib/Catalyst/Action/Deserialize.pm
lib/Catalyst/Action/REST.pm
lib/Catalyst/Action/Serialize.pm
lib/Catalyst/Controller/REST.pm
lib/Catalyst/Request/REST.pm
t/catalyst-action-rest.t
t/isa.t [new file with mode: 0644]
t/lib/Test/Catalyst/Action/REST/Controller/Root.pm [new file with mode: 0644]
t/pod.t [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..f0a5464
--- /dev/null
@@ -0,0 +1,9 @@
+META.yml
+Makefile
+blib
+inc
+pm_to_blib
+MANIFEST
+Makefile.old
+MANIFEST.bak
+
diff --git a/Changes b/Changes
index 5b595e4..5278db8 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,3 +1,19 @@
+Wed Jul 22 23:49:16 BST 2009 (t0m) - Release 0.74
+
+  Switch from NEXT to MRO::Compat (agladdish).
+
+  Add display of additional REST actions in the stats, and also fix a warning
+  in Catalyst 5.80 when you forward to another action from inside an
+  action_FOO method (as it was confusing the stats).
+
+  POD fixes
+
+  Catalyst::Action::REST no longer @ISA Catalyst or Catalyst::Controller.
+
+  Change constructor to call next::method instead of SUPER::
+
+  Change method used to find the application class to be more correct
+
 Sat Jun 27 20:20:09 EDT 2009 (hdp) - Release 0.73
   Packaging fixes
 
index 29069a5..32106f2 100644 (file)
@@ -3,7 +3,7 @@
 \bCVS\b
 ,v$
 \B\.svn\b
-\B\.git\b
+\B\.git(ignore)?\b
 
 # Makemaker generated files and dirs.
 ^MANIFEST\.
@@ -11,7 +11,7 @@
 ^blib/
 ^MakeMaker-\d
 ^pm_to_blib$
-
+^Catalyst-Action-REST-
 # Temp, old and emacs backup files.
 ~$
 \.old$
index 82e9a87..cd8b295 100644 (file)
@@ -13,6 +13,7 @@ requires('LWP::UserAgent'            => '2.033');
 requires('Data::Serializer'          => '0.36');
 requires('Class::Inspector'          => '1.13');
 requires('URI::Find'                 => undef);
+requires('MRO::Compat'               => '0.10');
 
 feature 'JSON (application/json) support',
     -default   => 0,
@@ -42,7 +43,7 @@ feature 'XML::Simple (text/xml) support',
 auto_include;
 auto_install;
 
-repository('http://github.com/hdp/catalyst-action-rest');
+repository 'http://github.com/bobtfish/catalyst-action-rest';
 
 WriteAll;
 
diff --git a/README b/README
index 34f800a..fcab6bd 100644 (file)
--- a/README
+++ b/README
@@ -284,22 +284,22 @@ IMPLEMENTATION DETAILS
         The "begin" method uses Catalyst::Action::Deserialize. The "end"
         method uses Catalyst::Action::Serialize. If you want to override
         either behavior, simply implement your own "begin" and "end" actions
-        and use NEXT:
+        and use MRO::Compat:
 
           my Foo::Controller::Monkey;
           use base qw(Catalyst::Controller::REST);
 
           sub begin :Private {
             my ($self, $c) = @_;
-            ... do things before Deserializing ...
-            $self->NEXT::begin($c); 
+            ... do things before Deserializing ...            
+            $self->maybe::next::method($c);
             ... do things after Deserializing ...
           } 
 
           sub end :Private {
             my ($self, $c) = @_;
-            ... do things before Serializing ...
-            $self->NEXT::end($c); 
+            ... do things before Serializing ...            
+            $self->maybe::next::method($c);
             ... do things after Serializing ...
           }
 
index 24aac2a..9daf029 100644 (file)
@@ -11,6 +11,7 @@ use warnings;
 
 use base 'Catalyst::Action::SerializeBase';
 use Module::Pluggable::Object;
+use MRO::Compat;
 
 __PACKAGE__->mk_accessors(qw(plugins));
 
@@ -38,7 +39,7 @@ sub execute {
         }
     }
 
-    $self->NEXT::execute(@_);
+    $self->maybe::next::method(@_);
 
     return 1;
 }
index 30f9723..898ce60 100644 (file)
@@ -12,19 +12,18 @@ use warnings;
 
 use base 'Catalyst::Action';
 use Class::Inspector;
-use Catalyst;
 use Catalyst::Request::REST;
 use Catalyst::Controller::REST;
 
 BEGIN { require 5.008001; }
 
-our $VERSION = '0.73';
+our $VERSION = '0.74';
 
 sub new {
   my $class  = shift;
   my $config = shift;
   Catalyst::Request::REST->_insert_self_into( $config->{class} );
-  return $class->SUPER::new($config, @_);
+  return $class->next::method($config, @_);
 }
 
 =head1 NAME
@@ -90,61 +89,73 @@ sub dispatch {
     my $c    = shift;
 
     my $controller = $c->component( $self->class );
-    my $method     = $self->name . "_" . uc( $c->request->method );
-    if ( my $action = $controller->action_for($method) ) {
+    my $rest_method = $self->name . "_" . uc( $c->request->method );
+
+    my ($code, $name);
+
+    # Common case, for foo_GET etc
+    if ( my $action = $controller->action_for($rest_method) ) {
         return $c->forward( $action,  $c->req->args );
-    } elsif ( $controller->can($method) ) {
+     } elsif ($code = $controller->can($rest_method)) {
+        # Exceute normal action
         $c->execute( $self->class, $self, @{ $c->req->args } );
-        return $controller->$method( $c, @{ $c->req->args } );
-    } else {
-        if ( $c->request->method eq "OPTIONS" ) {
-            return $self->_return_options($c);
-        } else {
-            my $handle_ni = $self->name . "_not_implemented";
-            if ( $controller->can($handle_ni) ) {
-                return $controller->$handle_ni( $c, @{ $c->req->args } );
-            } else {
-                return $self->_return_not_implemented($c);
-            }
-        }
+        $name = $rest_method;
     }
-}
 
-sub _return_options {
-    my ( $self, $c ) = @_;
+    # Generic handling for foo_OPTIONS
+    if (!$code && $c->request->method eq "OPTIONS") {
+        $name = $rest_method;
+        $code = sub { $self->_return_options($self->name, @_) };
+    }
 
-    my @allowed = $self->_get_allowed_methods($c);
-    $c->response->content_type('text/plain');
-    $c->response->status(200);
-    $c->response->header( 'Allow' => \@allowed );
+    # Otherwise, not implemented.
+    if (!$code) {
+        $name = $self->name . "_not_implemented";
+        $code = $controller->can($name) # User method
+            # Generic not implemented
+            || sub { $self->_return_not_implemented($self->name, @_) };
+    }
+
+    # localise stuff so we can dispatch the action 'as normal, but get
+    # different stats shown, and different code run.
+    local $self->{code} = $code;
+    local $self->{reverse} = $name;
+
+    $c->execute( $self->class, $self, @{ $c->req->args } );
 }
 
 sub _get_allowed_methods {
-    my ( $self, $c ) = @_;
-
-    my $controller = $self->class;
-    my $methods    = Class::Inspector->methods($controller);
+    my ( $self, $controller, $c, $name ) = @_;
+    my $class = ref($controller) ? ref($controller) : $controller;
+    my $methods    = Class::Inspector->methods($class);
     my @allowed;
     foreach my $method ( @{$methods} ) {
-        my $name = $self->name;
         if ( $method =~ /^$name\_(.+)$/ ) {
             push( @allowed, $1 );
         }
     }
     return @allowed;
+};
+
+sub _return_options {
+    my ( $self, $method_name, $controller, $c) = @_;
+    my @allowed = $self->_get_allowed_methods($controller, $c, $method_name);
+    $c->response->content_type('text/plain');
+    $c->response->status(200);
+    $c->response->header( 'Allow' => \@allowed );
 }
 
 sub _return_not_implemented {
-    my ( $self, $c ) = @_;
+    my ( $self, $method_name, $controller, $c ) = @_;
 
-    my @allowed = $self->_get_allowed_methods($c);
+    my @allowed = $self->_get_allowed_methods($controller, $c, $method_name);
     $c->response->content_type('text/plain');
     $c->response->status(405);
     $c->response->header( 'Allow' => \@allowed );
     $c->response->body( "Method "
           . $c->request->method
           . " not implemented for "
-          . $c->uri_for( $self->reverse ) );
+          . $c->uri_for( $method_name ) );
 }
 
 1;
@@ -164,6 +175,7 @@ L<Catalyst::Action::Serialize>, L<Catalyst::Action::Deserialize>
 
 =item Q: I'm getting a "415 Unsupported Media Type" error. What gives?!
 
+<<<<<<< HEAD:lib/Catalyst/Action/REST.pm
 A:  Most likely, you haven't set Content-type equal to "application/json", or one of the
 accepted return formats.  You can do this by setting it in your query string thusly:
 ?content-type=application%2Fjson (where %2F == / uri escaped).
@@ -175,12 +187,18 @@ Make sure AllowEncodedSlashes On is in your httpd.conf file in order for this to
 
 =cut
 
+=======
+A:  Most likely, you haven't set Content-type equal to "application/json", or
+one of the accepted return formats.  You can do this by setting it in your query
+accepted return formats.  You can do this by setting it in your query string
+thusly: C<< ?content-type=application%2Fjson (where %2F == / uri escaped). >>
+>>>>>>> f04ed654a172628f642bdefe8483c1e6becf9ad1:lib/Catalyst/Action/REST.pm
 
+B<NOTE> Apache will refuse %2F unless configured otherise.
+Make sure C<< AllowEncodedSlashes On >> is in your httpd.conf file in orde
+for this to run smoothly.
 
-
-=head1 MAINTAINER
-
-J. Shirley <jshirley@gmail.com>
+=back
 
 =head1 CONTRIBUTORS
 
@@ -192,11 +210,17 @@ John Goulah
 
 Daisuke Maki <daisuke@endeworks.jp>
 
+J. Shirley <jshirley@gmail.com>
+
+Hans Dieter Pearcey
+
+Tomas Doran (t0m) <bobtfish@bobtfish.net>
+
 =head1 AUTHOR
 
 Adam Jacob <adam@stalecoffee.org>, with lots of help from mst and jrockway
 
-Marchex, Inc. paid me while I developed this module.  (http://www.marchex.com)
+Marchex, Inc. paid me while I developed this module. (L<http://www.marchex.com>)
 
 =head1 LICENSE
 
index 31597cf..1f6db7d 100644 (file)
@@ -11,12 +11,13 @@ use warnings;
 
 use base 'Catalyst::Action::SerializeBase';
 use Module::Pluggable::Object;
+use MRO::Compat;
 
 sub execute {
     my $self = shift;
     my ( $controller, $c ) = @_;
 
-    $self->NEXT::execute(@_);
+    $self->maybe::next::method(@_);
 
     return 1 if $c->req->method eq 'HEAD';
     return 1 if length( $c->response->body );
index d4ed811..e6036d9 100644 (file)
@@ -1,10 +1,10 @@
 package Catalyst::Controller::REST;
 
-our $VERSION = '0.73';
+our $VERSION = '0.74';
 
 =head1 NAME
 
-Catalyst::Controller::REST - A RESTful controller 
+Catalyst::Controller::REST - A RESTful controller
 
 =head1 SYNOPSIS
 
@@ -17,11 +17,11 @@ Catalyst::Controller::REST - A RESTful controller
     # Answer GET requests to "thing"
     sub thing_GET {
        my ( $self, $c ) = @_;
-     
+
        # Return a 200 OK, with the data in entity
-       # serialized in the body 
+       # serialized in the body
        $self->status_ok(
-            $c, 
+            $c,
             entity => {
                 some => 'data',
                 foo  => 'is real bar-y',
@@ -30,7 +30,7 @@ Catalyst::Controller::REST - A RESTful controller
     }
 
     # Answer PUT requests to "thing"
-    sub thing_PUT { 
+    sub thing_PUT {
       .. some action ..
     }
 
@@ -38,23 +38,23 @@ Catalyst::Controller::REST - A RESTful controller
 
 Catalyst::Controller::REST implements a mechanism for building
 RESTful services in Catalyst.  It does this by extending the
-normal Catalyst dispatch mechanism to allow for different 
-subroutines to be called based on the HTTP Method requested, 
+normal Catalyst dispatch mechanism to allow for different
+subroutines to be called based on the HTTP Method requested,
 while also transparently handling all the serialization/deserialization for
 you.
 
 This is probably best served by an example.  In the above
 controller, we have declared a Local Catalyst action on
-"sub thing", and have used the ActionClass('REST').  
+"sub thing", and have used the ActionClass('REST').
 
 Below, we have declared "thing_GET" and "thing_PUT".  Any
-GET requests to thing will be dispatched to "thing_GET", 
-while any PUT requests will be dispatched to "thing_PUT".  
+GET requests to thing will be dispatched to "thing_GET",
+while any PUT requests will be dispatched to "thing_PUT".
 
 Any unimplemented HTTP methods will be met with a "405 Method Not Allowed"
 response, automatically containing the proper list of available methods.  You
 can override this behavior through implementing a custom
-C<thing_not_implemented> method.  
+C<thing_not_implemented> method.
 
 If you do not provide an OPTIONS handler, we will respond to any OPTIONS
 requests with a "200 OK", populating the Allowed header automatically.
@@ -69,18 +69,18 @@ contents of $c->request->body based on the requests content-type header.
 A list of understood serialization formats is below.
 
 If we do not have (or cannot run) a serializer for a given content-type, a 415
-"Unsupported Media Type" error is generated. 
+"Unsupported Media Type" error is generated.
 
 To make your Controller RESTful, simply have it
 
-  use base 'Catalyst::Controller::REST'; 
+  use base 'Catalyst::Controller::REST';
 
 =head1 SERIALIZATION
 
 Catalyst::Controller::REST will automatically serialize your
 responses, and deserialize any POST, PUT or OPTIONS requests. It evaluates
 which serializer to use by mapping a content-type to a Serialization module.
-We select the content-type based on: 
+We select the content-type based on:
 
 =over 2
 
@@ -95,7 +95,7 @@ If this is a GET request, you can supply a content-type query parameter.
 =item B<Evaluating the Accept Header>
 
 Finally, if the client provided an Accept header, we will evaluate
-it and use the best-ranked choice.  
+it and use the best-ranked choice.
 
 =back
 
@@ -103,11 +103,11 @@ it and use the best-ranked choice.
 
 A given serialization mechanism is only available if you have the underlying
 modules installed.  For example, you can't use XML::Simple if it's not already
-installed.  
+installed.
 
 In addition, each serializer has it's quirks in terms of what sorts of data
 structures it will properly handle.  L<Catalyst::Controller::REST> makes
-no attempt to save you from yourself in this regard. :) 
+no attempt to save you from yourself in this regard. :)
 
 =over 2
 
@@ -122,7 +122,7 @@ to hyperlinks.  Only useable for Serialization.
 
 =item C<application/json> => C<JSON>
 
-Uses L<JSON> to generate JSON output.  It is strongly advised to also have 
+Uses L<JSON> to generate JSON output.  It is strongly advised to also have
 L<JSON::XS> installed.  The C<text/x-json> content type is supported but is
 deprecated and you will receive warnings in your log.
 
@@ -164,13 +164,13 @@ you serialize be a HASHREF, we transform outgoing data to be in the form of:
 
 =item L<View>
 
-Uses a regular Catalyst view.  For example, if you wanted to have your 
+Uses a regular Catalyst view.  For example, if you wanted to have your
 C<text/html> and C<text/xml> views rendered by TT:
 
        'text/html' => [ 'View', 'TT' ],
        'text/xml'  => [ 'View', 'XML' ],
-       
-Will do the trick nicely. 
+
+Will do the trick nicely.
 
 =back
 
@@ -201,7 +201,7 @@ Using them will ensure that you are responding with the proper codes,
 headers, and entities.
 
 These helpers try and conform to the HTTP 1.1 Specification.  You can
-refer to it at: L<http://www.w3.org/Protocols/rfc2616/rfc2616.txt>.  
+refer to it at: L<http://www.w3.org/Protocols/rfc2616/rfc2616.txt>.
 These routines are all implemented as regular subroutines, and as
 such require you pass the current context ($c) as the first argument.
 
@@ -245,7 +245,7 @@ Returns a "200 OK" response.  Takes an "entity" to serialize.
 Example:
 
   $self->status_ok(
-    $c, 
+    $c,
     entity => {
         radiohead => "Is a good band!",
     }
@@ -271,7 +271,7 @@ and a "location" where the created object can be found.
 Example:
 
   $self->status_created(
-    $c, 
+    $c,
     location => $c->req->uri->as_string,
     entity => {
         radiohead => "Is a good band!",
@@ -313,7 +313,7 @@ Returns a "202 ACCEPTED" response.  Takes an "entity" to serialize.
 Example:
 
   $self->status_accepted(
-    $c, 
+    $c,
     entity => {
         status => "queued",
     }
@@ -340,7 +340,7 @@ response.
 Example:
 
   $self->status_bad_request(
-    $c, 
+    $c,
     message => "Cannot do what you have asked!",
   );
 
@@ -366,7 +366,7 @@ response.
 Example:
 
   $self->status_not_found(
-    $c, 
+    $c,
     message => "Cannot find what you were looking for!",
   );
 
@@ -423,9 +423,9 @@ This class provides a default configuration for Serialization.  It is currently:
             'text/x-data-dumper' => [ 'Data::Serializer', 'Data::Dumper' ],
             'text/x-data-denter' => [ 'Data::Serializer', 'Data::Denter' ],
             'text/x-data-taxi'   => [ 'Data::Serializer', 'Data::Taxi'   ],
-            'application/x-storable'    => [ 'Data::Serializer', 'Storable'     
+            'application/x-storable'    => [ 'Data::Serializer', 'Storable'
 ],
-            'application/x-freezethaw'  => [ 'Data::Serializer', 'FreezeThaw'   
+            'application/x-freezethaw'  => [ 'Data::Serializer', 'FreezeThaw'
 ],
             'text/x-config-general' => [ 'Data::Serializer', 'Config::General' ]
 ,
@@ -442,7 +442,7 @@ L<Catalyst::Action::Serialize>.
 The C<begin> method uses L<Catalyst::Action::Deserialize>.  The C<end>
 method uses L<Catalyst::Action::Serialize>.  If you want to override
 either behavior, simply implement your own C<begin> and C<end> actions
-and use NEXT:
+and use MRO::Compat:
 
   my Foo::Controller::Monkey;
   use base qw(Catalyst::Controller::REST);
@@ -450,14 +450,14 @@ and use NEXT:
   sub begin :Private {
     my ($self, $c) = @_;
     ... do things before Deserializing ...
-    $self->NEXT::begin($c); 
+    $self->maybe::next::method($c);
     ... do things after Deserializing ...
-  } 
+  }
 
   sub end :Private {
     my ($self, $c) = @_;
     ... do things before Serializing ...
-    $self->NEXT::end($c); 
+    $self->maybe::next::method($c);
     ... do things after Serializing ...
   }
 
index 8f3a5a8..41d2a21 100644 (file)
@@ -9,6 +9,7 @@ package Catalyst::Request::REST;
 
 use strict;
 use warnings;
+use Scalar::Util qw/blessed/;
 
 use base qw/Catalyst::Request Class::Accessor::Fast/;
 
@@ -19,7 +20,8 @@ sub _insert_self_into {
   my ($class, $app_class ) = @_;
   # the fallback to $app_class is for the (rare and deprecated) case when
   # people are defining actions in MyApp.pm instead of in a controller.
-  my $app = Catalyst::Utils::class2appclass( $app_class ) || $app_class;
+  my $app = (blessed($app_class) && $app_class->can('_application'))
+        ? $app_class->_application : Catalyst::Utils::class2appclass( $app_class ) || $app_class;
 
   my $req_class = $app->request_class;
   return if $req_class->isa($class);
@@ -64,7 +66,7 @@ returned the deserialized data structure.
 
 __PACKAGE__->mk_accessors(qw(data accept_only));
 
-=over 4 
+=over 4
 
 =item accepted_content_types
 
index b65fa1f..535ac4e 100644 (file)
@@ -1,104 +1,3 @@
-package Test::Catalyst::Action::REST;
-
-use FindBin;
-
-use lib ("$FindBin::Bin/../lib");
-
-use strict;
-use warnings;
-
-use Catalyst::Runtime '5.70';
-
-use Catalyst;
-
-__PACKAGE__->config( name => 'Test::Catalyst::Action::REST' );
-__PACKAGE__->setup;
-
-sub test : Local : ActionClass('REST') {
-    my ( $self, $c ) = @_;
-    $c->stash->{'entity'} = 'something';
-}
-
-sub test_GET : Local : ActionClass('REST') {
-    my ( $self, $c ) = @_;
-
-    $c->stash->{'entity'} .= " GET";
-    $c->forward('ok');
-}
-
-sub test_POST : Local : ActionClass('REST') {
-    my ( $self, $c ) = @_;
-
-    $c->stash->{'entity'} .= " POST";
-    $c->forward('ok');
-}
-
-sub test_PUT : Local : ActionClass('REST') {
-    my ( $self, $c ) = @_;
-
-    $c->stash->{'entity'} .= " PUT";
-    $c->forward('ok');
-}
-
-sub test_DELETE : Local : ActionClass('REST') {
-    my ( $self, $c ) = @_;
-
-    $c->stash->{'entity'} .= " DELETE";
-    $c->forward('ok');
-}
-
-sub test_OPTIONS : Local : ActionClass('REST') {
-    my ( $self, $c ) = @_;
-
-    $c->stash->{'entity'} .= " OPTIONS";
-    $c->forward('ok');
-}
-
-sub notreally : Local : ActionClass('REST') {
-}
-
-sub notreally_GET {
-    my ( $self, $c ) = @_;
-
-    $c->stash->{'entity'} = "notreally GET";
-    $c->forward('ok');
-}
-
-sub not_implemented : Local : ActionClass('REST') {
-}
-
-sub not_implemented_GET {
-    my ( $self, $c ) = @_;
-
-    $c->stash->{'entity'} = "not_implemented GET";
-    $c->forward('ok');
-}
-
-sub not_implemented_not_implemented {
-    my ( $self, $c ) = @_;
-
-    $c->stash->{'entity'} = "Not Implemented Handler";
-    $c->forward('ok');
-}
-
-sub not_modified : Local : ActionClass('REST') { }
-
-sub not_modified_GET {
-    my ( $self, $c ) = @_;
-    $c->res->status(304);
-    return 1;
-}
-
-
-sub ok : Private {
-    my ( $self, $c ) = @_;
-
-    $c->res->content_type('text/plain');
-    $c->res->body( $c->stash->{'entity'} );
-}
-
-package main;
-
 use strict;
 use warnings;
 use Test::More tests => 18;
@@ -122,7 +21,7 @@ foreach my $method (qw(GET DELETE POST PUT OPTIONS)) {
         $res = request(
             $t->$run_method(
                 url  => '/test',
-                data => { foo => 'bar' }
+                data => '',
             )
         );
     }
diff --git a/t/isa.t b/t/isa.t
new file mode 100644 (file)
index 0000000..14e3343
--- /dev/null
+++ b/t/isa.t
@@ -0,0 +1,20 @@
+use strict;
+use warnings;
+
+use FindBin qw/$Bin/;
+use lib "$Bin/lib";
+
+use Test::More tests => 5;
+
+use Test::Catalyst::Action::REST;
+
+my $controller = Test::Catalyst::Action::REST->controller('Root');
+ok $controller;
+
+my $action = $controller->action_for('test');
+ok $action;
+
+isa_ok($action, 'Catalyst::Action::REST');
+ok(!$action->isa('Catalyst'));
+ok(!$action->isa('Catalyst::Controller'));
+
diff --git a/t/lib/Test/Catalyst/Action/REST/Controller/Root.pm b/t/lib/Test/Catalyst/Action/REST/Controller/Root.pm
new file mode 100644 (file)
index 0000000..1ebbee6
--- /dev/null
@@ -0,0 +1,96 @@
+package Test::Catalyst::Action::REST::Controller::Root;
+use strict;
+use warnings;
+
+use base qw/Catalyst::Controller::REST/;
+
+__PACKAGE__->config( namespace => '' );
+
+sub begin {}  # Don't need serialization..
+
+sub test : Local : ActionClass('REST') {
+    my ( $self, $c ) = @_;
+    $c->stash->{'entity'} = 'something';
+}
+
+sub test_GET {
+    my ( $self, $c ) = @_;
+
+    $c->stash->{'entity'} .= " GET";
+    $c->forward('ok');
+}
+
+sub test_POST {
+    my ( $self, $c ) = @_;
+
+    $c->stash->{'entity'} .= " POST";
+    $c->forward('ok');
+}
+
+sub test_PUT {
+    my ( $self, $c ) = @_;
+
+    $c->stash->{'entity'} .= " PUT";
+    $c->forward('ok');
+}
+
+sub test_DELETE {
+    my ( $self, $c ) = @_;
+
+    $c->stash->{'entity'} .= " DELETE";
+    $c->forward('ok');
+}
+
+sub test_OPTIONS {
+    my ( $self, $c ) = @_;
+
+    $c->stash->{'entity'} .= " OPTIONS";
+    $c->forward('ok');
+}
+
+sub notreally : Local : ActionClass('REST') {
+}
+
+sub notreally_GET {
+    my ( $self, $c ) = @_;
+
+    $c->stash->{'entity'} = "notreally GET";
+    $c->forward('ok');
+}
+
+sub not_implemented : Local : ActionClass('REST') {
+}
+
+sub not_implemented_GET {
+    my ( $self, $c ) = @_;
+
+    $c->stash->{'entity'} = "not_implemented GET";
+    $c->forward('ok');
+}
+
+sub not_implemented_not_implemented {
+    my ( $self, $c ) = @_;
+
+    $c->stash->{'entity'} = "Not Implemented Handler";
+    $c->forward('ok');
+}
+
+sub not_modified : Local : ActionClass('REST') { }
+
+sub not_modified_GET {
+    my ( $self, $c ) = @_;
+    $c->res->status(304);
+    return 1;
+}
+
+sub ok : Private {
+    my ( $self, $c ) = @_;
+
+    $c->res->content_type('text/plain');
+    $c->res->body( $c->stash->{'entity'} );
+}
+
+sub end : Private {} # Don't need serialization..
+
+1;
+
diff --git a/t/pod.t b/t/pod.t
new file mode 100644 (file)
index 0000000..251640d
--- /dev/null
+++ b/t/pod.t
@@ -0,0 +1,9 @@
+use strict;
+use warnings;
+use Test::More;
+
+eval "use Test::Pod 1.14";
+plan skip_all => 'Test::Pod 1.14 required' if $@;
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+
+all_pod_files_ok();