make sure we can properly do utf8 constraints
John Napiorkowski [Thu, 26 Mar 2015 20:13:24 +0000 (15:13 -0500)]
Changes
lib/Catalyst.pm
t/arg_constraints.t

diff --git a/Changes b/Changes
index 3bcb925..70cbe01 100644 (file)
--- a/Changes
+++ b/Changes
@@ -9,6 +9,8 @@
     URL matches the defined args.
   - New top level document on Route matching. (Catalyst::RouteMatching).  This
     document is still in development, but is worth review and comments, please!
+  - uri_for now does more aggressing testing and warning if your args and captures
+    do not match expected number and type.
 
 5.90085 - 2015-03-25
   - Small change to Catalyst::Action to prevent autovivication of Args value (dim1++)
index 576f0c4..20595e0 100644 (file)
@@ -1447,6 +1447,10 @@ In general the scheme of the generated URI object will follow the incoming reque
 however if your targeted action or action chain has the Scheme attribute it will
 use that instead.
 
+Also, if the targeted Action or Action chain declares Args/CaptureArgs that have
+type constraints, we will require that your proposed URL verify on those declared
+constraints.
+
 =cut
 
 sub uri_for {
@@ -1469,15 +1473,17 @@ sub uri_for {
     foreach my $arg (@args) {
       if(ref($arg)||'' eq 'ARRAY') {
         push @encoded_args, [map {
-          my $encoded = encode_utf8 $_;
-          $encoded =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go;
-         $encoded;
+          #   my $encoded = encode_utf8 $_;
+          # $encoded =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go;
+          # $encoded;
+          $_
         } @$arg];
       } else {
         push @encoded_args, do {
-          my $encoded = encode_utf8 $arg;
-          $encoded =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go;
-          $encoded;
+          #   my $encoded = encode_utf8 $arg;
+          # $encoded =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go;
+          # $encoded;
+          $arg;
         }
       }
     }
@@ -1492,7 +1498,7 @@ sub uri_for {
 
         my $action = $path;
         my $expanded_action = $c->dispatcher->expand_action( $action );
-         my $num_captures = $expanded_action->number_of_captures;
+        my $num_captures = $expanded_action->number_of_captures;
 
         # ->uri_for( $action, \@captures_and_args, \%query_values? )
         if( !@encoded_args && $action->number_of_args ) {
@@ -1501,7 +1507,7 @@ sub uri_for {
 
         if($num_captures) {
           unless($expanded_action->match_captures($c, $captures)) {
-            carp "captures [@{$captures}] do not match the type constraints in action '$action'";
+            carp "captures [@{$captures}] do not match the type constraints in actionchain ending with '$action'";
             return;
           }
         }
@@ -1583,8 +1589,10 @@ sub uri_for {
       } @keys);
     }
 
-    #warn $base;
-    #warn $args;
+    $base = encode_utf8 $base;
+    $base =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go;
+    $args = encode_utf8 $args;
+    $args =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go;
     
     my $res = bless(\"${base}${args}${query}", $class);
     $res;
index e948dee..8bfa085 100644 (file)
@@ -1,6 +1,7 @@
 use warnings;
 use strict;
 use HTTP::Request::Common;
+use utf8;
 
 BEGIN {
   use Test::More;
@@ -20,7 +21,7 @@ BEGIN {
   use Types::Standard -types;
   use Type::Library
    -base,
-   -declare => qw( UserId User ContextLike );
+   -declare => qw( UserId Heart User ContextLike );
 
   extends "Types::Standard"; 
 
@@ -31,6 +32,10 @@ BEGIN {
    as Int,
    where { $_ < 5 };
 
+  declare Heart,
+   as Str,
+   where { $_ eq '♥' };
+
   # Tests using this are skipped pending deeper thought
   coerce User,
    from ContextLike,
@@ -61,7 +66,7 @@ BEGIN {
 
   use Moose;
   use MooseX::MethodAttributes;
-  use MyApp::Types qw/Tuple Int Str StrMatch ArrayRef UserId User/;
+  use MyApp::Types qw/Tuple Int Str StrMatch ArrayRef UserId User Heart/;
 
   extends 'Catalyst::Controller';
 
@@ -151,6 +156,11 @@ BEGIN {
     sub chained_zero3      :     Chained(chain_base2) PathPart('') Args(1) { $_[1]->res->body('chained_zero3') }
 
 
+  sub heart :Local Args(Heart) { }
+
+  sub utf8_base :Chained(/) CaptureArgs(Heart) { }
+    sub utf8_end :Chained(utf8_base) PathPart('') Args(Heart) { }
+
   sub default :Default {
     my ($self, $c, $int) = @_;
     $c->res->body('default');
@@ -409,12 +419,24 @@ SKIP: {
     ok my $url = ! eval { $c->uri_for($c->controller('Root')->action_for('finally'), ['a','a',3,4,4,'6']) };
   }
 
-}
-
-done_testing;
+  {
+    ok my $url = eval { $c->uri_for($c->controller('Root')->action_for('heart'), ['♥']) };
+    is $url, 'http://localhost/heart/%E2%99%A5';
+  }
 
+  {
+    ok my $url = ! eval { $c->uri_for($c->controller('Root')->action_for('heart'), ['1']) };
+  }
 
-__END__
+  {
+    ok my $url = eval { $c->uri_for($c->controller('Root')->action_for('utf8_end'), ['♥','♥']) };
+    is $url, 'http://localhost/utf8_base/%E2%99%A5/%E2%99%A5';
+  }
 
+  {
+    ok my $url = ! eval { $c->uri_for($c->controller('Root')->action_for('utf8_end'), ['2','1']) };
+  }
 
+}
 
+done_testing;