created storage method to execute a coderef using master storage only, changed tnx_do...
John Napiorkowski [Wed, 28 May 2008 19:15:34 +0000 (19:15 +0000)]
lib/DBIx/Class/Storage/DBI/Replicated.pm
t/93storage_replication.t

index 3bfbf60..c95a084 100644 (file)
@@ -224,7 +224,6 @@ has 'write_handler' => (
         update
         delete
         dbh
-        txn_do
         txn_commit
         txn_rollback
         sth
@@ -374,6 +373,56 @@ sub all_storages {
        );
 }
 
+=head2 execute_reliably ($coderef, ?@args)
+
+Given a coderef, saves the current state of the L</read_handler>, forces it to
+use reliable storage (ie sets it to the master), executes a coderef and then
+restores the original state.
+
+Example:
+
+    my $reliably = sub {
+        my $name = shift @_;
+        $schema->resultset('User')->create({name=>$name});
+        my $user_rs = $schema->resultset('User')->find({name=>$name}); 
+    };
+
+    $schema->storage->execute_reliably($reliably, 'John');
+
+Use this when you must be certain of your database state, such as when you just
+inserted something and need to get a resultset including it, etc.
+
+=cut
+
+sub execute_reliably {
+    my ($self, $coderef, @args) = @_;
+       
+    unless( ref $coderef eq 'CODE') {
+        $self->throw_exception('Second argument must be a coderef');
+    }
+
+    ##Get copy of master storage
+    my $master = $self->master;
+    
+    ##Get whatever the current read hander is
+    my $current = $self->read_handler;
+    
+    ##Set the read handler to master
+    $self->read_handler($master);
+    
+    ## do whatever the caller needs
+    eval {
+        $coderef->(@args);
+    };
+    
+    if($@) {
+        $self->throw_exception("coderef returned an error: $@");
+    }
+  
+    ##Reset to the original state
+    $self->schema->storage->read_handler($current);    
+}
+
 =head2 set_reliable_storage
 
 Sets the current $schema to be 'reliable', that is all queries, both read and
@@ -404,6 +453,19 @@ sub set_balanced_storage {
     $schema->storage->read_handler($write_handler);
 }
 
+=head2 txn_do ($coderef)
+
+Overload to the txn_do method, which is delegated to whatever the
+L<write_handler> is set to.  We overload this in order to wrap in inside a
+L</execute_reliably> method.
+
+=cut
+
+sub txn_do {
+       my($self, $coderef, @args) = @_;
+       $self->execute_reliably($coderef, @args);
+}
+
 =head2 connected
 
 Check that the master and at least one of the replicants is connected.
@@ -550,11 +612,12 @@ sub disconnect {
 
 =head1 AUTHOR
 
-Norbert Csongrádi <bert@cpan.org>
+    John Napiorkowski <john.napiorkowski@takkle.com>
 
-Peter Siklósi <einon@einon.hu>
+Based on code originated by:
 
-John Napiorkowski <john.napiorkowski@takkle.com>
+    Norbert Csongrádi <bert@cpan.org>
+    Peter Siklósi <einon@einon.hu>
 
 =head1 LICENSE
 
index 7617af3..7765767 100644 (file)
@@ -2,13 +2,14 @@ use strict;
 use warnings;
 use lib qw(t/lib);
 use Test::More;
+use Test::Exception;
 use DBICTest;
 
 BEGIN {
     eval "use Moose; use Test::Moose";
     plan $@
         ? ( skip_all => 'needs Moose for testing' )
-        : ( tests => 50 );
+        : ( tests => 57 );
 }
 
 use_ok 'DBIx::Class::Storage::DBI::Replicated::Pool';
@@ -433,6 +434,52 @@ SKIP: {
            => "both replicants reactivated";        
 }
 
+## Test the reliably callback
+
+ok my $reliably = sub {
+       
+    ok $replicated->schema->resultset('Artist')->find(5)
+        => 'replicant reactivated';    
+       
+} => 'created coderef properly';
+
+$replicated->schema->storage->execute_reliably($reliably);
+
+## Try something with an error
+
+ok my $unreliably = sub {
+    
+    ok $replicated->schema->resultset('ArtistXX')->find(5)
+        => 'replicant reactivated'; 
+    
+} => 'created coderef properly';
+
+throws_ok {$replicated->schema->storage->execute_reliably($unreliably)} 
+    qr/coderef returned an error: Can't find source for ArtistXX/
+    => 'Bad coderef throws proper error';
+    
+## make sure transactions are set to execute_reliably
+
+ok my $transaction = sub {
+       
+       $replicated
+           ->schema
+           ->populate('Artist', [
+               [ qw/artistid name/ ],
+               [ 666, "Children of the Grave"],
+           ]);
+           
+   ok my $result = $replicated->schema->resultset('Artist')->find(666);
+   
+};
+
+$replicated->schema->txn_do($transaction);
+
+## Make sure replication came back
+
+ok $replicated->schema->resultset('Artist')->find(5)
+    => 'replicant reactivated';
+        
 ## Delete the old database files
 $replicated->cleanup;