Document :ChainedParent.
[catagits/Catalyst-Runtime.git] / lib / Catalyst / DispatchType / Chained.pm
index bee76f1..df9c1a6 100644 (file)
@@ -102,7 +102,7 @@ sub match {
     my @parts = split('/', $path);
 
     my ($chain, $captures, $parts) = $self->recurse_match($c, '/', \@parts);
-       push @{$c->req->args}, @$parts;
+    push @{$c->req->args}, @$parts if $parts && @$parts;
 
     return 0 unless $chain;
 
@@ -127,7 +127,7 @@ sub recurse_match {
     my ( $self, $c, $parent, $path_parts ) = @_;
     my $children = $self->{children_of}{$parent};
     return () unless $children;
-       my $best_action;
+    my $best_action;
     my @captures;
     TRY: foreach my $try_part (sort { length($b) <=> length($a) }
                                    keys %$children) {
@@ -157,30 +157,48 @@ sub recurse_match {
                 my ($actions, $captures, $action_parts) = $self->recurse_match(
                                              $c, '/'.$action->reverse, \@parts
                                            );
-                               if ($actions && (!$best_action || $#$action_parts < $#{$best_action->{parts}})){
-                                       $best_action = {
-                                               actions => [ $action, @$actions ],
-                                               captures=> [ @captures, @$captures ],
-                                               parts   => $action_parts
-                                               };
-                               }
-               }
-                       else {
+                #    No best action currently
+                # OR The action has less parts
+                # OR The action has equal parts but less captured data (ergo more defined)
+                if ($actions    &&
+                    (!$best_action                                 ||
+                     $#$action_parts < $#{$best_action->{parts}}   ||
+                     ($#$action_parts == $#{$best_action->{parts}} &&
+                      $#$captures < $#{$best_action->{captures}}))){
+                    $best_action = {
+                        actions => [ $action, @$actions ],
+                        captures=> [ @captures, @$captures ],
+                        parts   => $action_parts
+                        };
+                }
+            }
+            else {
                 {
                     local $c->req->{arguments} = [ @{$c->req->args}, @parts ];
                     next TRY_ACTION unless $action->match($c);
                 }
-                               if (!$best_action || $#parts < $#{$best_action->{parts}}){
-                       $best_action = {
-                                               actions => [ $action ],
-                                               captures=> [],
-                                               parts   => \@parts
-                                               }
-                                       }
+                my $args_attr = $action->attributes->{Args}->[0];
+
+                #    No best action currently
+                # OR This one matches with fewer parts left than the current best action,
+                #    And therefore is a better match
+                # OR No parts and this expects 0 
+                #    The current best action might also be Args(0),
+                #    but we couldn't chose between then anyway so we'll take the last seen
+
+                if (!$best_action                       ||
+                    @parts < @{$best_action->{parts}}   ||
+                    (!@parts && $args_attr eq 0)){
+                    $best_action = {
+                        actions => [ $action ],
+                        captures=> [],
+                        parts   => \@parts
+                    }
+                }
             }
         }
     }
-       return @$best_action{qw/actions captures parts /} if $best_action;
+    return @$best_action{qw/actions captures parts/} if $best_action;
     return ();
 }
 
@@ -197,31 +215,13 @@ sub register {
 
     return 0 unless @chained_attr;
 
-    if (@chained_attr > 2) {
+    if (@chained_attr > 1) {
         Catalyst::Exception->throw(
           "Multiple Chained attributes not supported registering ${action}"
         );
     }
 
-    my $parent = $chained_attr[0];
-
-    if (defined($parent) && length($parent)) {
-        if ($parent eq '.') {
-            $parent = '/'.$action->namespace;
-        } elsif ($parent !~ m/^\//) {
-            if ($action->namespace) {
-                $parent = '/'.join('/', $action->namespace, $parent);
-            } else {
-                $parent = '/'.$parent; # special case namespace '' (root)
-            }
-        }
-    } else {
-        $parent = '/'
-    }
-
-    $action->attributes->{Chained} = [ $parent ];
-
-    my $children = ($self->{children_of}{$parent} ||= {});
+    my $children = ($self->{children_of}{ $chained_attr[0] } ||= {});
 
     my @path_part = @{ $action->attributes->{PathPart} || [] };
 
@@ -469,13 +469,18 @@ with C<sub bar :PathPart('foo/bar') :Chained('/')> would bind to
 C</foo/bar/...>. If you don't specify C<:PathPart> it has the same
 effect as using C<:PathPart>, it would default to the action name.
 
+=item PathPrefix
+
+Sets PathPart to the path_prefix of the current controller.
+
 =item Chained
 
 Has to be specified for every child in the chain. Possible values are
-absolute and relative private action paths, with the relatives pointing
-to the current controller, or a single slash C</> to tell Catalyst that
-this is the root of a chain. The attribute C<:Chained> without arguments
-also defaults to the C</> behavior.
+absolute and relative private action paths or a single slash C</> to
+tell Catalyst that this is the root of a chain. The attribute
+C<:Chained> without arguments also defaults to the C</> behavior.
+Relative action paths may use C<../> to refer to actions in parent
+controllers.
 
 Because you can specify an absolute path to the parent action, it
 doesn't matter to Catalyst where that parent is located. So, if your
@@ -498,6 +503,19 @@ with the path of the current controller namespace, namely
 C</foo/bar>. That action chains directly to C</>, so the C</bar/*/baz/*>
 chain comes out as the end product.
 
+=item ChainedParent
+
+Chains an action to another action with the same name in the parent
+controller. For Example:
+
+  # in MyApp::Controller::Foo
+  sub bar : Chained CaptureArgs(1) { ... }
+
+  # in MyApp::Controller::Foo::Moo
+  sub bar : ChainedParent Args(1) { ... }
+
+This builds a chain like C</bar/*/bar/*>.
+
 =item CaptureArgs
 
 Must be specified for every part of the chain that is not an
@@ -544,9 +562,9 @@ The C<forward>ing to other actions does just what you would expect. But if
 you C<detach> out of a chain, the rest of the chain will not get called
 after the C<detach>.
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Matt S Trout <mst@shadowcatsystems.co.uk>
+Catalyst Contributors, see Catalyst.pm
 
 =head1 COPYRIGHT