From: Tara L Andrews Date: Sat, 21 Jan 2012 20:06:07 +0000 (+0100) Subject: simplify Directory and add exceptions; X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=861c3e272c65c7553ad7c03cca51cbdd561f126c;hp=eb33038fdbbd61e619318542704d462eb03f8e51;p=scpubgit%2Fstemmatology.git simplify Directory and add exceptions; change reading_sequence interface; populate ->text and ->layertext in witnesses for all 'real' parsers --- diff --git a/Makefile.PL b/Makefile.PL index acd2439..c5427c7 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -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' ); diff --git a/lib/Text/Tradition/Collation.pm b/lib/Text/Tradition/Collation.pm index 10d215e..77a367e 100644 --- a/lib/Text/Tradition/Collation.pm +++ b/lib/Text/Tradition/Collation.pm @@ -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 diff --git a/lib/Text/Tradition/Directory.pm b/lib/Text/Tradition/Directory.pm index 20c3301..0f7da99 100644 --- a/lib/Text/Tradition/Directory.pm +++ b/lib/Text/Tradition/Directory.pm @@ -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 index 0000000..14bfa79 --- /dev/null +++ b/lib/Text/Tradition/Error.pm @@ -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 Eaurum@cpan.orgE diff --git a/lib/Text/Tradition/Parser/BaseText.pm b/lib/Text/Tradition/Parser/BaseText.pm index 5744542..e07cdec 100644 --- a/lib/Text/Tradition/Parser/BaseText.pm +++ b/lib/Text/Tradition/Parser/BaseText.pm @@ -301,7 +301,7 @@ sub merge_base { # $rel->type, $rel->from->id, $rel->to->id ); # } # } - # $collation->calculate_ranks(); + $collation->calculate_ranks(); } =item B diff --git a/lib/Text/Tradition/Parser/CTE.pm b/lib/Text/Tradition/Parser/CTE.pm index 32b6a71..e706906 100644 --- a/lib/Text/Tradition/Parser/CTE.pm +++ b/lib/Text/Tradition/Parser/CTE.pm @@ -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 ); } } diff --git a/lib/Text/Tradition/Parser/CollateText.pm b/lib/Text/Tradition/Parser/CollateText.pm index 944a8dd..21e54bd 100644 --- a/lib/Text/Tradition/Parser/CollateText.pm +++ b/lib/Text/Tradition/Parser/CollateText.pm @@ -270,7 +270,6 @@ sub merge_stone_apparatus { } # end processing of $app } # end foreach my $app in line } # end while - $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 ); } } diff --git a/lib/Text/Tradition/Parser/CollateX.pm b/lib/Text/Tradition/Parser/CollateX.pm index 1ee448b..0d9db22 100644 --- a/lib/Text/Tradition/Parser/CollateX.pm +++ b/lib/Text/Tradition/Parser/CollateX.pm @@ -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 diff --git a/lib/Text/Tradition/Parser/Self.pm b/lib/Text/Tradition/Parser/Self.pm index dee5969..b1082f3 100644 --- a/lib/Text/Tradition/Parser/Self.pm +++ b/lib/Text/Tradition/Parser/Self.pm @@ -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; diff --git a/lib/Text/Tradition/Parser/TEI.pm b/lib/Text/Tradition/Parser/TEI.pm index afea435..3131774 100644 --- a/lib/Text/Tradition/Parser/TEI.pm +++ b/lib/Text/Tradition/Parser/TEI.pm @@ -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 { diff --git a/lib/Text/Tradition/Parser/Tabular.pm b/lib/Text/Tradition/Parser/Tabular.pm index 4b53e3c..bf2e077 100644 --- a/lib/Text/Tradition/Parser/Tabular.pm +++ b/lib/Text/Tradition/Parser/Tabular.pm @@ -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 { diff --git a/lib/Text/Tradition/Witness.pm b/lib/Text/Tradition/Witness.pm index 6ae078d..776cebe 100644 --- a/lib/Text/Tradition/Witness.pm +++ b/lib/Text/Tradition/Witness.pm @@ -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. diff --git a/script/make_tradition.pl b/script/make_tradition.pl index b3b5b44..309033b 100755 --- a/script/make_tradition.pl +++ b/script/make_tradition.pl @@ -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(); diff --git a/t/text_tradition_directory.t b/t/text_tradition_directory.t index c473ced..4c4157a 100644 --- a/t/text_tradition_directory.t +++ b/t/text_tradition_directory.t @@ -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" ); +} } diff --git a/t/text_tradition_parser_tabular.t b/t/text_tradition_parser_tabular.t index c67e4f1..bfd6dc9 100644 --- a/t/text_tradition_parser_tabular.t +++ b/t/text_tradition_parser_tabular.t @@ -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" );