From: Tomas Doran Date: Mon, 23 Mar 2009 23:53:47 +0000 (+0000) Subject: svn diff -r 9551:9552 http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Runtime... X-Git-Tag: 5.71001~6 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Runtime.git;a=commitdiff_plain;h=58a5e7024325ce351aefdf0bbac7404d8a28863a svn diff -r 9551:9552 dev.catalyst.perl.org/repos/Catalyst/Catalyst-Runtime/5.80/trunk/ (kinda) Fixup Visit for chained - backport where branch was merged to 5.80 trunk, applied cleanly sans test renames (t0m) --- diff --git a/Changes b/Changes index e1e41b5..d885eab 100644 --- a/Changes +++ b/Changes @@ -1,11 +1,16 @@ # This file documents the revision history for Perl extension Catalyst. 5.71000_01 UNRELEASED - - Add failing test for passing arguments to visited chained - actions (Radoslaw Zielinski) - Support Moose components so that attribute defaults work and BUILD methods are correctly called (t0m) - Add tests for this (Florian Ragwitz) + - Change the $c->visit and $c->go methods to optionally take + CaptureArgs, making them useful to call ActionChains with (t0m) + - Tests for this (Radoslaw Zielinski) + - Fix _invoke_as_component method to find the proper action instance + for dispatchable actions so that ->visit or ->going to ActionChains + with qw/Class::Name method_name/ works correctly (t0m) + - Tests for this (Radoslaw Zielinski) - Added Catalyst::Test::ctx_request to be able to inspect the context object after a request is made (Jos Boumans) diff --git a/lib/Catalyst.pm b/lib/Catalyst.pm index 6fe1ac0..9df1276 100644 --- a/lib/Catalyst.pm +++ b/lib/Catalyst.pm @@ -328,9 +328,9 @@ When called with no arguments it escapes the processing chain entirely. sub detach { my $c = shift; $c->dispatcher->detach( $c, @_ ) } -=head2 $c->visit( $action [, \@arguments ] ) +=head2 $c->visit( $action [, \@captures, \@arguments ] ) -=head2 $c->visit( $class, $method, [, \@arguments ] ) +=head2 $c->visit( $class, $method, [, \@captures, \@arguments ] ) Almost the same as C, but does a full dispatch, instead of just calling the new C<$action> / C<$class-E$method>. This means that C, @@ -354,9 +354,9 @@ been reached directly from a URL. sub visit { my $c = shift; $c->dispatcher->visit( $c, @_ ) } -=head2 $c->go( $action [, \@arguments ] ) +=head2 $c->go( $action [, \@captures, \@arguments ] ) -=head2 $c->go( $class, $method, [, \@arguments ] ) +=head2 $c->go( $class, $method, [, \@captures, \@arguments ] ) Almost the same as C, but does a full dispatch like C, instead of just calling the new C<$action> / diff --git a/lib/Catalyst/Dispatcher.pm b/lib/Catalyst/Dispatcher.pm index e8e201e..50e0c63 100644 --- a/lib/Catalyst/Dispatcher.pm +++ b/lib/Catalyst/Dispatcher.pm @@ -12,7 +12,7 @@ use Catalyst::Utils; use Text::SimpleTable; use Tree::Simple; use Tree::Simple::Visitor::FindByPath; -use Scalar::Util (); +use Scalar::Util qw(blessed); # Stringify to class use overload '""' => sub { return ref shift }, fallback => 1; @@ -129,7 +129,8 @@ sub dispatch { } # $self->_command2action( $c, $command [, \@arguments ] ) -# Search for an action, from the command and returns C<($action, $args)> on +# $self->_command2action( $c, $command [, \@captures, \@arguments ] ) +# Search for an action, from the command and returns C<($action, $args, $captures)> on # success. Returns C<(0)> on error. sub _command2action { @@ -140,8 +141,12 @@ sub _command2action { return 0; } - my @args; + my (@args, @captures); + if ( ref( $extra_params[-2] ) eq 'ARRAY' ) { + @captures = @{ pop @extra_params }; + } + if ( ref( $extra_params[-1] ) eq 'ARRAY' ) { @args = @{ pop @extra_params } } else { @@ -168,7 +173,7 @@ sub _command2action { $action = $self->_invoke_as_component( $c, $command, $method ); } - return $action, \@args; + return $action, \@args, \@captures; } =head2 $self->visit( $c, $command [, \@arguments ] ) @@ -186,7 +191,7 @@ sub _do_visit { my $self = shift; my $opname = shift; my ( $c, $command ) = @_; - my ( $action, $args ) = $self->_command2action(@_); + my ( $action, $args, $captures ) = $self->_command2action(@_); my $error = qq/Couldn't $opname("$command"): /; if (!$action) { @@ -195,7 +200,7 @@ sub _do_visit { } elsif (!defined $action->namespace) { $error .= qq/Action has no namespace: cannot $opname() to a plain / - .qq/method or component, must be a :Action or some sort./ + .qq/method or component, must be an :Action of some sort./ } elsif (!$action->class->can('_DISPATCH')) { $error .= qq/Action cannot _DISPATCH. / @@ -214,6 +219,7 @@ sub _do_visit { $action = $self->expand_action($action); local $c->request->{arguments} = $args; + local $c->request->{captures} = $captures; local $c->{namespace} = $action->{'namespace'}; local $c->{action} = $action; @@ -241,7 +247,7 @@ Documented in L sub forward { my $self = shift; my ( $c, $command ) = @_; - my ( $action, $args ) = $self->_command2action(@_); + my ( $action, $args, $captures ) = $self->_command2action(@_); unless ($action) { my $error = @@ -292,35 +298,44 @@ sub _invoke_as_path { } } -sub _find_component_class { +sub _find_component { my ( $self, $c, $component ) = @_; - return ref($component) - || ref( $c->component($component) ) - || $c->component($component); + # fugly, why doesn't ->component('MyApp') work? + return $c if ($component eq blessed($c)); + + return blessed($component) + ? $component + : $c->component($component); } sub _invoke_as_component { - my ( $self, $c, $component, $method ) = @_; + my ( $self, $c, $component_or_class, $method ) = @_; - my $class = $self->_find_component_class( $c, $component ) || return 0; + my $component = $self->_find_component($c, $component_or_class); + my $component_class = blessed $component || return 0; + + if (my $code = $component_class->can('action_for')) { + my $possible_action = $component->$code($method); + return $possible_action if $possible_action; + } - if ( my $code = $class->can($method) ) { + if ( my $code = $component_class->can($method) ) { return $self->method_action_class->new( { name => $method, code => $code, - reverse => "$class->$method", - class => $class, + reverse => "$component_class->$method", + class => $component_class, namespace => Catalyst::Utils::class2prefix( - $class, $c->config->{case_sensitive} + $component_class, $c->config->{case_sensitive} ), } ); } else { my $error = - qq/Couldn't forward to "$class". Does not implement "$method"/; + qq/Couldn't forward to "$component_class". Does not implement "$method"/; $c->error($error); $c->log->debug($error) if $c->debug; diff --git a/t/lib/TestApp.pm b/t/lib/TestApp.pm index d903b3c..11bcfec 100644 --- a/t/lib/TestApp.pm +++ b/t/lib/TestApp.pm @@ -76,16 +76,6 @@ sub class_forward_test_method :Private { $c->response->headers->header( 'X-Class-Forward-Test-Method' => 1 ); } -sub class_go_test_method :Private { - my ( $self, $c ) = @_; - $c->response->headers->header( 'X-Class-Go-Test-Method' => 1 ); -} - -sub class_visit_test_method :Private { - my ( $self, $c ) = @_; - $c->response->headers->header( 'X-Class-Visit-Test-Method' => 1 ); -} - sub loop_test : Local { my ( $self, $c ) = @_; diff --git a/t/lib/TestApp/Controller/Action/Go.pm b/t/lib/TestApp/Controller/Action/Go.pm index 9c7f3e6..cecb8e8 100644 --- a/t/lib/TestApp/Controller/Action/Go.pm +++ b/t/lib/TestApp/Controller/Action/Go.pm @@ -63,7 +63,7 @@ sub go_die : Local { sub go_chained : Local { my ( $self, $c, $val ) = @_; - $c->go('/action/chained/foo/spoon',[1]); + $c->go('/action/chained/foo/spoon', ['captureme'], [qw/arg1 arg2/]); } sub view : Local { @@ -96,7 +96,7 @@ sub embed : Local { sub class_go_test_action : Local { my ( $self, $c ) = @_; - $c->go(qw/TestApp class_go_test_method/); + $c->go(qw/TestApp/); } 1; diff --git a/t/lib/TestApp/Controller/Action/Visit.pm b/t/lib/TestApp/Controller/Action/Visit.pm index 0447d34..0ddaacb 100644 --- a/t/lib/TestApp/Controller/Action/Visit.pm +++ b/t/lib/TestApp/Controller/Action/Visit.pm @@ -61,10 +61,11 @@ sub visit_die : Local { } sub visit_chained : Local { - my ( $self, $c, $val ) = @_; - $val eq 1 ? $c->visit( '/action/chained/foo/spoon', [$val] ) - : $val eq 2 ? $c->visit( qw/ Action::Chained::Foo spoon /, [$val] ) - : $c->visit( $c->controller('Action::Chained::Foo')->action_for('spoon'), [$val] ) + my ( $self, $c, $val, $capture, @args ) = @_; + my @cap_and_args = ([$capture], [@args]); + $val eq 1 ? $c->visit( '/action/chained/foo/spoon', @cap_and_args) + : $val eq 2 ? $c->visit( qw/ Action::Chained::Foo spoon /, @cap_and_args) + : $c->visit( $c->controller('Action::Chained::Foo')->action_for('spoon'), @cap_and_args) } sub view : Local { @@ -97,7 +98,7 @@ sub embed : Local { sub class_visit_test_action : Local { my ( $self, $c ) = @_; - $c->visit(qw/TestApp class_visit_test_method/); + $c->visit(qw/TestApp/); } 1; diff --git a/t/live_component_controller_action_go.t b/t/live_component_controller_action_go.t index b1088c9..3f5ee25 100644 --- a/t/live_component_controller_action_go.t +++ b/t/live_component_controller_action_go.t @@ -242,7 +242,7 @@ sub run_tests { ); ok( !$response->is_success, 'Response Fails' ); is( $response->content, - q(FATAL ERROR: Couldn't go("TestApp"): Action has no namespace: cannot go() to a plain method or component, must be a :Action or some sort.), + q(FATAL ERROR: Couldn't go("TestApp"): Action has no namespace: cannot go() to a plain method or component, must be an :Action of some sort.), 'Error message' ); } @@ -262,7 +262,7 @@ sub run_tests { ok( my $response = request('http://localhost/action/go/go_chained'), 'go to chained + subcontroller endpoint' ); is( $response->header('X-Catalyst-Executed'), $expected, 'Executed actions' ); - is( $response->content, '; 1', 'Content OK' ); + is( $response->content, 'arg1, arg2; captureme', 'Content OK' ); } } diff --git a/t/live_component_controller_action_visit.t b/t/live_component_controller_action_visit.t index 6ce6706..102fe3c 100644 --- a/t/live_component_controller_action_visit.t +++ b/t/live_component_controller_action_visit.t @@ -253,7 +253,7 @@ sub run_tests { ); ok( !$response->is_success, 'Response Fails' ); is( $response->content, - q[FATAL ERROR: Couldn't visit("TestApp"): Action has no namespace: cannot visit() to a plain method or component, must be a :Action or some sort.], + q{FATAL ERROR: Couldn't visit("TestApp"): Action has no namespace: cannot visit() to a plain method or component, must be an :Action of some sort.}, "Cannot visit app namespace" ); } @@ -272,11 +272,12 @@ sub run_tests { my $expected = join( ", ", @expected ); for my $i ( 1..3 ) { - ok( my $response = request("http://localhost/action/visit/visit_chained/$i"), + ok( my $response = request("http://localhost/action/visit/visit_chained/$i/becomescapture/arg1/arg2"), "visit to chained + subcontroller endpoint for $i" ); is( $response->header('X-Catalyst-Executed'), $expected, "Executed actions for $i" ); - is( $response->content, "; $i", "Content OK for $i" ); + is( $response->content, "arg1, arg2; becomescapture", + "Content OK for $i" ); } }