rework the local-izing code to navigate the maze of references correctly
Matt S Trout [Tue, 29 Sep 2015 01:09:56 +0000 (01:09 +0000)]
lib/DBIx/Class/ParameterizedJoinHack.pm
lib/DBIx/Class/ResultSet/ParameterizedJoinHack.pm
t/00basic.t
t/lib/My/Schema/Result/Person.pm

index b0a5d7f..cfd5960 100644 (file)
@@ -22,9 +22,9 @@ sub parameterized_has_many {
   my $store = $class->$STORE({
     %{$class->$STORE||{}},
     $rel => { params => {}, args => $args },
-  });
+  })->{$rel};
   my $wrapped_code = sub {
-    my $params = $store->{$rel}{params};
+    my $params = $store->{params};
     my @missing = grep !exists $params->{$_}, @$args;
     die "Attempted to use parameterized rel ${rel} for ${class} without"
         ." passing parameters ".join(', ', @missing) if @missing;
index 14ed789..4de2973 100644 (file)
@@ -23,14 +23,22 @@ sub with_parameterized_join {
   );
 }
 
+sub _localize_parameters {
+  my ($self, $final, $params, $store, $first, @rest) = @_;
+  return $final->() unless $first;
+  local $store->{$first}{params} = $params->{$first};
+  $self->_localize_parameters($final, $params, $store, @rest);
+}
+
 sub call_with_parameters {
   my ($self, $method, @args) = @_;
   my %params = %{$self->{attrs}{join_parameters}||{}};
   my $store = $self->_parameterized_join_store;
-  local @{$store}{keys %params} = map {
-    +{ %{$store->{$_}}, params => $params{$_} }
-  } keys %params;
-  return $self->$method(@args);
+  return $self->_localize_parameters(
+    sub { $self->$method(@args) },
+    \%params, $store,
+    keys %params
+  );
 }
 
 sub _resolved_attrs { my $self = shift; $self->call_with_parameters($self->next::can, @_) }
index f24197b..23fe945 100644 (file)
@@ -1,7 +1,7 @@
 use strict;
 use warnings;
 use lib 't/lib';
-use Test::More qw(no_plan);
+use Test::More;
 use My::Schema;
 
 my $schema = My::Schema->connect('dbi:SQLite:dbname=:memory:');
@@ -27,14 +27,68 @@ $bob->create_related(assigned_tasks => {
     urgency => 30,
 });
 
-my $test_rs = $schema
-    ->resultset('Person')
-    ->search({ 'me.name' => { -like => 'Bob%' } });
+subtest 'has_many' => sub {
 
-is $test_rs->with_parameterized_join(
-    urgent_assigned_tasks => { urgency_threshold => 20 },
-)->count, 1, 'joined';
+    my $join_with_min = sub {
+        return shift->with_parameterized_join(
+            urgent_assigned_tasks => { urgency_threshold => $_[0] },
+        );
+    };
 
-is $test_rs->with_parameterized_join(
-    urgent_assigned_tasks => { urgency_threshold => 200 },
-)->count, 1, 'not joined';
+#    my $join_with_range = sub {
+#        return shift->with_parameterized_join(
+#            tasks_in_urgency_range => {
+#                min => $_[0],
+#                max => $_[1],
+#            },
+#        );
+#    };
+
+    my $search = sub {
+        return scalar shift->search(
+            { 'me.name' => { -like => 'Bob%' } },
+            {
+                '+select' => [{
+                    count => \['urgent_assigned_tasks.id'],
+                }],
+                '+as' => ['task_count'],
+            },
+        );
+    };
+
+    my $fetch_count = sub {
+        return shift->next->get_column('task_count');
+    };
+
+    subtest 'simple filter' => sub {
+        is $people->$join_with_min(19)->$search->$fetch_count,
+            2, 'filter min 19';
+        is $people->$join_with_min(29)->$search->$fetch_count,
+            1, 'filter min 29';
+        is $people->$join_with_min(39)->$search->$fetch_count,
+            0, 'filter min 39';
+    };
+
+    subtest 'multiple filters' => sub {
+        my $rs1 = $people->$join_with_min(19)->$search;
+        my $rs2 = $people->$join_with_min(29)->$search;
+        is $rs1->$fetch_count, 2, 'first';
+        is $rs2->$fetch_count, 1, 'second';
+    };
+
+    subtest 'overrides' => sub {
+        is $people
+            ->$join_with_min(190)
+            ->$join_with_min(19)
+            ->$search
+            ->$fetch_count,
+            2, 'overridden parameter';
+    };
+
+#    subtest 'multi parameter' => sub {
+#        is $people->$join_with_range(10, 30)->$search->$fetch_count,
+#            3, 'full range';
+#    };
+};
+
+done_testing;
index e38c12f..e89b3a6 100644 (file)
@@ -34,4 +34,20 @@ __PACKAGE__->parameterized_has_many(
   ]
 );
 
+__PACKAGE__->parameterized_has_many(
+  tasks_in_urgency_range => 'My::Schema::Result::Task',
+  [ [ qw( min max ) ], sub {
+      my $args = shift;
+      +{
+        "$args->{foreign_alias}.assigned_to_id" =>
+          { -ident => "$args->{self_alias}.id" },
+        "$args->{foreign_alias}.urgency" =>
+          { '>=', $_{min} },
+        "$args->{foreign_alias}.urgency" =>
+          { '<=', $_{max} },
+      }
+    }
+  ]
+);
+
 1;