mass update and delete
Matt S Trout [Tue, 12 Jan 2010 20:13:02 +0000 (20:13 +0000)]
lib/DBIx/Data/Collection/Set.pm
t/01basic_collection.t
t/03set_updates.t [new file with mode: 0644]

index 29b116d..e524177 100644 (file)
@@ -94,6 +94,11 @@ method _all_key_cache_members {
   values %{$self->_key_cache}
 }
 
+method _set_key_cache_members ($members) {
+  %{$self->_key_cache} = (map +($self->_object_to_id($_) => $_), @$members);
+  return
+}
+
 ## observers
 
 has _observer_callbacks => (
@@ -128,7 +133,7 @@ method _setup_observation_of ($other) {
       $self->_remove_from_caches($payload);
     } elsif ($event eq 'all_members') {
       # separate arrayref since future add will trigger push()
-      $self->_set_member_cache([ @$payload ]);
+      $self->_set_caches([ @$payload ]);
     }
   });
   return
@@ -142,6 +147,7 @@ method _setup_observation_of ($other) {
 #    (this is used for pk-generated values and later lazy loading)
 #
 # _deflate_spec is attributes of final repr -> raw data
+# _merge_spec is final repr + extra attributes and update repr
 
 method _inflate ($raw) {
   bless($raw, $self->_class) if $self->_has_class;
@@ -169,6 +175,11 @@ method _deflate_spec ($spec) {
   $spec
 }
 
+method _merge_spec ($obj, $spec) {
+  @{$obj}{keys %$spec} = values %$spec;
+  $obj
+}
+
 ## methods to get ids
 
 method _raw_to_id ($raw) {
@@ -201,6 +212,12 @@ method as_stream {
   Data::Perl::Stream::Array->new(array => $self->_member_cache);
 }
 
+method _set_caches ($members) {
+  $self->_set_member_cache($members);
+  $self->_set_key_cache_members($members);
+  return
+}
+
 ## load single row
 
 method get ($spec) {
@@ -219,7 +236,7 @@ method _get_from_store ($raw) {
   $self->_store->new_select_single_command($raw)->execute
 }
 
-## add to set
+## add member
 
 method add ($new) {
   $self->_add_to_store($new);
@@ -240,7 +257,7 @@ method _add_to_caches ($new) {
   $new
 }
 
-## remove from set
+## remove member
 
 method remove ($old) {
   $self->_remove_from_store($old);
@@ -259,7 +276,7 @@ method _remove_from_caches ($old) {
   $old
 }
 
-## update
+## update member
 
 method _update_in_store ($obj) {
   # this is currently a call command but we should think about it
@@ -268,4 +285,26 @@ method _update_in_store ($obj) {
   $self->_store->new_update_single_command($self->_deflate($obj))->execute
 }
 
+# I do wonder if we needed _merge_spec or if we'd be better off with
+# just using the raw merge routine ...
+
+method _update_set_in_store ($spec) {
+  $self->_store->new_update_command($self->_deflate_spec($spec))->execute;
+  if ($self->_member_cache_built) {
+    my $cache = $self->_member_cache;
+    foreach my $obj (@{$cache}) {
+      $self->_merge_spec($obj, $spec);
+    }
+    $self->_notify_observers(all_members => $cache);
+  }
+  return
+}
+
+method _remove_set_from_store {
+  $self->_store->new_delete_command->execute;
+  $self->_set_caches([]);
+  $self->_notify_observers(all_members => []);
+  return
+}
+
 1;
index 8ad5fc7..ee20e11 100644 (file)
@@ -10,15 +10,19 @@ use Devel::Dwarn;
 use strict;
 use warnings FATAL => 'all';
 
-my $dsn = 'dbi:SQLite:tmp.db';
-
 sub sort_set {
   sort { $a->{name} cmp $b->{name} } @_
 }
 
-sub setup_db {
+my $dsn = 'dbi:SQLite:tmp.db';
+
+sub setup_dbh {
   unlink('tmp.db');
-  my $dbh = DBI->connect($dsn);
+  return DBI->connect($dsn)
+}
+
+sub setup_db {
+  my $dbh = setup_dbh;
   $dbh->do(q{
     CREATE TABLE person (
       id INTEGER NOT NULL PRIMARY KEY,
@@ -35,6 +39,8 @@ sub setup_db {
 
 my $db_store = DBIx::Data::Store->connect($dsn);
 
+sub raw_store { $db_store }
+
 sub make_store {
   my ($crud) = @_;
   DBIx::Data::Store::CRUD->new(
diff --git a/t/03set_updates.t b/t/03set_updates.t
new file mode 100644 (file)
index 0000000..3eb80ce
--- /dev/null
@@ -0,0 +1,92 @@
+use strict;
+use warnings FATAL => 'all';
+use Test::More;
+
+BEGIN {
+  package BasicCollection;
+  require 't/01basic_collection.t';
+}
+
+sub setup_dbh { BasicCollection::setup_dbh }
+
+my $dbh;
+
+sub setup_db {
+  $dbh = setup_dbh;
+  $dbh->do("CREATE TABLE spoon (
+    id INTEGER NOT NULL PRIMARY KEY,
+    last_frobnicated DATE NOT NULL
+  )");
+  $dbh->do("INSERT INTO spoon (last_frobnicated) VALUES ('2009-03-30')")
+    for 1 .. 5;
+}
+
+setup_db;
+
+my $store = DBIx::Data::Store::CRUD->new(
+  raw_store => BasicCollection::raw_store,
+  select_sql => 'SELECT id, last_frobnicated FROM spoon',
+  select_column_order => [ qw(id last_frobbed) ],
+  update_sql => 'UPDATE spoon SET last_frobnicated = ?',
+  update_argument_order => [ qw(last_frobbed) ],
+  delete_sql => 'DELETE FROM spoon',
+);
+
+sub make_set {
+  DBIx::Data::Collection::Set->new(
+    store => $store,
+    set_over => [ 'id' ],
+  );
+}
+
+sub sort_set { sort { $a->{id} <=> $b->{id} } @_ }
+
+my $set = make_set;
+
+my $x = 0;
+
+my @expected = map +{ id => ++$x, last_frobbed => '2009-03-30' }, 1 .. 5;
+
+my @data = $set->flatten;
+
+is_deeply(
+  \@data,
+  \@expected,
+  'Simple fetch'
+);
+
+$set->_update_set_in_store({ last_frobbed => '2009-04-01' });
+
+$_->{last_frobbed} = '2009-04-01' for @expected;
+
+is_deeply(
+  \@data,
+  \@expected,
+  'After update'
+);
+
+is_deeply(
+  [ sort_set make_set->flatten ],
+  \@expected,
+  'Refetch'
+);
+
+$set->_remove_set_from_store;
+
+@expected = ();
+
+@data = $set->flatten;
+
+is_deeply(
+  \@data,
+  \@expected,
+  'After delete'
+);
+
+is_deeply(
+  [ sort_set make_set->flatten ],
+  \@expected,
+  'Refetch'
+);
+
+done_testing;