simplify Directory and add exceptions;
Tara L Andrews [Sat, 21 Jan 2012 20:06:07 +0000 (21:06 +0100)]
change reading_sequence interface;
populate ->text and ->layertext in witnesses for all 'real' parsers

15 files changed:
Makefile.PL
lib/Text/Tradition/Collation.pm
lib/Text/Tradition/Directory.pm
lib/Text/Tradition/Error.pm [new file with mode: 0644]
lib/Text/Tradition/Parser/BaseText.pm
lib/Text/Tradition/Parser/CTE.pm
lib/Text/Tradition/Parser/CollateText.pm
lib/Text/Tradition/Parser/CollateX.pm
lib/Text/Tradition/Parser/Self.pm
lib/Text/Tradition/Parser/TEI.pm
lib/Text/Tradition/Parser/Tabular.pm
lib/Text/Tradition/Witness.pm
script/make_tradition.pl
t/text_tradition_directory.t
t/text_tradition_parser_tabular.t

index acd2439..c5427c7 100644 (file)
@@ -23,6 +23,8 @@ requires( 'Moose' );
 requires( 'Moose::Util::TypeConstraints' );
 requires( 'Text::CSV_XS' );
 requires( 'Text::CSV::Simple' ); # TODO delete
+requires( 'Throwable::X' );
+requires( 'TryCatch' );
 requires( 'XML::LibXML' );
 requires( 'XML::LibXML::XPathContext' );
 build_requires( 'Test::Warn' );
index 10d215e..77a367e 100644 (file)
@@ -883,7 +883,7 @@ sub make_alignment_table {
                { 'witness' => $wit->sigil, 'tokens' => \@row } );
         if( $wit->is_layered ) {
                my @wit_ac_path = $self->reading_sequence( $self->start, $self->end, 
-                       $wit->sigil.$self->ac_label, $wit->sigil );
+                       $wit->sigil.$self->ac_label );
             my @ac_row = _make_witness_row( \@wit_ac_path, \@all_pos, $noderefs );
                        push( @{$table->{'alignment'}},
                                { 'witness' => $wit->sigil.$self->ac_label, 'tokens' => \@ac_row } );
@@ -961,7 +961,7 @@ used wherever no path exists for $sigil or $backup.
 # TODO Get rid of backup; we should know from what witness is whether we need it.
 
 sub reading_sequence {
-    my( $self, $start, $end, $witness, $backup ) = @_;
+    my( $self, $start, $end, $witness ) = @_;
 
     $witness = $self->baselabel unless $witness;
     my @readings = ( $start );
@@ -974,7 +974,7 @@ sub reading_sequence {
         }
         $seen{$n->id} = 1;
         
-        my $next = $self->next_reading( $n, $witness, $backup );
+        my $next = $self->next_reading( $n, $witness );
         unless( $next ) {
             warn "Did not find any path for $witness from reading " . $n->id;
             last;
@@ -1021,7 +1021,15 @@ sub prior_reading {
 }
 
 sub _find_linked_reading {
-    my( $self, $direction, $node, $path, $alt_path ) = @_;
+    my( $self, $direction, $node, $path ) = @_;
+    
+    # Get a backup if we are dealing with a layered witness
+    my $alt_path;
+    my $aclabel = $self->ac_label;
+    if( $path && $path =~ /^(.*)\Q$aclabel\E$/ ) {
+       $alt_path = $1;
+    }
+    
     my @linked_paths = $direction eq 'next' 
         ? $self->sequence->edges_from( $node ) 
         : $self->sequence->edges_to( $node );
@@ -1090,10 +1098,10 @@ the generation of a subset of the witness text.
 =cut
 
 sub path_text {
-       my( $self, $wit, $backup, $start, $end ) = @_;
+       my( $self, $wit, $start, $end ) = @_;
        $start = $self->start unless $start;
        $end = $self->end unless $end;
-       my @path = grep { !$_->is_meta } $self->reading_sequence( $start, $end, $wit, $backup );
+       my @path = grep { !$_->is_meta } $self->reading_sequence( $start, $end, $wit );
        return join( ' ', map { $_->text } @path );
 }
 
@@ -1268,6 +1276,28 @@ sub flatten_ranks {
     }
 }
 
+=head2 text_from_paths
+
+Calculate the text array for all witnesses from the path, for later consistency
+checking.  Only to be used if there is no non-graph-based way to know the
+original texts.
+
+=cut
+
+sub text_from_paths {
+       my $self = shift;
+    foreach my $wit ( $self->tradition->witnesses ) {
+       my @text = split( /\s+/, 
+               $self->reading_sequence( $self->start, $self->end, $wit->sigil ) );
+       $wit->text( \@text );
+       if( $wit->is_layered ) {
+                       my @uctext = split( /\s+/, 
+                               $self->reading_sequence( $self->start, $self->end, 
+                                       $wit->sigil.$self->ac_label ) );
+                       $wit->text( \@uctext );
+       }
+    }    
+}
 
 =head1 UTILITY FUNCTIONS
 
index 20c3301..0f7da99 100644 (file)
@@ -6,6 +6,7 @@ use Moose;
 use KiokuDB::GC::Naive;
 use KiokuDB::TypeMap;
 use KiokuDB::TypeMap::Entry::Naive;
+use Text::Tradition::Error;
 
 extends 'KiokuX::Model';
 
@@ -53,7 +54,7 @@ Writes the given tradition to the database, returning its ID.
 
 =begin testing
 
-use Test::Warn;
+use TryCatch;
 use File::Temp;
 use Text::Tradition;
 use_ok 'Text::Tradition::Directory';
@@ -62,39 +63,76 @@ my $fh = File::Temp->new();
 my $file = $fh->filename;
 $fh->close;
 my $dsn = "dbi:SQLite:dbname=$file";
-
-my $d = Text::Tradition::Directory->new( 'dsn' => $dsn,
-       'extra_args' => { 'create' => 1 } );
-is( ref $d, 'Text::Tradition::Directory', "Got directory object" );
-
-my $scope = $d->new_scope;
+my $uuid;
 my $t = Text::Tradition->new( 
        'name'  => 'inline', 
        'input' => 'Tabular',
        'file'  => 't/data/simple.txt',
        );
-my $uuid = $d->save( $t );
-ok( $uuid, "Saved test tradition" );
-
-my $s = $t->add_stemma( 't/data/simple.dot' );
-ok( $d->save( $t ), "Updated tradition with stemma" );
-is( $d->tradition( $uuid ), $t, "Correct tradition returned for id" );
-is( $d->tradition( $uuid )->stemma, $s, "...and it has the correct stemma" );
-warning_like { $d->save( $s ) } qr/not a Text::Tradition/, "Correctly failed to save stemma directly";
-
-my $e = Text::Tradition::Directory->new( 'dsn' => $dsn );
-$scope = $e->new_scope;
-is( scalar $e->tradition_ids, 1, "Directory index has our tradition" );
-my $te = $e->tradition( $uuid );
-is( $te->name, $t->name, "Retrieved the tradition from a new directory" );
-my $sid = $e->object_to_id( $te->stemma );
-warning_like { $e->tradition( $sid ) } qr/not a Text::Tradition/, "Did not retrieve stemma via tradition call";
-warning_like { $e->delete( $sid ) } qr/Cannot directly delete non-Tradition object/, "Stemma object not deleted from DB";
-$e->delete( $uuid );
-ok( !$e->exists( $uuid ), "Object is deleted from DB" );
-ok( !$e->exists( $sid ), "Object stemma also deleted from DB" );
-is( scalar $e->tradition_ids, 0, "Object is deleted from index" );
 
+{
+       my $d = Text::Tradition::Directory->new( 'dsn' => $dsn,
+               'extra_args' => { 'create' => 1 } );
+       is( ref $d, 'Text::Tradition::Directory', "Got directory object" );
+       
+       my $scope = $d->new_scope;
+       $uuid = $d->save( $t );
+       ok( $uuid, "Saved test tradition" );
+       
+       my $s = $t->add_stemma( 't/data/simple.dot' );
+       ok( $d->save( $t ), "Updated tradition with stemma" );
+       is( $d->tradition( $uuid ), $t, "Correct tradition returned for id" );
+       is( $d->tradition( $uuid )->stemma, $s, "...and it has the correct stemma" );
+       try {
+               $d->save( $s );
+       } catch( Text::Tradition::Error $e ) {
+               is( $e->ident, 'database error', "Got exception trying to save stemma directly" );
+               like( $e->message, qr/Cannot directly save non-Tradition object/, 
+                       "Exception has correct message" );
+       }
+}
+my $nt = Text::Tradition->new(
+       'name' => 'CX',
+       'input' => 'CollateX',
+       'file' => 't/data/Collatex-16.xml',
+       );
+is( ref( $nt ), 'Text::Tradition', "Made new tradition" );
+
+{
+       my $f = Text::Tradition::Directory->new( 'dsn' => $dsn );
+       my $scope = $f->new_scope;
+       is( scalar $f->tradition_ids, 1, "Directory index has our tradition" );
+       my $nuuid = $f->save( $nt );
+       ok( $nuuid, "Stored second tradition" );
+       is( scalar $f->tradition_ids, 2, "Directory index has both traditions" );
+       my $tf = $f->tradition( $uuid );
+       is( $tf->name, $t->name, "Retrieved the tradition from a new directory" );
+       my $sid = $f->object_to_id( $tf->stemma );
+       try {
+               $f->tradition( $sid );
+       } catch( Text::Tradition::Error $e ) {
+               is( $e->ident, 'database error', "Got exception trying to fetch stemma directly" );
+               like( $e->message, qr/not a Text::Tradition/, "Exception has correct message" );
+       }
+       try {
+               $f->delete( $sid );
+       } catch( Text::Tradition::Error $e ) {
+               is( $e->ident, 'database error', "Got exception trying to delete stemma directly" );
+               like( $e->message, qr/Cannot directly delete non-Tradition object/, 
+                       "Exception has correct message" );
+       }
+       $f->delete( $uuid );
+       ok( !$f->exists( $uuid ), "Object is deleted from DB" );
+       ok( !$f->exists( $sid ), "Object stemma also deleted from DB" );
+       is( scalar $f->tradition_ids, 1, "Object is deleted from index" );
+}
+
+SKIP: {
+       skip 'Have yet to figure out garbage collection', 1;
+       my $g = Text::Tradition::Directory->new( 'dsn' => $dsn );
+       my $scope = $g->new_scope;
+       is( scalar $g->tradition_ids, 1, "Now one object in new directory index" );
+}
 
 =end testing
 
@@ -113,77 +151,60 @@ has +typemap => (
        },
 );
 
-has tradition_index => (
-    traits => ['Hash'],
-    isa => 'HashRef[HashRef[Str]]',
-    handles => {
-        add_index              => 'set',
-        del_index              => 'delete',
-        info                   => 'get',
-        tradition_ids  => 'keys',
-    },
-    default => sub { {} },
-    );
-
-# Populate the tradition index.
-sub BUILD {
+before [ qw/ store update insert delete / ] => sub {
        my $self = shift;
-       my $stream = $self->root_set;
-       until( $stream->is_done ) {
-               foreach my $obj ( $stream->items ) {
-                       my $uuid = $self->object_to_id( $obj );
-                       if( ref( $obj ) eq 'Text::Tradition' ) {
-                                $self->add_index( $uuid => { 'name' => $obj->name, 
-                                       'id' => $uuid, 'has_stemma' => $obj->has_stemma } );
-                       } else {
-                               warn "Found root object in DB that is not a Text::Tradition";
+       my @nontrad;
+       foreach my $obj ( @_ ) {
+               if( ref( $obj ) && ref( $obj ) ne 'Text::Tradition' ) {
+                       # Is it an id => Tradition hash?
+                       if( ref( $obj ) eq 'HASH' && keys( %$obj ) == 1 ) {
+                               my( $k ) = keys %$obj;
+                               next if ref( $obj->{$k} ) eq 'Text::Tradition';
                        }
+                       push( @nontrad, $obj );
                }
        }
-       return $self;
-}
+       if( @nontrad ) {
+               throw( "Cannot directly save non-Tradition object of type "
+                       . ref( $nontrad[0] ) );
+       }
+};
 
 # If a tradition is deleted, remove it from the index.
-around delete => sub {
-       my $orig = shift;
+after delete => sub {
        my $self = shift;
-       warn "Will only delete one tradition at a time" if @_ > 1;
-       my $arg = shift;
-       my $obj = ref( $arg ) ? $arg : $self->lookup( $arg );
-       my $id = ref( $arg ) ? $self->object_to_id( $arg ) : $arg;
-       unless( ref $obj eq 'Text::Tradition' ) {
-               warn "Cannot directly delete non-Tradition object $obj";
-               return;
-       }
-       $self->$orig( $arg );
        my $gc = KiokuDB::GC::Naive->new( backend => $self->directory->backend );
-       $self->$orig( $gc->garbage->members );
-       $self->del_index( $id );
+       $self->directory->backend->delete( $gc->garbage->members );
 };
 
 sub save {
-       my( $self, $obj ) = @_;
-       unless( ref( $obj ) eq 'Text::Tradition' ) {
-               warn "Object $obj is not a Text::Tradition";
-               return;
-       }
-       my $uuid = $self->store( $obj );
-       $self->add_index( $uuid => { 'name' => $obj->name, 
-                                       'id' => $uuid, 'has_stemma' => $obj->has_stemma } ) if $uuid;
-       return $uuid;
+       my $self = shift;
+       return $self->store( @_ );
 }
 
-
 sub tradition {
        my( $self, $id ) = @_;
        my $obj = $self->lookup( $id );
        unless( ref( $obj ) eq 'Text::Tradition' ) {
-               warn "Retrieved object is a " . ref( $obj ) . ", not a Text::Tradition";
-               return;
+               throw( "Retrieved object is a " . ref( $obj ) . ", not a Text::Tradition" );
        }
        return $obj;
 }
 
+sub tradition_ids {
+       my $self = shift;
+       my @ids;
+       $self->scan( sub { push( @ids, $self->object_to_id( @_ ) ) } );
+       return @ids;
+}
+
+sub throw {
+       Text::Tradition::Error->throw( 
+               'ident' => 'database error',
+               'message' => $_[0],
+               );
+}
+
 1;
        
                
\ No newline at end of file
diff --git a/lib/Text/Tradition/Error.pm b/lib/Text/Tradition/Error.pm
new file mode 100644 (file)
index 0000000..14bfa79
--- /dev/null
@@ -0,0 +1,31 @@
+package Text::Tradition::Error;
+
+use strict;
+use warnings;
+use Moose;
+use overload '""' => \&_stringify, 'fallback' => 1;
+
+with qw/ Throwable::X /;
+use Throwable::X -all;
+
+sub _stringify {
+       my $self = shift;
+       return "Error: " . $self->ident . " // " . $self->message;
+}
+
+no Moose;
+__PACKAGE__->meta->make_immutable;
+
+=head1 NAME
+
+Text::Tradition::Error - throwable error class for CollateX package
+
+=head1 DESCRIPTION
+
+A basic exception class to throw around, as it were.
+
+=cut
+
+=head1 AUTHOR
+
+Tara L Andrews E<lt>aurum@cpan.orgE<gt>
index 5744542..e07cdec 100644 (file)
@@ -301,7 +301,7 @@ sub merge_base {
 #                             $rel->type, $rel->from->id, $rel->to->id );
 #         }
 #     }
-    # $collation->calculate_ranks();
+    $collation->calculate_ranks();
 }
 
 =item B<read_base>
index 32b6a71..e706906 100644 (file)
@@ -110,6 +110,10 @@ sub parse {
     # Finally, add explicit witness paths, remove the base paths, and remove
     # the app/anchor tags.
     expand_all_paths( $c );
+
+    # Save the text for each witness so that we can ensure consistency
+    # later on
+       $tradition->collation->text_from_paths();       
 }
 
 sub _stringify_sigil {
@@ -331,7 +335,7 @@ sub expand_all_paths {
         $wit->path( \@path );
         if( $has_ac{$sig} ) {
             my @ac_path = grep { !$_->is_ph } 
-                $c->reading_sequence( $c->start, $c->end, $sig.$c->ac_label, $sig );
+                $c->reading_sequence( $c->start, $c->end, $sig.$c->ac_label );
             $wit->uncorrected_path( \@ac_path );
         }
     }   
index 944a8dd..21e54bd 100644 (file)
@@ -270,7 +270,6 @@ sub merge_stone_apparatus {
             } # end processing of $app
         } # end foreach my $app in line
     } # end while <line>
-    $DB::single = 1;
     
     # Now reconcile all the paths in the collation, and delete our
     # temporary anchor nodes.
@@ -278,6 +277,10 @@ sub merge_stone_apparatus {
     
     # Finally, calculate the ranks we've got.
     # $c->calculate_ranks;
+    
+    # Save the text for each witness so that we can ensure consistency
+    # later on
+       $tradition->collation->text_from_paths();       
 }
 
 sub _find_reading_on_line {
@@ -552,7 +555,7 @@ sub expand_all_paths {
         $wit->path( \@path );
         if( $ALL_SIGLA{$sig} > 1 ) {
             my @ac_path = grep { $_->name !~ /ATTACH/ } 
-                $c->reading_sequence( $c->start, $c->end, $sig.$c->ac_label, $sig );
+                $c->reading_sequence( $c->start, $c->end, $sig.$c->ac_label );
             $wit->uncorrected_path( \@ac_path );
         }
     }   
index 1ee448b..0d9db22 100644 (file)
@@ -157,6 +157,10 @@ sub parse {
     
     # Rank the readings.
     $collation->calculate_ranks() if $collation->linear;
+
+    # Save the text for each witness so that we can ensure consistency
+    # later on
+       $tradition->collation->text_from_paths();       
 }
     
 =head1 BUGS / TODO
index dee5969..b1082f3 100644 (file)
@@ -214,6 +214,11 @@ sub parse {
                $collation->add_relationship( $from->{$IDKEY}, $to->{$IDKEY}, 
                        $relationship_opts );
        }
+       
+    # Save the text for each witness so that we can ensure consistency
+    # later on
+       $tradition->collation->text_from_paths();       
+
 }
 
 1;
index afea435..3131774 100644 (file)
@@ -188,6 +188,10 @@ sub parse {
     # Now that we have ranks, see if we have distinct nodes with identical
     # text and identical rank that can be merged.
     $tradition->collation->flatten_ranks();
+    
+    # Save the text for each witness so that we can ensure consistency
+    # later on
+       $tradition->collation->text_from_paths();       
 }
 
 sub _clean_sequence {
index 4b53e3c..bf2e077 100644 (file)
@@ -100,7 +100,7 @@ foreach my $k ( keys %seen_wits ) {
                ok( $wit->has_layertext, "Witness $k has an a.c. version" );
                my $origtext = join( ' ', @{$wit->layertext} );
                my $acsig = $wit->sigil . $t->collation->ac_label;
-               my $graphtext = $t->collation->path_text( $acsig, $wit->sigil );
+               my $graphtext = $t->collation->path_text( $acsig );
                is( $graphtext, $origtext, "Collation matches original a.c. for witness $k" );
        } else {
                ok( !$wit->is_layered, "Witness $k not marked as layered" );
@@ -209,6 +209,7 @@ sub parse {
        my $ac_wit = $tradition->witness( $a );
         my $main_wit = $tradition->witness( $ac_wits{$a} );
         next unless $main_wit;
+        $main_wit->is_layered(1);
         $main_wit->uncorrected_path( $ac_wit->path );
         $tradition->del_witness( $ac_wit );
     }
@@ -219,6 +220,23 @@ sub parse {
        foreach my $rdg ( grep { $_->is_lacuna } $c->readings ) {
                $c->del_reading( $rdg ) unless $c->reading_witnesses( $rdg );
        }
+       
+       # Do a consistency check.
+       foreach my $wit ( $tradition->witnesses ) {
+               my $pathtext = $c->path_text( $wit->sigil );
+               my $origtext = join( ' ', @{$wit->text} );
+               warn "Text differs for witness " . $wit->sigil 
+                       unless $pathtext eq $origtext;
+               if( $wit->is_layered ) {
+                       $pathtext = $c->path_text( $wit->sigil.$c->ac_label );
+                       $origtext = join( ' ', @{$wit->layertext} );
+                       warn "Ante-corr text differs for witness " . $wit->sigil
+                               unless $pathtext eq $origtext;
+               } else {
+                       warn "Text " . $wit->sigil . " has a layered text but is not marked as layered"
+                               if $wit->has_layertext;
+               }
+       }
 }
 
 sub make_nodes {
index 6ae078d..776cebe 100644 (file)
@@ -32,7 +32,12 @@ Create a new witness.  Options include:
 =item * sigil - A short code to represent the manuscript.  Required.
 
 =item * text - An array of strings (words) that contains the text of the
-manuscript.
+manuscript.  This should not change after the witness has been instantiated,
+and the path through the collation should always match it.
+
+=item * layertext - An array of strings (words) that contains the layered text,
+if any, of the manuscript.  This should not change after the witness has been 
+instantiated, and the path through the collation should always match it.
 
 =item * source - A reference to the text, such as a filename, if it is not
 given in the 'text' option.
index b3b5b44..309033b 100755 (executable)
@@ -5,23 +5,27 @@ use strict;
 use warnings;
 use Getopt::Long;
 use Text::Tradition;
+use Text::Tradition::Directory;
 use Text::Tradition::StemmaUtil;
 
 binmode STDERR, ":utf8";
 binmode STDOUT, ":utf8";
 eval { no warnings; binmode $DB::OUT, ":utf8"; };
 
-my( $informat, $inbase, $outformat, $help, $linear, $name, $HACK, $sep ) 
-    = ( '', '', '', '', 1, 'Tradition', 0, "\t" );
+my( $informat, $inbase, $outformat, $help, $linear, $name, $HACK, $sep, $stemmafile, $dsn ) 
+    = ( '', '', '', '', 1, 'Tradition', 0, "\t", '',
+       "dbi:SQLite:dbname=stemmaweb/db/traditions.db" );
 
 GetOptions( 'i|in=s'    => \$informat,
             'b|base=s'  => \$inbase,
             'o|out=s'   => \$outformat,
             'l|linear!' => \$linear,
-            'n|name=s'    => \$name,
+            'n|name=s'  => \$name,
             'h|help'    => \$help,
+            's|stemma=s' => \$stemmafile,
             'sep=s'            => \$sep,
             'hack'      => \$HACK,
+            'dsn=s'            => \$dsn,
     );
 
 if( $help ) {
@@ -39,8 +43,8 @@ $informat = 'TEI' if $informat =~ /^tei$/i;
 $informat = 'Tabular' if $informat =~ /^tab$/i;
 $informat = 'CollateText' if $informat =~ /^stone$/i;
 
-unless( $outformat =~ /^(graphml|svg|dot|stemma|csv)$/ ) {
-    help( "Output format must be one of graphml, svg, csv, stemma, or dot" );
+unless( $outformat =~ /^(graphml|svg|dot|stemma|csv|db)$/ ) {
+    help( "Output format must be one of db, graphml, svg, csv, stemma, or dot" );
 }
 
 # Do we have a base if we need it?
@@ -64,6 +68,10 @@ if( $informat eq 'CollateText' ) {
     $args{'sigla'} = [ qw/ S M X V Z Bb B K W L / ];
 }
 my $tradition = Text::Tradition->new( %args );
+if( $stemmafile ) {
+       my $stemma = $tradition->add_stemma( $stemmafile );
+       print STDERR "Saved stemma at $stemmafile\n" if $stemma;
+}
 
 ### Custom hacking
 # Remove witnesses C, E, G in the Matthew text
@@ -82,6 +90,12 @@ if( $outformat eq 'stemma' ) {
     } else {
         print STDERR "Bad result: $tree";
     }
+} elsif( $outformat eq 'db' ) {
+       my $dir = Text::Tradition::Directory->new( 'dsn' => $dsn, 
+               'extra_args' => { 'create' => 1 } );
+       my $scope = $dir->new_scope;
+       my $uuid = $dir->store( $tradition );
+       print STDERR "Saved tradition to database with ID $uuid\n";
 } else {
     my $output = "as_$outformat";
     print $tradition->collation->$output();
index c473ced..4c4157a 100644 (file)
@@ -8,7 +8,7 @@ $| = 1;
 
 # =begin testing
 {
-use Test::Warn;
+use TryCatch;
 use File::Temp;
 use Text::Tradition;
 use_ok 'Text::Tradition::Directory';
@@ -17,38 +17,76 @@ my $fh = File::Temp->new();
 my $file = $fh->filename;
 $fh->close;
 my $dsn = "dbi:SQLite:dbname=$file";
-
-my $d = Text::Tradition::Directory->new( 'dsn' => $dsn,
-       'extra_args' => { 'create' => 1 } );
-is( ref $d, 'Text::Tradition::Directory', "Got directory object" );
-
-my $scope = $d->new_scope;
+my $uuid;
 my $t = Text::Tradition->new( 
        'name'  => 'inline', 
        'input' => 'Tabular',
        'file'  => 't/data/simple.txt',
        );
-my $uuid = $d->save( $t );
-ok( $uuid, "Saved test tradition" );
-
-my $s = $t->add_stemma( 't/data/simple.dot' );
-ok( $d->save( $t ), "Updated tradition with stemma" );
-is( $d->tradition( $uuid ), $t, "Correct tradition returned for id" );
-is( $d->tradition( $uuid )->stemma, $s, "...and it has the correct stemma" );
-warning_like { $d->save( $s ) } qr/not a Text::Tradition/, "Correctly failed to save stemma directly";
-
-my $e = Text::Tradition::Directory->new( 'dsn' => $dsn );
-$scope = $e->new_scope;
-is( scalar $e->tradition_ids, 1, "Directory index has our tradition" );
-my $te = $e->tradition( $uuid );
-is( $te->name, $t->name, "Retrieved the tradition from a new directory" );
-my $sid = $e->object_to_id( $te->stemma );
-warning_like { $e->tradition( $sid ) } qr/not a Text::Tradition/, "Did not retrieve stemma via tradition call";
-warning_like { $e->delete( $sid ) } qr/Cannot directly delete non-Tradition object/, "Stemma object not deleted from DB";
-$e->delete( $uuid );
-ok( !$e->exists( $uuid ), "Object is deleted from DB" );
-ok( !$e->exists( $sid ), "Object stemma also deleted from DB" );
-is( scalar $e->tradition_ids, 0, "Object is deleted from index" );
+
+{
+       my $d = Text::Tradition::Directory->new( 'dsn' => $dsn,
+               'extra_args' => { 'create' => 1 } );
+       is( ref $d, 'Text::Tradition::Directory', "Got directory object" );
+       
+       my $scope = $d->new_scope;
+       $uuid = $d->save( $t );
+       ok( $uuid, "Saved test tradition" );
+       
+       my $s = $t->add_stemma( 't/data/simple.dot' );
+       ok( $d->save( $t ), "Updated tradition with stemma" );
+       is( $d->tradition( $uuid ), $t, "Correct tradition returned for id" );
+       is( $d->tradition( $uuid )->stemma, $s, "...and it has the correct stemma" );
+       try {
+               $d->save( $s );
+       } catch( Text::Tradition::Error $e ) {
+               is( $e->ident, 'database error', "Got exception trying to save stemma directly" );
+               like( $e->message, qr/Cannot directly save non-Tradition object/, 
+                       "Exception has correct message" );
+       }
+}
+my $nt = Text::Tradition->new(
+       'name' => 'CX',
+       'input' => 'CollateX',
+       'file' => 't/data/Collatex-16.xml',
+       );
+is( ref( $nt ), 'Text::Tradition', "Made new tradition" );
+
+{
+       my $f = Text::Tradition::Directory->new( 'dsn' => $dsn );
+       my $scope = $f->new_scope;
+       is( scalar $f->tradition_ids, 1, "Directory index has our tradition" );
+       my $nuuid = $f->save( $nt );
+       ok( $nuuid, "Stored second tradition" );
+       is( scalar $f->tradition_ids, 2, "Directory index has both traditions" );
+       my $tf = $f->tradition( $uuid );
+       is( $tf->name, $t->name, "Retrieved the tradition from a new directory" );
+       my $sid = $f->object_to_id( $tf->stemma );
+       try {
+               $f->tradition( $sid );
+       } catch( Text::Tradition::Error $e ) {
+               is( $e->ident, 'database error', "Got exception trying to fetch stemma directly" );
+               like( $e->message, qr/not a Text::Tradition/, "Exception has correct message" );
+       }
+       try {
+               $f->delete( $sid );
+       } catch( Text::Tradition::Error $e ) {
+               is( $e->ident, 'database error', "Got exception trying to delete stemma directly" );
+               like( $e->message, qr/Cannot directly delete non-Tradition object/, 
+                       "Exception has correct message" );
+       }
+       $f->delete( $uuid );
+       ok( !$f->exists( $uuid ), "Object is deleted from DB" );
+       ok( !$f->exists( $sid ), "Object stemma also deleted from DB" );
+       is( scalar $f->tradition_ids, 1, "Object is deleted from index" );
+}
+
+SKIP: {
+       skip 'Have yet to figure out garbage collection', 1;
+       my $g = Text::Tradition::Directory->new( 'dsn' => $dsn );
+       my $scope = $g->new_scope;
+       is( scalar $g->tradition_ids, 1, "Now one object in new directory index" );
+}
 }
 
 
index c67e4f1..bfd6dc9 100644 (file)
@@ -57,7 +57,7 @@ foreach my $k ( keys %seen_wits ) {
                ok( $wit->has_layertext, "Witness $k has an a.c. version" );
                my $origtext = join( ' ', @{$wit->layertext} );
                my $acsig = $wit->sigil . $t->collation->ac_label;
-               my $graphtext = $t->collation->path_text( $acsig, $wit->sigil );
+               my $graphtext = $t->collation->path_text( $acsig );
                is( $graphtext, $origtext, "Collation matches original a.c. for witness $k" );
        } else {
                ok( !$wit->is_layered, "Witness $k not marked as layered" );