Fix model/view/controller methods to handle stringifiable objects
Matt S Trout [Tue, 14 Aug 2012 17:43:13 +0000 (17:43 +0000)]
Previously, we tested ref() and assumed if that was true that we'd been
supplied with a regexp; by switching to using Safe::Isa to check for

  $thing->$_isa('Regexp')

a stringifiable object will still get handled correctly. This is necessary
when using string overloading tools such as i18n.pm or HTML::String.

Changes
Makefile.PL
lib/Catalyst.pm
t/aggregate/unit_core_mvc.t

diff --git a/Changes b/Changes
index baf322f..06eadd6 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,5 +1,6 @@
 # This file documents the revision history for Perl extension Catalyst.
 
+  - Fix model/view/controller methods to handle stringifiable objects
   - Fix RT#78377 - IIS7 ignores response body for 3xx requests, which
     causes (a different) response to be broken when using keepalive.
     Fixed by applying Middleware which removes the response body and
index c8ff69a..af8361f 100644 (file)
@@ -59,6 +59,7 @@ requires 'Time::HiRes';
 requires 'Tree::Simple' => '1.15';
 requires 'Tree::Simple::Visitor::FindByPath';
 requires 'Try::Tiny';
+requires 'Safe::Isa';
 requires 'URI' => '1.35';
 requires 'Task::Weaken';
 requires 'Text::Balanced'; # core in 5.8.x but mentioned for completeness
index e2d3ca9..2f9225f 100644 (file)
@@ -33,6 +33,7 @@ use Catalyst::EngineLoader;
 use utf8;
 use Carp qw/croak carp shortmess/;
 use Try::Tiny;
+use Safe::Isa;
 use Plack::Middleware::Conditional;
 use Plack::Middleware::ReverseProxy;
 use Plack::Middleware::IIS6ScriptNameFix;
@@ -547,13 +548,13 @@ sub _comp_names_search_prefixes {
     # undef for a name will return all
     return keys %eligible if !defined $name;
 
-    my $query  = ref $name ? $name : qr/^$name$/i;
+    my $query  = $name->$_isa('Regexp') ? $name : qr/^$name$/i;
     my @result = grep { $eligible{$_} =~ m{$query} } keys %eligible;
 
     return @result if @result;
 
     # if we were given a regexp to search against, we're done.
-    return if ref $name;
+    return if $name->$_isa('Regexp');
 
     # skip regexp fallback if configured
     return
@@ -644,7 +645,7 @@ sub controller {
 
     my $appclass = ref($c) || $c;
     if( $name ) {
-        unless ( ref($name) ) { # Direct component hash lookup to avoid costly regexps
+        unless ( $name->$_isa('Regexp') ) { # Direct component hash lookup to avoid costly regexps
             my $comps = $c->components;
             my $check = $appclass."::Controller::".$name;
             return $c->_filter_component( $comps->{$check}, @args ) if exists $comps->{$check};
@@ -682,7 +683,7 @@ sub model {
     my ( $c, $name, @args ) = @_;
     my $appclass = ref($c) || $c;
     if( $name ) {
-        unless ( ref($name) ) { # Direct component hash lookup to avoid costly regexps
+        unless ( $name->$_isa('Regexp') ) { # Direct component hash lookup to avoid costly regexps
             my $comps = $c->components;
             my $check = $appclass."::Model::".$name;
             return $c->_filter_component( $comps->{$check}, @args ) if exists $comps->{$check};
@@ -741,7 +742,7 @@ sub view {
 
     my $appclass = ref($c) || $c;
     if( $name ) {
-        unless ( ref($name) ) { # Direct component hash lookup to avoid costly regexps
+        unless ( $name->$_isa('Regexp') ) { # Direct component hash lookup to avoid costly regexps
             my $comps = $c->components;
             my $check = $appclass."::View::".$name;
             if( exists $comps->{$check} ) {
index b04c3a3..c84e1d4 100644 (file)
@@ -1,4 +1,4 @@
-use Test::More tests => 51;
+use Test::More;
 use strict;
 use warnings;
 
@@ -24,6 +24,12 @@ my @complist =
     __PACKAGE__->setup_log;
 }
 
+{
+    package MyStringThing;
+
+    use overload '""' => sub { $_[0]->{string} }, fallback => 1;
+}
+
 is( MyMVCTestApp->view('View'), 'MyMVCTestApp::V::View', 'V::View ok' );
 
 is( MyMVCTestApp->controller('Controller'),
@@ -117,6 +123,18 @@ is ( MyMVCTestApp->model , 'MyMVCTestApp::Model::M', 'default_model in class met
     # object w/ qr{}
     is_deeply( [ MyMVCTestApp->model( qr{Test} ) ], [ MyMVCTestApp->components->{'MyMVCTestApp::Model::Test::Object'} ], 'Object returned' );
 
+    is_deeply([ MyMVCTestApp->model( bless({ string => 'Model' }, 'MyStringThing') ) ], [ MyMVCTestApp->components->{'MyMVCTestApp::M::Model'} ], 'Explicit model search with overloaded object');
+
+    {
+        my $warnings = 0;
+        no warnings 'redefine';
+        local *Catalyst::Log::warn = sub { $warnings++ };
+
+        # object w/ regexp fallback
+        is_deeply( [ MyMVCTestApp->model( bless({ string => 'Test' }, 'MyStringThing') ) ], [ MyMVCTestApp->components->{'MyMVCTestApp::Model::Test::Object'} ], 'Object returned' );
+        ok( $warnings, 'regexp fallback warnings' );
+    }
+
     {
         my $warnings = 0;
         no warnings 'redefine';
@@ -225,3 +243,5 @@ is ( MyMVCTestApp->model , 'MyMVCTestApp::Model::M', 'default_model in class met
     is( MyApp::WithoutRegexFallback->controller('Foo'), undef, 'no controller Foo found');
     ok( !$warnings, 'no regexp fallback warnings' );
 }
+
+done_testing;