has _store => (is => 'ro', required => 1, init_arg => 'store');
-has _class => (is => 'ro', predicate => '_has_class');
+has _class => (is => 'ro', predicate => '_has_class', init_arg => 'class');
has _set_over => (is => 'ro', required => 1, init_arg => 'set_over');
while (my ($raw) = $stream->next) {
my $obj = do {
if (my ($obj) = $self->_key_cache_get_raw($raw)) {
- $obj # can't $self->_merge($obj, $raw) since $obj might have changed
+ # can't just $self->_merge($obj, $raw) since $obj might have changed
+ $self->_refresh($obj, $raw)
} else {
$self->_add_to_key_cache($self->_inflate($raw))
}
};
push @cache, $obj;
}
+ $self->_notify_observers(all_members => \@cache);
\@cache
}
: ()
}
+## observers
+
+has _observer_callbacks => (
+ is => 'ro', isa => 'ArrayRef', default => sub { [] }
+);
+
+method _notify_observers ($event, $payload) {
+ foreach my $cb (@{$self->_observer_callbacks}) {
+ $self->$cb($event, $payload);
+ }
+}
+
## thunking between the store representation and the set representation
#
# _inflate is raw data -> final repr
$obj
}
+method _refresh ($obj, $raw) {
+ # if $obj has been changed but not flushed we'd destroy data doing
+ # a blind merge - but if $obj has change tracking of some sort then
+ # we -could- do something safely, so this method exists to be mangled
+ # by subclasses
+ $obj
+}
+
method _deflate_spec ($spec) {
$spec
}
method add ($new) {
$self->_add_to_store($new);
$self->_add_to_caches($new);
+ $self->_notify_observers(add => $new);
$new
}
method remove ($old) {
$self->_remove_from_store($old);
$self->_remove_from_caches($old);
+ $self->_notify_observers(remove => $old);
$old
}
my $dsn = 'dbi:SQLite:tmp.db';
-my @expect;
-
sub sort_set {
sort { $a->{name} cmp $b->{name} } @_
}
-{
+sub setup_db {
unlink('tmp.db');
my $dbh = DBI->connect($dsn);
$dbh->do(q{
my $pop = $dbh->prepare(q{INSERT INTO person (name) VALUES (?)});
my @names = qw(Joe Jim Bob Pterry);
$pop->execute($_) for @names;
- @expect = sort_set do {
+ return sort_set do {
my $id = 0; map +{ id => ++$id, name => $_ }, @names
};
}
+my $db_store = DBIx::Data::Store->connect($dsn);
+
+sub make_store {
+ my ($crud) = @_;
+ DBIx::Data::Store::CRUD->new(
+ raw_store => $db_store,
+ select_sql => q{SELECT id, name FROM person},
+ select_column_order => [ qw(id name) ],
+ %$crud
+ );
+}
+
sub make_set {
- my ($set, $crud) = @_;
- DBIx::Data::Collection::Set->new(
+ my ($set, $crud, $class) = @_;
+ ($class || '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},
- select_column_order => [ qw(id name) ],
- %$crud,
- ),
+ store => make_store($crud),
%$set
);
}
-my $set = make_set;
+sub run_tests {
-is_deeply([ sort_set $set->flatten ], \@expect, 'Basic data out ok (flatten)');
+ my @expect = setup_db;
-{
- my $stream = $set->as_stream;
+ my $set = make_set;
- my @got; while (my ($next) = $stream->next) { push @got, $next }
+ is_deeply([ sort_set $set->flatten ], \@expect, 'Basic data out ok (flatten)');
- is_deeply([ sort_set @got ], \@expect, 'Basic data out ok (stream)');
-}
+ {
+ my $stream = $set->as_stream;
-$set = make_set { class => 'Spoon' };
+ my @got; while (my ($next) = $stream->next) { push @got, $next }
-is_deeply(
- [ sort_set $set->flatten ],
- [ map { bless({ %$_ }, 'Spoon') } @expect ],
- 'Basic data with class out ok'
-);
+ is_deeply([ sort_set @got ], \@expect, 'Basic data out ok (stream)');
+ }
-$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(@_)
- );
- },
- delete_sql => q{DELETE FROM person WHERE id = ?},
- delete_argument_order => [ 'id' ],
-};
+ $set = make_set { class => 'Spoon' };
-my $doug = $set->add({ name => 'Doug' });
+ is_deeply(
+ [ sort_set $set->flatten ],
+ [ map { bless({ %$_ }, 'Spoon') } @expect ],
+ 'Basic data with class out ok'
+ );
-ok($doug->{id}, 'id filled out in new row');
+ $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(@_)
+ );
+ },
+ delete_sql => q{DELETE FROM person WHERE id = ?},
+ delete_argument_order => [ 'id' ],
+ };
-my ($set_doug) = grep $_->{name} eq 'Doug', $set->flatten;
+ my $doug = $set->add({ name => 'Doug' });
-ok($set_doug, 'new row exists in flatten');
+ ok($doug->{id}, 'id filled out in new row');
-cmp_ok(refaddr($doug), '==', refaddr($set_doug), 'Same hashref returned');
+ my ($set_doug) = grep $_->{name} eq 'Doug', $set->flatten;
-$set->remove($doug);
+ ok($set_doug, 'new row exists in flatten');
-is_deeply([ sort_set $set->flatten ], \@expect, 'new row gone after remove');
+ cmp_ok(refaddr($doug), '==', refaddr($set_doug), 'Same hashref returned');
-$set = make_set;
+ $set->remove($doug);
-is_deeply([ sort_set $set->flatten ], \@expect, 'new row still gone on reload');
+ is_deeply([ sort_set $set->flatten ], \@expect, 'new row gone after remove');
-$set = make_set {}, {
- update_sql => q{UPDATE person SET name = ? WHERE id = ?},
- update_argument_order => [ qw(name id) ]
-};
+ $set = make_set;
-my ($pterry) = grep $_->{name} eq 'Pterry', $set->flatten;
+ is_deeply([ sort_set $set->flatten ], \@expect, 'new row still gone on reload');
-$pterry->{name} = 'Sir Pterry'; # http://xrl.us/bgse8s
+ $set = make_set {}, {
+ update_sql => q{UPDATE person SET name = ? WHERE id = ?},
+ update_argument_order => [ qw(name id) ]
+ };
+
+ my ($pterry) = grep $_->{name} eq 'Pterry', $set->flatten;
-$set->_update_in_store($pterry);
+ $pterry->{name} = 'Sir Pterry'; # http://xrl.us/bgse8s
-$set = make_set;
+ $set->_update_in_store($pterry);
-my ($fresh_pterry) = grep $_->{name} =~ /Pterry/, $set->flatten;
+ $set = make_set;
-is($fresh_pterry->{name}, 'Sir Pterry', 'Update persisted correctly');
+ my ($fresh_pterry) = grep $_->{name} =~ /Pterry/, $set->flatten;
-$set = make_set {}, {
- select_single_sql => q{SELECT id, name FROM person WHERE id = ?},
- select_single_argument_order => [ qw(id) ],
-};
+ is($fresh_pterry->{name}, 'Sir Pterry', 'Update persisted correctly');
+
+ $set = make_set {}, {
+ select_single_sql => q{SELECT id, name FROM person WHERE id = ?},
+ select_single_argument_order => [ qw(id) ],
+ };
-my $pterry_id = (grep $_->{name} eq 'Pterry', @expect)[0]->{id};
+ my $pterry_id = (grep $_->{name} eq 'Pterry', @expect)[0]->{id};
-$pterry = $set->get({ id => $pterry_id });
+ $pterry = $set->get({ id => $pterry_id });
-is($pterry->{name}, 'Sir Pterry', 'Pterry retrieved by id');
+ is($pterry->{name}, 'Sir Pterry', 'Pterry retrieved by id');
-ok(!defined($set->get({ id => -1 })), 'undef on missing id');
+ ok(!defined($set->get({ id => -1 })), 'undef on missing id');
-$pterry->{name} = 'Pterry';
+ $pterry->{name} = 'Pterry';
+
+ is_deeply([ sort_set $set->flatten ], \@expect, 'Basic data after fetch by id');
+
+ done_testing;
+}
-is_deeply([ sort_set $set->flatten ], \@expect, 'Basic data after fetch by id');
+run_tests unless caller;
-done_testing;
+1;