push(@captures, splice(@parts, 0, $capture_attr->[0]));
####
my @action_captures = splice(@parts, 0, $capture_attr->[0]);
next TRY_ACTION if !$self->match_action_captures( $c, $action, \@action_captures );
push(@captures, @action_captures);
####
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;
}
####
sub load_category_match_captures : Private {
my ( $this, $c, $category ) = @_;
return scalar grep { $category eq $_ } qw(cosmetics entertainment);
}
####
package TestApp;
use Catalyst; # qw(-Debug);
sub setup_dispatcher {
my $this = shift;
my $dispatcher = $this->next::method(@_);
$dispatcher->preload_dispatch_types(
['+TestApp::DispatchType::Chained'] );
return $dispatcher;
}
__PACKAGE__->setup;
my $dp = __PACKAGE__->dispatcher->dispatch_types;
@$dp = grep { ( ref($_) || $_ ) ne 'Catalyst::DispatchType::Chained'; } @$dp;
1;
####
/cs/something ....
/en/something ....
####
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;
}