From: Matt S Trout Date: Tue, 12 Jan 2010 20:13:02 +0000 (+0000) Subject: mass update and delete X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=32746a09efe0e64a8cbb7b7b9edcb520c374fdec;p=dbsrgits%2FDBIx-Data-Store-old.git mass update and delete --- diff --git a/lib/DBIx/Data/Collection/Set.pm b/lib/DBIx/Data/Collection/Set.pm index 29b116d..e524177 100644 --- a/lib/DBIx/Data/Collection/Set.pm +++ b/lib/DBIx/Data/Collection/Set.pm @@ -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; diff --git a/t/01basic_collection.t b/t/01basic_collection.t index 8ad5fc7..ee20e11 100644 --- a/t/01basic_collection.t +++ b/t/01basic_collection.t @@ -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 index 0000000..3eb80ce --- /dev/null +++ b/t/03set_updates.t @@ -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;