From: Matt S Trout Date: Fri, 8 Jan 2010 22:08:56 +0000 (+0000) Subject: skeleton insert code X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=3a2e7c1c79cf7db2ac812f2e250e939d3e88d639;p=dbsrgits%2FDBIx-Data-Store-old.git skeleton insert code --- diff --git a/lib/DBIx/Data/Collection/Set.pm b/lib/DBIx/Data/Collection/Set.pm index 9d54ccd..1c812fc 100644 --- a/lib/DBIx/Data/Collection/Set.pm +++ b/lib/DBIx/Data/Collection/Set.pm @@ -8,26 +8,102 @@ has _store => (is => 'ro', required => 1, init_arg => 'store'); has _class => (is => 'ro', predicate => '_has_class'); -has _member_cache => (is => 'rw', lazy_build => 1); +has _set_over => (is => 'ro', required => 1, init_arg => 'set_over'); + +## member cache (all members) + +has _member_cache => ( + is => 'rw', lazy_build => 1, + predicate => '_member_cache_built', +); method _build__member_cache { my $stream = $self->_new_raw_stream; my @cache; while (my ($raw) = $stream->next) { - push @cache, $self->_inflate($raw); + my $obj = do { + if (my ($obj) = $self->_key_cache_get_raw($raw)) { + $self->_merge($obj, $raw) + } else { + $self->_inflate($raw) + } + }; + push @cache, $obj; } \@cache; } +method _add_to_member_cache ($to_add) { + return unless $self->_member_cache_built; + push @{$self->_member_cache}, $to_add; +} + +## key cache - by primary/unique key + +has _key_cache => (is => 'ro', default => sub { {} }); + +method _add_to_key_cache ($to_add) { + $self->_key_cache->{$self->_object_to_id($to_add)} = $to_add; + return +} + +method _key_cache_has_raw ($raw) { + exists $self->_key_cache->{$self->_raw_to_id($raw)} +} + +method _key_cache_has_object ($obj) { + exists $self->_key_cache->{$self->_object_to_id($obj)} +} + +method _key_cache_get_raw ($raw) { + my $id = $self->_raw_to_id($raw); + exists $self->_key_cache->{$id} + ? ($self->_key_cache->{$id}) + : () +} + +method _key_cache_get_object ($obj) { + $self->_key_cache_get_raw($self->_deflate($obj)) +} + +## loading data + method _new_raw_stream { $self->_store->new_select_command([])->execute; } +## thunking between the store representation and the set representation +# +# _inflate is raw data -> final repr +# _deflate is final repr -> raw data +# _merge takes final repr + raw data and updates the repr +# (this is used for pk-generated values and later lazy loading) + method _inflate ($raw) { bless($raw, $self->_class) if $self->_has_class; $raw; } +method _deflate ($obj) { + +{ %$obj } +} + +method _merge ($obj, $raw) { + @{$obj}{keys %$raw} = values %$raw; + $obj; +} + +## methods to get ids + +method _raw_to_id ($raw) { + # XXX must escape this. or do something else. + join ';', map $raw->{$_}, @{$self->_set_over} +} + +method _object_to_id ($obj) { + $self->_raw_to_id($self->_deflate($obj)); +} + method flatten { @{$self->_member_cache}; } @@ -36,4 +112,22 @@ method as_stream { Data::Perl::Stream::Array->new(array => $self->_member_cache); } +method add ($new) { + $self->_add_to_store($new); + $self->_add_to_caches($new); + $new; +} + +method _add_to_store ($new) { + my $new_raw = $self->_deflate($new); + $self->_merge($new, $self->_store->new_insert_command($new_raw)->execute); + $self->_add_to_caches($new); + return +} + +method _add_to_caches ($new) { + $self->_add_to_member_cache($new); + $self->_add_to_key_cache($new); +} + 1; diff --git a/lib/DBIx/Data/Store.pm b/lib/DBIx/Data/Store.pm index e10bb0a..2713c2c 100644 --- a/lib/DBIx/Data/Store.pm +++ b/lib/DBIx/Data/Store.pm @@ -29,7 +29,8 @@ method new_call_command ($sql, $args) { method new_row_command ($sql, $args, $column_order) { return DBIx::Data::Store::Command::Row->new( - run => $sql, with => $args, against => $self->connection + run => $sql, with => $args, against => $self->connection, + column_order => $column_order ); } diff --git a/lib/DBIx/Data/Store/CRUD.pm b/lib/DBIx/Data/Store/CRUD.pm index 5ba2e89..eff5e58 100644 --- a/lib/DBIx/Data/Store/CRUD.pm +++ b/lib/DBIx/Data/Store/CRUD.pm @@ -10,6 +10,8 @@ foreach my $type (qw(select insert update delete)) { has "${type}_argument_order" => (is => 'ro', default => sub { [] }); } +has 'insert_command_constructor' => (is => 'ro'); + has "select_column_order" => (is => 'ro'); method new_select_command ($args) { @@ -25,17 +27,27 @@ method _unwrap_args_for ($type, $args) { [ @{$args}{@{$self->${\"${type}_argument_order"}}} ] } -method _new_call_command ($type, $args) { +method _new_command ($builder, $type, $args) { my $has_meth = "has_${type}_sql"; die "${self}->${has_meth}" unless $self->$has_meth; - $self->raw_store->new_call_command( + $self->$builder( $self->${\"${type}_sql"}, - $self->_unwrap_args_for($type => $args) + $self->_unwrap_args_for($type => $args), + ); +} + +method _new_call_command ($type, $args) { + $self->_new_command( + sub { shift->raw_store->new_call_command(@_) }, + $type => $args, ); } method new_insert_command ($args) { - $self->_new_call_command(insert => $args); + my $builder = $self->insert_command_constructor; + $builder + ? $self->_new_command($builder => insert => $args) + : $self->_new_call_command(insert => $args); } method new_update_command ($args) { diff --git a/lib/DBIx/Data/Store/Command/Insert/LastInsertId.pm b/lib/DBIx/Data/Store/Command/Insert/LastInsertId.pm new file mode 100644 index 0000000..eb9d904 --- /dev/null +++ b/lib/DBIx/Data/Store/Command/Insert/LastInsertId.pm @@ -0,0 +1,27 @@ +package DBIx::Data::Store::Command::Insert::LastInsertId; + +use Moose; +use Method::Signatures::Simple; + +has 'id_column' => (is => 'ro', required => 1); + +has 'insert_call_command' => (is => 'ro', required => 1); + +has 'raw_store' => (is => 'ro', required => 1); + +method execute { + $self->insert_call_command->execute; + return { $self->id_column => $self->_get_last_insert_id } +}; + +method _get_last_insert_id { + # this should probably involve some sort of call to the raw + # store to get the command object from it, but meh + $self->raw_store->connection->run(sub { + shift->last_insert_id((undef) x 4) + }); +} + +__PACKAGE__->meta->make_immutable; + +1; diff --git a/t/01basic_collection.t b/t/01basic_collection.t index 7b744cf..0c14444 100644 --- a/t/01basic_collection.t +++ b/t/01basic_collection.t @@ -35,6 +35,7 @@ sub sort_set { sub make_set { my ($set, $crud) = @_; DBIx::Data::Collection::Set->new( + set_over => [ 'id' ], store => DBIx::Data::Store::CRUD->new( raw_store => DBIx::Data::Store->connect($dsn), select_sql => q{SELECT id, name FROM person}, @@ -65,4 +66,24 @@ is_deeply( 'Basic data with class out ok' ); +$set = make_set {}, { + insert_sql => q{INSERT INTO person (name) VALUES (?) }, + insert_argument_order => [ 'name' ], + insert_command_constructor => sub { + require DBIx::Data::Store::Command::Insert::LastInsertId; + my $self = shift; + DBIx::Data::Store::Command::Insert::LastInsertId->new( + id_column => 'id', + raw_store => $self->raw_store, + insert_call_command => $self->raw_store->new_call_command(@_) + ); + } +}; + +my $doug = $set->add({ name => 'Doug' }); + +use Devel::Dwarn; + +Dwarn $doug; + done_testing;