Yet more sanity
[catagits/Catalyst-Action-REST.git] / lib / Catalyst / Action / REST.pm
index b9e84cf..6d650d9 100644 (file)
@@ -18,7 +18,7 @@ use Catalyst::Controller::REST;
 
 BEGIN { require 5.008001; }
 
-our $VERSION = '0.71';
+our $VERSION = '0.73';
 
 sub new {
   my $class  = shift;
@@ -37,11 +37,11 @@ Catalyst::Action::REST - Automated REST Method Dispatching
       ... do setup for HTTP method specific handlers ...
     }
 
-    sub foo_GET { 
+    sub foo_GET {
       ... do something for GET requests ...
     }
 
-    sub foo_PUT { 
+    sub foo_PUT {
       ... do somethign for PUT requests ...
     }
 
@@ -49,13 +49,13 @@ Catalyst::Action::REST - Automated REST Method Dispatching
 
 This Action handles doing automatic method dispatching for REST requests.  It
 takes a normal Catalyst action, and changes the dispatch to append an
-underscore and method name. 
+underscore and method name.
 
 For example, in the synopsis above, calling GET on "/foo" would result in
 the foo_GET method being dispatched.
 
-If a method is requested that is not implemented, this action will 
-return a status 405 (Method Not Found).  It will populate the "Allow" header 
+If a method is requested that is not implemented, this action will
+return a status 405 (Method Not Found).  It will populate the "Allow" header
 with the list of implemented request methods.  You can override this behavior
 by implementing a custom 405 handler like so:
 
@@ -90,59 +90,71 @@ sub dispatch {
     my $c    = shift;
 
     my $controller = $c->component( $self->class );
-    my $method     = $self->name . "_" . uc( $c->request->method );
-    if ( $controller->can($method) ) {
+    my $rest_method = $self->name . "_" . uc( $c->request->method );
+
+    my ($code, $name);
+
+    # Common case, for foo_GET etc
+    if ($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;