X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Runtime.git;a=blobdiff_plain;f=lib%2FCatalyst%2FDispatchType%2FChained.pm;h=f413d9d2a5adf13ff9ece12f3d3e48d004922387;hp=ec0e74305b94781ab484f80867efb695606fafc6;hb=b684787167ee18eb7e81245f55e0b48a72a8a1b8;hpb=6b04360e008573c47022ba52b94c98e897430aab diff --git a/lib/Catalyst/DispatchType/Chained.pm b/lib/Catalyst/DispatchType/Chained.pm index ec0e743..f413d9d 100644 --- a/lib/Catalyst/DispatchType/Chained.pm +++ b/lib/Catalyst/DispatchType/Chained.pm @@ -8,6 +8,7 @@ use Catalyst::ActionChain; use Catalyst::Utils; use URI; use Scalar::Util (); +use Encode 2.21 'decode_utf8'; has _endpoints => ( is => 'rw', @@ -101,6 +102,8 @@ sub list { my @parents = (); my $parent = "DUMMY"; my $extra = $self->_list_extra_http_methods($endpoint); + my $consumes = $self->_list_extra_consumes($endpoint); + my $scheme = $self->_list_extra_scheme($endpoint); my $curr = $endpoint; while ($curr) { if (my $cap = $curr->list_extra_info->{CaptureArgs}) { @@ -129,13 +132,21 @@ sub list { if (defined(my $cap = $p->list_extra_info->{CaptureArgs})) { $name .= ' ('.$cap.')'; } + if (defined(my $ct = $p->list_extra_info->{Consumes})) { + $name .= ' :'.$ct; + } + if (defined(my $s = $p->list_extra_info->{Scheme})) { + $scheme = uc $s; + } + unless ($p eq $parents[0]) { $name = "-> ${name}"; } push(@rows, [ '', $name ]); } - push(@rows, [ '', (@rows ? "=> " : '').($extra ? "$extra " : '')."/${endpoint}" ]); - $rows[0][0] = join('/', '', @parts) || '/'; + push(@rows, [ '', (@rows ? "=> " : '').($extra ? "$extra " : ''). ($scheme ? "$scheme: ":'')."/${endpoint}". ($consumes ? " :$consumes":"" ) ]); + my @display_parts = map { $_ =~s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; decode_utf8 $_ } @parts; + $rows[0][0] = join('/', '', @display_parts) || '/'; $paths->row(@$_) for @rows; } @@ -148,6 +159,19 @@ sub _list_extra_http_methods { my ( $self, $action ) = @_; return unless defined $action->list_extra_info->{HTTP_METHODS}; return join(', ', @{$action->list_extra_info->{HTTP_METHODS}}); + +} + +sub _list_extra_consumes { + my ( $self, $action ) = @_; + return unless defined $action->list_extra_info->{CONSUMES}; + return join(', ', @{$action->list_extra_info->{CONSUMES}}); +} + +sub _list_extra_scheme { + my ( $self, $action ) = @_; + return unless defined $action->list_extra_info->{Scheme}; + return uc $action->list_extra_info->{Scheme}; } =head2 $self->match( $c, $path ) @@ -212,19 +236,19 @@ sub recurse_match { my @try_actions = @{$children->{$try_part}}; TRY_ACTION: foreach my $action (@try_actions) { if (my $capture_attr = $action->attributes->{CaptureArgs}) { - $capture_attr ||= 0; + my $capture_count = $action->number_of_captures|| 0; # Short-circuit if not enough remaining parts - next TRY_ACTION unless @parts >= $capture_attr->[0]; + next TRY_ACTION unless @parts >= $capture_count; my @captures; my @parts = @parts; # localise # strip CaptureArgs into list - push(@captures, splice(@parts, 0, $capture_attr->[0])); + push(@captures, splice(@parts, 0, $capture_count)); # check if the action may fit, depending on a given test by the app - if ($action->can('match_captures')) { next TRY_ACTION unless $action->match_captures($c, \@captures) } + next TRY_ACTION unless $action->match_captures($c, \@captures); # try the remaining parts against children of this action my ($actions, $captures, $action_parts, $n_pathparts) = $self->recurse_match( @@ -323,27 +347,20 @@ sub register { ); } - $action->attributes->{PathPart} = [ $part ]; + my $encoded_part = URI->new($part)->canonical; + $encoded_part =~ s{(?<=[^/])/+\z}{}; + + $action->attributes->{PathPart} = [ $encoded_part ]; - unshift(@{ $children->{$part} ||= [] }, $action); + unshift(@{ $children->{$encoded_part} ||= [] }, $action); $self->_actions->{'/'.$action->reverse} = $action; - if (exists $action->attributes->{Args}) { - my $args = $action->attributes->{Args}->[0]; - if (defined($args) and not ( - Scalar::Util::looks_like_number($args) and - int($args) == $args - )) { - require Data::Dumper; - local $Data::Dumper::Terse = 1; - local $Data::Dumper::Indent = 0; - $args = Data::Dumper::Dumper($args); - Catalyst::Exception->throw( - "Invalid Args($args) for action " . $action->reverse() . - " (use 'Args' or 'Args()'" - ); - } + if (exists $action->attributes->{Args} and exists $action->attributes->{CaptureArgs}) { + Catalyst::Exception->throw( + "Combining Args and CaptureArgs attributes not supported registering " . + $action->reverse() + ); } unless ($action->attributes->{CaptureArgs}) { @@ -370,11 +387,15 @@ sub uri_for_action { my @captures = @$captures; my $parent = "DUMMY"; my $curr = $action; + # If this is an action chain get the last action in the chain + if($curr->can('chain') ) { + $curr = ${$curr->chain}[-1]; + } while ($curr) { - if (my $cap = $curr->attributes->{CaptureArgs}) { - return undef unless @captures >= ($cap->[0]||0); # not enough captures - if ($cap->[0]) { - unshift(@parts, splice(@captures, -$cap->[0])); + if (my $cap = $curr->number_of_captures) { + return undef unless @captures >= $cap; # not enough captures + if ($cap) { + unshift(@parts, splice(@captures, -$cap)); } } if (my $pp = $curr->attributes->{PathPart}) { @@ -658,6 +679,28 @@ An action that is part of a chain (that is, one that has a C<:Chained> attribute) but has no C<:CaptureArgs> attribute is treated by Catalyst as a chain end. +Allowed values for CaptureArgs is a single integer (CaptureArgs(2), meaning two +allowed) or you can declare a L, L or L +named constraint such as CaptureArgs(Int,Str) would require two args with +the first being a Integer and the second a string. You may declare your own +custom type constraints and import them into the controller namespace: + + package MyApp::Controller::Root; + + use Moose; + use MooseX::MethodAttributes; + use MyApp::Types qw/Int/; + + extends 'Catalyst::Controller'; + + sub chain_base :Chained(/) CaptureArgs(1) { } + + sub any_priority_chain :Chained(chain_base) PathPart('') Args(1) { } + + sub int_priority_chain :Chained(chain_base) PathPart('') Args(Int) { } + +See L for more. + =item Args By default, endpoints receive the rest of the arguments in the path. You @@ -685,7 +728,7 @@ of the endpoint of the chain, not on the chained actions way. The C actions will be run before the chain dispatching begins. In every other aspect, C actions behave as documented. -The Cing to other actions does just what you would expect. ie +The Cing to other actions does just what you would expect. i.e. only the target action is run. The actions that that action is chained to are not run. If you C out of a chain, the rest of the chain will not get