Add docs and fix various bugs
Dave Rolsky [Sun, 23 Jan 2011 21:05:12 +0000 (15:05 -0600)]
Only default Chained parent for end points

Use full path when generating sub names for end points

Don't default Args to 0

lib/CatalystX/Routes.pm

index 1673f65..bd9a131 100644 (file)
@@ -13,7 +13,7 @@ use Moose::Exporter;
 
 Moose::Exporter->setup_import_methods(
     with_meta       => [qw( get get_html post put del chain_point )],
-    as_is           => [qw( chained args capture_args path_part action )],
+    as_is           => [qw( chained args capture_args path_part action_class_name )],
     class_metaroles => {
         class => ['CatalystX::Routes::Role::Class'],
     },
@@ -42,7 +42,16 @@ sub del {
 sub _add_route {
     my $rest = shift;
     my $meta = shift;
-    my ( $name, $attrs, $sub ) = _process_args( $meta, @_ );
+    my ( $attrs, $sub ) = _process_args( $meta, @_ );
+
+    unless ( exists $attrs->{Chained} ) {
+        $attrs->{Chained} = q{/};
+    }
+
+    # We need to turn the full chain name into a path, since two end points
+    # from two different chains could have the same end point name.
+    ( my $name = ( $attrs->{Chained} eq '/' ? q{} : $attrs->{Chained} ) . q{/}
+            . $_[0] ) =~ s/(\W)/'X' . sprintf( '%x', ord($1) )/eg;
 
     my $meth_base = '__route__' . $name;
 
@@ -63,7 +72,9 @@ sub chain_point {
 
 sub _add_chain_point {
     my $meta = shift;
-    my ( $name, $attrs, $sub ) = _process_args( $meta, @_ );
+    my ( $attrs, $sub ) = _process_args( $meta, @_ );
+
+    ( my $name = $_[0] ) =~ s/(\W)/'X' . sprintf('%x', ord(1) )/eg;
 
     $meta->add_chain_point( $name => [ $attrs, $sub ] );
 }
@@ -101,17 +112,7 @@ sub _process_args {
         $p{PathPart} = [$part];
     }
 
-    unless ( $p{CaptureArgs} || $p{Args} ) {
-        $p{Args} = [0];
-    }
-
-    unless ( exists $p{Chained} ) {
-        $p{Chained} = q{/};
-    }
-
-    ( my $name = $path ) =~ s/(\W)/'X' . sprintf( '%x', ord($1) )/eg;
-
-    return $name, \%p, $sub;
+    return \%p, $sub;
 }
 
 sub _maybe_add_rest_route {
@@ -154,7 +155,7 @@ sub path_part ($) {
     return ( PathPart => [ $_[0] ] );
 }
 
-sub action ($) {
+sub action_class_name ($) {
     return ( ActionClass => [ $_[0] ] );
 }
 
@@ -180,3 +181,157 @@ sub _STRINGLIKE0 ($) {
 }
 
 1;
+
+# ABSTRACT: Sugar for declaring RESTful chained action in Catalyst
+
+__END__
+
+=head1 SYNOPSIS
+
+  package MyApp::Controller::User;
+
+  use Moose;
+  use CatalystX::Routes;
+
+  BEGIN { extends 'Catalyst::Controller'; }
+
+  # /user/:user_id
+
+  chain_point '_set_user'
+      => chained '/'
+      => path_part 'user'
+      => capture_args 1
+      => sub {
+          my $self = shift;
+          my $c    = shift;
+          my $user_id = shift;
+
+          $c->stash()->{user} = ...;
+      };
+
+  # GET /user/:user_Id
+  get ''
+     => chained('_set_user')
+     => args 0
+     => sub { ... };
+
+  # GET /user/foo
+  get 'foo' => sub { ... }
+
+  sub _post { ... }
+
+  # POST /user/foo
+  post 'foo' => \&_post;
+
+  # PUT /root
+  put '/root' => sub { ... };
+
+  # /user/plain_old_catalyst
+  sub plain_old_catalyst : Local { ... }
+
+=head1 DESCRIPTION
+
+This module provides a sugar layer that allows controllers to declare chained
+RESTful actions.
+
+Under the hood, all the sugar declarations are turned into Chained subs. All
+chain end points are declared using one of C<get>, C<get_html>, C<post>,
+C<put>, or C<del>. These will declare actions using the
+L<Catalyst::Action::REST::ForBrowsers> action class from the
+L<Catalyst::Action::REST> distribution.
+
+=head1 PUTTING IT ALL TOGETHER
+
+This module is merely sugar over Catalyst's built-in L<Chained
+dispatching|Catalyst::DispatchType::Chained> and L<Catalyst::Action::REST>. It
+helps to know how those two things work.
+
+=head1 SUGAR FUNCTIONS
+
+All of these functions will be exported into your controller class when you
+use C<CatalystX::Routes>.
+
+=head2 get ...
+
+This declares a C<GET> handler.
+
+=head2 get_html
+
+This declares a C<GET> handler for browsers. Use this to generate a standard
+HTML page for browsers while still being able to generate some sort of RESTful
+data response for other clients.
+
+If a browser makes a C<GET> request and no C<get_html> action has been
+declared, a C<get> action is used as a fallback. See
+C<Catalyst::TraitFor::Request::REST::ForBrowsers> for details on how
+"browser-ness" is determined.
+
+=head2 post ...
+
+This declares a C<POST> handler.
+
+=head2 put
+
+This declares a C<PUT> handler.
+
+=head2 del
+
+This declares a C<DELETE> handler.
+
+=head2 chain_point
+
+This declares an intermediate chain point that should not be exposed as a
+public URI.
+
+=head2 chained $path
+
+This function takes a single argument, the previous chain point from which the
+action is chained.
+
+=head2 args $number
+
+This declares the number of arguments that this action expects. This should
+only be used for the end of a chain.
+
+=head2 capture_args $number
+
+The number of arguments to capture at this point in the chain. This should
+only be used for the beginning or middle parts of a chain.
+
+=head2 path_part $path
+
+The path part for this part of the chain. If you are declaring a chain end
+point with C<get>, etc., then this isn't necessary. By default, the name
+passed to the initial sugar function will be converted to a path part. See
+below for details.
+
+=head2 action_class_name $class
+
+Use this to declare an action class. By default, this will be
+L<Catalyst::Action::REST::ForBrowsers> for end points. For other parts of a
+chain, it simply won't be set.
+
+=head1 Path Generation
+
+All of the end point function (C<get>, C<post>, etc.) take a path as the first
+argument. By default, this will be used as the C<path_part> for the chain. You
+can override this by explicitly calling C<path_part>, in which case the name
+is essentially ignored (but still required).
+
+Note that it is legitimate to pass the empty string as the name for a chain's
+end point.
+
+If you don't specify a C<chained> value for an end point, then it will use the
+root URI, C</>, as the root of the chain.
+
+By default, no arguments are specified for a chain's end point, meaning it
+will accept any number of arguments.
+
+=head1 BUGS
+
+Please report any bugs or feature requests to
+C<bug-catalystx-routes@rt.cpan.org>, or through the web interface at
+L<http://rt.cpan.org>.  I will be notified, and then you'll automatically be
+notified of progress on your bug as I make changes.
+
+=cut