change forward/detach to work with instances
John Napiorkowski [Thu, 21 Jul 2022 20:01:45 +0000 (15:01 -0500)]
Right now if you $c->forward($view) where $view is an instance
of a view the code will create a new view object of the same
type and forward to that, rather than forward to the view
instance (and other components).  This fixes that

lib/Catalyst/Dispatcher.pm
t/forward_instances.t [new file with mode: 0644]

index 9ac3581..7a31c77 100644 (file)
@@ -328,37 +328,39 @@ sub _find_component {
 }
 
 sub _invoke_as_component {
-    my ( $self, $c, $component_or_class, $method ) = @_;
-
-    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 = $component_class->can($method) ) {
-        return $self->_method_action_class->new(
-            {
-                name      => $method,
-                code      => $code,
-                reverse   => "$component_class->$method",
-                class     => $component_class,
-                namespace => Catalyst::Utils::class2prefix(
-                    $component_class, ref($c)->config->{case_sensitive}
-                ),
-            }
-        );
-    }
-    else {
-        my $error =
-          qq/Couldn't forward to "$component_class". Does not implement "$method"/;
-        $c->error($error);
-        $c->log->debug($error)
-          if $c->debug;
-        return 0;
-    }
+  my ( $self, $c, $component_or_class, $method ) = @_;
+
+  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;
+  }
+
+  my $component_to_call = blessed($component_or_class) ? $component_or_class : $component_class;
+
+  if ( my $code = $component_to_call->can($method) ) {
+      return $self->_method_action_class->new(
+          {
+              name      => $method,
+              code      => $code,
+              reverse   => "$component_class->$method",
+              class     => $component_to_call,
+              namespace => Catalyst::Utils::class2prefix(
+                  $component_class, ref($c)->config->{case_sensitive}
+              ),
+          }
+      );
+  }
+  else {
+      my $error =
+        qq/Couldn't forward to "$component_class". Does not implement "$method"/;
+      $c->error($error);
+      $c->log->debug($error)
+        if $c->debug;
+      return 0;
+  }
 }
 
 =head2 $self->prepare_action($c)
diff --git a/t/forward_instances.t b/t/forward_instances.t
new file mode 100644 (file)
index 0000000..3588d91
--- /dev/null
@@ -0,0 +1,50 @@
+use warnings;
+use strict;
+use Test::More;
+
+# Test case for reported issue when an action consumes JSON but a
+# POST sends nothing we get a hard error
+
+{
+  package MyApp::Controller::Root;
+  $INC{'MyApp/Controller/Root.pm'} = __FILE__;
+
+  use base 'Catalyst::Controller';
+
+  sub test :Local Args(0) {
+    my( $self, $c ) = @_;
+    my $view = $c->view('Test');
+    $c->forward($view);
+
+    Test::More::is("$view", "@{[ $c->res->body]}");
+  }
+
+  package MyApp::View::Test;
+  $INC{'MyApp/View/Test.pm'} = __FILE__;
+
+  use base 'Catalyst::View';
+
+  sub ACCEPT_CONTEXT {
+    my ($self, $c, @args) = @_;
+    return ref($self)->new;
+  }
+
+  sub process {
+    my ($self, $c, @args) = @_;
+    $c->res->body("$self");
+
+  }
+
+  package MyApp;
+  use Catalyst;
+  MyApp->setup;
+}
+
+use HTTP::Request::Common;
+use Catalyst::Test 'MyApp';
+
+{
+  ok my $res = request GET 'root/test';
+}
+
+done_testing(2);