package TestApp::DispatchType::Chained; use Moose; extends 'Catalyst::DispatchType::Chained'; sub recurse_match { my ( $self, $c, $parent, $path_parts ) = @_; my $children = $self->_children_of->{$parent}; return () unless $children; my $best_action; my @captures; TRY: foreach my $try_part (sort { length($b) <=> length($a) } keys %$children) { # $b then $a to try longest part first my @parts = @$path_parts; if (length $try_part) { # test and strip PathPart next TRY unless ($try_part eq join('/', # assemble equal number of parts splice( # and strip them off @parts as well @parts, 0, scalar(@{[split('/', $try_part)]}) ))); # @{[]} to avoid split to @_ } my @try_actions = @{$children->{$try_part}}; TRY_ACTION: foreach my $action (@try_actions) { if (my $capture_attr = $action->attributes->{CaptureArgs}) { # Short-circuit if not enough remaining parts next TRY_ACTION unless @parts >= $capture_attr->[0]; my @captures; my @parts = @parts; # localise # original Catalyst::DispatchType::Chained # push(@captures, splice(@parts, 0, $capture_attr->[0])); # /original # modification my @action_captures = splice(@parts, 0, $capture_attr->[0]); next TRY_ACTION if !$self->match_action_captures( $c, $action, \@action_captures ); # strip CaptureArgs into list push(@captures, @action_captures); # /modification # try the remaining parts against children of this action my ($actions, $captures, $action_parts) = $self->recurse_match( $c, '/'.$action->reverse, \@parts ); # 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); } 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 (); } sub match_action_captures { my ( $this, $c, $action, $action_captures ) = @_; my $mc_action = $c->dispatcher->get_action_by_path( $action->private_path . '_match_captures' ); return $mc_action ? $c->forward( $mc_action, $action_captures ) : 1; }