nested txn_do doesn't create nested changesets
Yuval Kogman [Tue, 29 Jul 2008 19:08:51 +0000 (19:08 +0000)]
lib/DBIx/Class/Schema/Journal.pm
lib/DBIx/Class/Schema/Journal/DB.pm
t/01test.t
t/02noautodeploy.t [new file with mode: 0644]
t/lib/DBICTest/Schema.pm

index c056b29..8b50081 100644 (file)
@@ -7,11 +7,12 @@ use DBIx::Class::Schema::Journal::DB;
 
 __PACKAGE__->mk_classdata('journal_storage_type');
 __PACKAGE__->mk_classdata('journal_connection');
-__PACKAGE__->mk_classdata('journal_no_automatic_deploy');
+__PACKAGE__->mk_classdata('journal_deploy_on_connect');
 __PACKAGE__->mk_classdata('journal_sources'); ## [ source names ]
 __PACKAGE__->mk_classdata('journal_user'); ## [ class, field for user id ]
 __PACKAGE__->mk_classdata('_journal_schema'); ## schema object for journal
 __PACKAGE__->mk_classdata('_journal_internal_sources'); # the sources used to journal journal_sources
+__PACKAGE__->mk_classdata('journal_nested_changesets');
 
 our $VERSION = '0.01';
 
@@ -63,7 +64,7 @@ sub connection
     }
 
     ## get our own private version of the journaling sources
-   $self->_journal_schema($journal_schema->compose_namespace(blessed($self) . '::Journal'));
+    $self->_journal_schema($journal_schema->compose_namespace(blessed($self) . '::Journal'));
 
     ## Create auditlog+history per table
     my %j_sources = map { $_ => 1 } $self->journal_sources
@@ -82,8 +83,13 @@ sub connection
 
     $self->_journal_internal_sources(\@journal_sources);
 
+    if ( $self->journal_nested_changesets ) {
+        $self->_journal_schema->nested_changesets(1);
+        die "FIXME nested changeset schema not yet supported... add parent_id to ChangeSet here";
+    }
+
     $self->journal_schema_deploy()
-        unless $self->journal_no_automatic_deploy;
+        if $self->journal_deploy_on_connect;
 
     ## Set up relationship between changeset->user_id and this schema's user
     if(!@{$self->journal_user || []})
@@ -98,6 +104,17 @@ sub connection
     return $schema;
 }
 
+sub deploy
+{
+    my ( $self, $sqlt_args, @args ) = @_;
+
+    $self->next::method($sqlt_args, @args);
+
+    $sqlt_args ||= {};
+    local $sqlt_args->{sources} = $self->_journal_internal_sources;
+    $self->journal_schema_deploy($sqlt_args, @args);
+}
+
 sub journal_schema_deploy
 {
     my ( $self, $sqlt_args, @args ) = @_;
@@ -151,38 +168,32 @@ sub create_journal_for
 
 sub txn_do
 {
-    my ($self, $code) = @_;
+    my ($self, $user_code, @args) = @_;
 
-    ## Create a new changeset, then run $code as a transaction
-    my $cs = $self->_journal_schema->resultset('ChangeSet');
+    my $jschema = $self->_journal_schema;
 
-    $self->txn_begin;
-    my %changesetdata;
-    if( defined $self->_journal_schema->current_user() )
-    {
-        $changesetdata{user_id} = $self->_journal_schema->current_user();
-    }
-    if( defined $self->_journal_schema->current_session() )
-    {
-        $changesetdata{session_id} = $self->_journal_schema->current_session();
-    }
+    my $code;
 
-#         ( 
-#           $self->_journal_schema->current_user() 
-#           ? ( user_id => $self->_journal_schema->current_user()) 
-#           : (),
-#           $self->_journal_schema->current_session() 
-#           ? ( session_id => $self->_journal_schema->current_session() ) 
-#           : () 
-#         );
-    if(!%changesetdata)
+    my $current_changeset = $jschema->current_changeset;
+    if ( !$current_changeset || $self->journal_nested_changesets )
     {
-        %changesetdata = ( ID => undef );
+        my $current_changeset_ref = $jschema->_current_changeset_container;
+
+        unless ( $current_changeset_ref ) {
+            # this is a hash because scalar refs can't be localized
+            $current_changeset_ref = { };
+            $jschema->_current_changeset_container($current_changeset_ref);
+        }
+
+        # wrap the thunk with a new changeset creation
+        $code = sub {
+            my $changeset = $jschema->journal_create_changeset( parent_id => $current_changeset );
+            local $current_changeset_ref->{changeset} = $changeset->ID;
+            $user_code->(@_);
+        };
     }
-    my $changeset = $cs->create({ %changesetdata });
-    $self->_journal_schema->current_changeset($changeset->ID);
 
-    $self->next::method($code);
+    $self->next::method($code || $user_code);
 }
 
 sub changeset_user
index 7e2ac82..4f31cb1 100644 (file)
@@ -2,9 +2,21 @@ package DBIx::Class::Schema::Journal::DB;
 
 use base 'DBIx::Class::Schema';
 
-__PACKAGE__->mk_classdata('current_user');
-__PACKAGE__->mk_classdata('current_session');
-__PACKAGE__->mk_classdata('current_changeset');
+__PACKAGE__->mk_classdata('nested_changesets');
+__PACKAGE__->mk_group_accessors( simple => 'current_user' );
+__PACKAGE__->mk_group_accessors( simple => 'current_session' );
+__PACKAGE__->mk_group_accessors( simple => '_current_changeset_container' );
+
+# this is for localization of the current changeset
+sub current_changeset {
+    my ( $self, @args ) = @_;
+
+    $self->throw_error("setting current_changeset is not supported, use txn_do to create a new changeset") if @args;
+
+    my $ref = $self->_current_changeset_container;
+
+    return $ref && $ref->{changeset};
+}
 
 DBIx::Class::Schema::Journal::DB->load_classes(qw/
                                                ChangeSet
@@ -13,4 +25,26 @@ DBIx::Class::Schema::Journal::DB->load_classes(qw/
                                                AuditHistory
                                                /);
 
+sub journal_create_changeset {
+    my ( $self, @args ) = @_;
+
+    my %changesetdata = ( @args, ID => undef );
+
+    delete $changesetdata{parent_id} unless $self->nested_changesets;
+
+    if( defined( my $user = $self->current_user() ) )
+    {
+        $changesetdata{user_id} = $user;
+    }
+    if( defined( my $session = $self->current_session() ) )
+    {
+        $changesetdata{session_id} = $session;
+    }
+
+    ## Create a new changeset, then run $code as a transaction
+    my $cs = $self->resultset('ChangeSet');
+
+    $cs->create({ %changesetdata });
+}
+
 1;
index 9f419af..8de5a54 100644 (file)
@@ -10,7 +10,7 @@ BEGIN {
     eval "use DBD::SQLite";
     plan $@
         ? ( skip_all => 'needs DBD::SQLite for testing' )
-        : ( tests => 14 );
+        : ( tests => 15 );
 }
 
 my $schema = DBICTest->init_schema(no_populate => 1);
@@ -20,31 +20,28 @@ isa_ok($schema->_journal_schema, 'DBIx::Class::Schema::Journal::DB', 'Actually h
 isa_ok($schema->_journal_schema->source('CDAuditHistory'), 'DBIx::Class::ResultSource', 'CDAuditHistory source exists');
 isa_ok($schema->_journal_schema->source('ArtistAuditLog'), 'DBIx::Class::ResultSource', 'ArtistAuditLog source exists');
 
-{
-       my $count = eval { 
-               warn $schema->_journal_schema->resultset('ArtistAuditLog')->count;
-       };
-       my $e = $@;
-
-       is( $count, undef, "no count" );
-       like( $e, qr/table.*artist_audit_log/i, "missing table error" );
-}
-
-$schema->journal_schema_deploy();
-
 my $artist;
 my $new_cd = $schema->txn_do( sub {
+    my $current_changeset = $schema->_journal_schema->current_changeset;
+    ok( $current_changeset, "have a current changeset" );
+
     $artist = $schema->resultset('Artist')->create({
         name => 'Fred Bloggs',
     });
-    return  $schema->resultset('CD')->create({
-        title => 'Angry young man',
-        artist => $artist,
-        year => 2000,
+
+    $schema->txn_do(sub {
+        is( $current_changeset, $schema->_journal_schema->current_changeset, "nested txn doesn't create a new changeset" );
+        return $schema->resultset('CD')->create({
+            title => 'Angry young man',
+            artist => $artist,
+            year => 2000,
+        });
     });
 });
 isa_ok($new_cd, 'DBIx::Class::Journal', 'Created CD object');
 
+is( $schema->_journal_schema->current_changeset, undef, "no current changeset" );
+
 my $search = $schema->_journal_schema->resultset('CDAuditLog')->search();
 ok($search->count, 'Created an entry in the CD audit log');
 
diff --git a/t/02noautodeploy.t b/t/02noautodeploy.t
new file mode 100644 (file)
index 0000000..17a6024
--- /dev/null
@@ -0,0 +1,37 @@
+use strict;
+use warnings;  
+
+use Test::More;
+use lib qw(t/lib);
+use DBICTest;
+use Data::Dumper;
+
+BEGIN {
+    eval "use DBD::SQLite";
+    plan $@
+        ? ( skip_all => 'needs DBD::SQLite for testing' )
+        : ( 'no_plan' );
+}
+
+my $schema = DBICTest->init_schema(no_populate => 1, no_deploy => 1);
+
+ok($schema, 'Created a Schema');
+isa_ok($schema->_journal_schema, 'DBIx::Class::Schema::Journal::DB', 'Actually have a schema object for the journaling');
+isa_ok($schema->_journal_schema->source('CDAuditHistory'), 'DBIx::Class::ResultSource', 'CDAuditHistory source exists');
+isa_ok($schema->_journal_schema->source('ArtistAuditLog'), 'DBIx::Class::ResultSource', 'ArtistAuditLog source exists');
+
+my $count = eval { 
+       $schema->_journal_schema->resultset('ArtistAuditLog')->count;
+};
+my $e = $@;
+
+is( $count, undef, "no count" );
+like( $e, qr/table.*artist_audit_log/i, "missing table error" );
+
+$schema->journal_schema_deploy();
+
+$count = eval { $schema->_journal_schema->resultset('ArtistAuditLog')->count };
+
+is( $@, '', "no error" );
+is( $count, 0, "count is 0" );
+
index ffe1777..3e58f95 100644 (file)
@@ -5,8 +5,6 @@ use base qw/DBIx::Class::Schema/;
 
 __PACKAGE__->load_components(qw/+DBIx::Class::Schema::Journal/);
 
-__PACKAGE__->journal_no_automatic_deploy(1);
-
 __PACKAGE__->journal_connection(['dbi:SQLite:t/var/Audit.db']);
 
 no warnings qw/qw/;