make the first couple of tests pass
Tara L Andrews [Mon, 16 May 2011 20:04:32 +0000 (22:04 +0200)]
lib/Text/Tradition.pm
lib/Text/Tradition/Collation.pm
lib/Text/Tradition/Collation/Reading.pm
lib/Text/Tradition/Parser/GraphML.pm
lib/Text/Tradition/Witness.pm
t/graph.t

index a156c0d..695f4e2 100644 (file)
@@ -1,43 +1,39 @@
 package Text::Tradition;
 
-use Text::Tradition::Witness;
-use Text::Tradition::Collation;
 use Moose;
+use Text::Tradition::Collation;
+use Text::Tradition::Witness;
 
 has 'collation' => (
-                   is => 'ro',
-                   isa => 'Text::Tradition::Collation',
-                   );
+    is => 'ro',
+    isa => 'Text::Tradition::Collation',
+    writer => '_save_collation',
+    );
 
 has 'witnesses' => (
-                   traits => ['Array'],
-                   is => 'rw',
-                   isa => 'ArrayRef[Text::Tradition::Witness]',
-                   handles => {
-                       all_options    => 'elements',
-                       add_option     => 'push',
-                       map_options    => 'map',
-                       option_count   => 'count',
-                       sorted_options => 'sort',
-                   },
-                   );
-
-around BUILDARGS => sub {
-    my $orig = shift;
-    my $class = shift;
+    traits => ['Array'],
+    is => 'rw',
+    isa => 'ArrayRef[Text::Tradition::Witness]',
+    handles => {
+       all_options    => 'elements',
+       add_option     => 'push',
+       map_options    => 'map',
+       option_count   => 'count',
+       sorted_options => 'sort',
+    },
+    );
 
-    # Now @_ contains the original constructor args.  Make a
-    # collation argument and a witnesses argument.
-    my %init_args = @_;
-    my %member_objects = ( 'collation' => undef,
-                          'witnesses' => [] );
+sub BUILD {
+    my( $self, $init_args ) = @_;
+    print STDERR "Calling tradition build\n";
 
-    if( exists $init_args{'witnesses'} ) {
+    $DB::single = 1;
+    if( exists $init_args->{'witnesses'} ) {
        # We got passed an uncollated list of witnesses.  Make a
        # witness object for each witness, and then send them to the
        # collator.
        my $autosigil = 0;
-       foreach my $wit ( %{$init_args{'witnesses'}} ) {
+       foreach my $wit ( %{$init_args->{'witnesses'}} ) {
            # Each item in the list is either a string or an arrayref.
            # If it's a string, it is a filename; if it's an arrayref,
            # it is a tuple of 'sigil, file'.  Handle either case.
@@ -50,19 +46,16 @@ around BUILDARGS => sub {
                          'file' => $wit };
                $autosigil++;
            }
-           push( @{$member_objects{'witnesses'}},
-                 Text::Tradition::Witness->new( $args ) );
-           # Now how to collate these?
+           $self->witnesses->push( Text::Tradition::Witness->new( $args ) );
+           # TODO Now how to collate these?
        }
     } else {
-       $member_objects{'collation'} = 
-           Text::Tradition::Collation->new( %init_args );
-       @{$member_objects{'witnesses'}} = 
-           $member_objects{'collation'}->create_witnesses();
+       # Else we got passed args intended for the collator.
+       $init_args->{'tradition'} = $self;
+       $self->_save_collation( Text::Tradition::Collation->new( %$init_args ) );
+       $self->witnesses( $self->collation->create_witnesses() );
     }
-
-    return $class->$orig( %member_objects );
-};
+}
 
 # The user will usually be instantiating a Tradition object, and
 # examining its collation.  The information about the tradition can
index fe1570b..106ab55 100644 (file)
@@ -1,28 +1,54 @@
 package Text::Tradition::Collation;
 
 use Graph::Easy;
+use IPC::Run qw( run binary );
+use Module::Load;
+use Text::Tradition::Collation::Reading;
 use Moose;
 
 has 'graph' => (
     is => 'ro',
     isa => 'Graph::Easy',
     handles => {
-       add_node => 'add_reading',
-       del_node => 'del_reading',
-       add_edge => 'add_path',
-       del_edge => 'del_path',
-       nodes => 'readings',
-       edges => 'paths',
+       add_reading => 'add_node',
+       del_reading => 'del_node',
+       add_path => 'add_edge',
+       del_path => 'del_edge',
+       reading => 'node',
+       path => 'edge',
+       readings => 'nodes',
+       paths => 'edges',
     },
     default => sub { Graph::Easy->new( undirected => 0 ) },
     );
                
 
 has 'tradition' => (
-    is => 'ro',
+    is => 'rw',
     isa => 'Text::Tradition',
     );
 
+has 'svg' => (
+    is => 'ro',
+    isa => 'Str',
+    writer => '_save_svg',
+    predicate => 'has_svg',
+    );
+
+has 'graphviz' => (
+    is => 'ro',
+    isa => 'Str',
+    writer => '_save_graphviz',
+    predicate => 'has_graphviz',
+    );
+
+has 'graphml' => (
+    is => 'ro',
+    isa => 'XML::LibXML::Document',
+    writer => '_save_graphml',
+    predicate => 'has_graphml',
+    );
+
 # The collation can be created two ways:
 # 1. Collate a set of witnesses (with CollateX I guess) and process
 #    the results as in 2.
@@ -47,15 +73,16 @@ sub BUILD {
     unless( $format ) {
        warn "No data given to create a graph; will initialize an empty one";
     }
-    if( $format =~ /^(CSV|CTE)$/ && !exists $args->{'base'} ) {
+    if( $format && $format =~ /^(CSV|CTE)$/ && !exists $args->{'base'} ) {
        warn "Cannot make a graph from $format without a base text";
        return;
     }
 
     # Initialize our graph object.
+    $self->graph->use_class('node', 'Text::Tradition::Collation::Reading');
     $self->graph->set_attribute( 'node', 'shape', 'ellipse' );
     # Starting point for all texts
-    my $last_node = $self->graph->add_node( '#START#' );
+    my $last_node = $self->add_reading( '#START#' );
 
     # Now do the parsing.
     my @sigla;
@@ -72,7 +99,7 @@ sub BUILD {
        my $mod = "Text::Tradition::Parser::$format";
        load( $mod );
        # TODO parse needs to return witness IDs
-       @sigla = $mod->can('parse')->( $self->graph, @parseargs );
+       @sigla = $mod->can('parse')->( $self, @parseargs );
     }
 
     # Do we need to initialize the witnesses?
@@ -97,5 +124,217 @@ sub merge_readings {
     return $self->graph->merge_nodes( @_ );
 }
 
+=head2 Output method(s)
+
+=over
+
+=item B<as_svg>
+
+print $graph->as_svg( $recalculate );
+
+Returns an SVG string that represents the graph.  Uses GraphViz to do
+this, because Graph::Easy doesn't cope well with long graphs. Unless
+$recalculate is passed (and is a true value), the method will return a
+cached copy of the SVG after the first call to the method.
+
+=cut
+
+sub as_svg {
+    my( $self, $recalc ) = @_;
+    return $self->svg if $self->has_svg;
+    
+    $self->_save_graphviz( $self->graph->as_graphviz() )
+       unless( $self->has_graphviz && !$recalc );
+    
+    my @cmd = qw/dot -Tsvg/;
+    my( $svg, $err );
+    my $in = $self->graphviz;
+    run( \@cmd, \$in, ">", binary(), \$svg );
+    $self->{'svg'} = $svg;
+    return $svg;
+}
+
+=item B<as_graphml>
+
+print $graph->as_graphml( $recalculate )
+
+Returns a GraphML representation of the collation graph, with
+transposition information and position information. Unless
+$recalculate is passed (and is a true value), the method will return a
+cached copy of the SVG after the first call to the method.
+
+=cut
+
+sub as_graphml {
+    my( $self, $recalc ) = @_;
+    return $self->graphml if $self->has_graphml;
+
+    # Some namespaces
+    my $graphml_ns = 'http://graphml.graphdrawing.org/xmlns';
+    my $xsi_ns = 'http://www.w3.org/2001/XMLSchema-instance';
+    my $graphml_schema = 'http://graphml.graphdrawing.org/xmlns ' .
+       'http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd';
+
+    # Create the document and root node
+    my $graphml = XML::LibXML->createDocument( "1.0", "UTF-8" );
+    my $root = $graphml->createElementNS( $graphml_ns, 'graphml' );
+    $graphml->setDocumentElement( $root );
+    $root->setNamespace( $xsi_ns, 'xsi', 0 );
+    $root->setAttributeNS( $xsi_ns, 'schemaLocation', $graphml_schema );
+
+    # Add the data keys for nodes
+    my @node_data = ( 'name', 'token', 'identical', 'position' );
+    foreach my $ndi ( 0 .. $#node_data ) {
+       my $key = $root->addNewChild( $graphml_ns, 'key' );
+       $key->setAttribute( 'attr.name', $node_data[$ndi] );
+       $key->setAttribute( 'attr.type', 'string' );
+       $key->setAttribute( 'for', 'node' );
+       $key->setAttribute( 'id', 'd'.$ndi );
+    }
+
+    # Add the data keys for edges
+    my %wit_hash;
+    my $wit_ctr = 0;
+    foreach my $wit ( $self->getWitnessList ) {
+       my $wit_key = 'w' . $wit_ctr++;
+       $wit_hash{$wit} = $wit_key;
+       my $key = $root->addNewChild( $graphml_ns, 'key' );
+       $key->setAttribute( 'attr.name', $wit );
+       $key->setAttribute( 'attr.type', 'string' );
+       $key->setAttribute( 'for', 'edge' );
+       $key->setAttribute( 'id', $wit_key );
+    }
+
+    # Add the graph, its nodes, and its edges
+    my $graph = $root->addNewChild( $graphml_ns, 'graph' );
+    $graph->setAttribute( 'edgedefault', 'directed' );
+    $graph->setAttribute( 'id', 'g0' ); # TODO make this meaningful
+    $graph->setAttribute( 'parse.edgeids', 'canonical' );
+    $graph->setAttribute( 'parse.edges', $self->edges() );
+    $graph->setAttribute( 'parse.nodeids', 'canonical' );
+    $graph->setAttribute( 'parse.nodes', $self->nodes() );
+    $graph->setAttribute( 'parse.order', 'nodesfirst' );
+
+    my $node_ctr = 0;
+    my %node_hash;
+    foreach my $n ( $self->readings ) {
+       my %this_node_data = ();
+       foreach my $ndi ( 0 .. $#node_data ) {
+           my $value;
+           $this_node_data{'d'.$ndi} = $n->name if $node_data[$ndi] eq 'name';
+           $this_node_data{'d'.$ndi} = $n->label 
+               if $node_data[$ndi] eq 'token';
+           $this_node_data{'d'.$ndi} = $n->primary->name if $n->has_primary;
+           $this_node_data{'d'.$ndi} = 
+               $self->{'positions'}->node_position( $n )
+               if $node_data[$ndi] eq 'position';
+       }
+       my $node_el = $graph->addNewChild( $graphml_ns, 'node' );
+       my $node_xmlid = 'n' . $node_ctr++;
+       $node_hash{ $n->name } = $node_xmlid;
+       $node_el->setAttribute( 'id', $node_xmlid );
+           
+       foreach my $dk ( keys %this_node_data ) {
+           my $d_el = $node_el->addNewChild( $graphml_ns, 'data' );
+           $d_el->setAttribute( 'key', $dk );
+           $d_el->appendTextChild( $this_node_data{$dk} );
+       }
+    }
+
+    foreach my $e ( $self->edges() ) {
+       my( $name, $from, $to ) = ( $e->name,
+                                   $node_hash{ $e->from()->name() },
+                                   $node_hash{ $e->to()->name() } );
+       my $edge_el = $graph->addNewChild( $graphml_ns, 'edge' );
+       $edge_el->setAttribute( 'source', $from );
+       $edge_el->setAttribute( 'target', $to );
+       $edge_el->setAttribute( 'id', $name );
+       # TODO Got to add the witnesses
+    }
+
+    # Return the thing
+    $self->_save_graphml( $graphml );
+    return $graphml;
+}
+
+=back
+
+=item B<start>
+
+my $beginning = $collation->start();
+
+Returns the beginning of the collation, a meta-reading with label '#START#'.
+
+=cut
+
+sub start {
+    # Return the beginning node of the graph.
+    my $self = shift;
+    my( $new_start ) = @_;
+    if( $new_start ) {
+       $self->del_reading( '#START#' );
+       $self->graph->rename_node( $new_start, '#START#' );
+    }
+    return $self->reading('#START#');
+}
+
+=item B<next_word>
+
+my $next_node = $graph->next_word( $node, $path );
+
+Returns the node that follows the given node along the given witness
+path.  TODO These are badly named.
+
+=cut
+
+sub next_word {
+    # Return the successor via the corresponding edge.
+    my $self = shift;
+    return $self->_find_linked_word( 'next', @_ );
+}
+
+=item B<prior_word>
+
+my $prior_node = $graph->prior_word( $node, $path );
+
+Returns the node that precedes the given node along the given witness
+path.  TODO These are badly named.
+
+=cut
+
+sub prior_word {
+    # Return the predecessor via the corresponding edge.
+    my $self = shift;
+    return $self->_find_linked_word( 'prior', @_ );
+}
+
+sub _find_linked_word {
+    my( $self, $direction, $node, $edge ) = @_;
+    $edge = 'base text' unless $edge;
+    my @linked_edges = $direction eq 'next' 
+       ? $node->outgoing() : $node->incoming();
+    return undef unless scalar( @linked_edges );
+    
+    # We have to find the linked edge that contains all of the
+    # witnesses supplied in $edge.
+    my @edge_wits = split( /, /, $edge );
+    foreach my $le ( @linked_edges ) {
+       my @le_wits = split( /, /, $le->name() );
+       if( _is_within( \@edge_wits, \@le_wits ) ) {
+           # This is the right edge.
+           return $direction eq 'next' ? $le->to() : $le->from();
+       }
+    }
+    warn "Could not find $direction node from " . $node->label 
+       . " along edge $edge";
+    return undef;
+}
+
+sub create_witnesses {
+    # TODO Given a new collation, make a bunch of Witness objects.
+
+    return [];
+}
+
 no Moose;
 __PACKAGE__->meta->make_immutable;
index 8334564..146eadf 100644 (file)
@@ -1,8 +1,8 @@
 package Text::Tradition::Collation::Reading;
 
+use Moose;
 use Moose::Util::TypeConstraints;
 use MooseX::NonMoose;
-use Moose;
 
 extends 'Graph::Easy::Node';
 
@@ -24,18 +24,31 @@ has 'same_as' => (
     isa => 'ArrayRef[Text::Tradition::Collation::Reading]',
     );
 
-# This is a hash mapping of 'relationship => reading'.
-# TODO we should validate the relationships sometime.
+# # This is a hash mapping of 'relationship => reading'.
+# # TODO we should validate the relationships sometime.
 has 'relationships' => (
     is => 'ro',
     isa => 'HashRef[Text::Tradition::Collation::Reading]',
     default => sub { {} },
     );
 
+# Deal with the non-arg option for Graph::Easy's constructor.
+around BUILDARGS => sub {
+    my $orig = shift;
+    my $class = shift;
+
+    my %args;
+    if( @_ == 1 && ref( $_[0] ) ne 'HASH' ) {
+       return $class->$orig( 'name' => $_[0] );
+    } else {
+       return $class->$orig( @_ );
+    }
+};
+
 # Initialize the identity pool. 
 sub BUILD {
     my( $self, $args ) = @_;
-#    $self->same_as( [ $self ] );
+    $self->same_as( [ $self ] );
 }
 
 sub merge_from {
@@ -68,7 +81,7 @@ sub set_identical {
                                           $other_node->same_as );
 
     # ...and set this node to point to the enlarged pool.
-    $self->set_same_as( $enlarged_pool );
+    $self->same_as( $enlarged_pool );
 }   
 
 sub _merge_array_pool {
@@ -87,6 +100,17 @@ sub _merge_array_pool {
     return $main_pool;
 }
 
+sub has_primary {
+    my $self = shift;
+    my $pool = $self->same_as;
+    return $pool->[0]->name eq $self->name;
+}
+
+sub primary {
+    my $self = shift;
+    return $self->same_as->[0];
+}
+
 # Much easier to do this with a hash than with an array of Relationship objects,
 # which would be the proper OO method.
 
index 260e938..d731f1d 100644 (file)
@@ -30,12 +30,12 @@ graph.
 =cut
 
 sub parse {
-    my( $graph, $graphml_str ) = @_;
+    my( $collation, $graphml_str ) = @_;
 
     my $parser = XML::LibXML->new();
     my $doc = $parser->parse_string( $graphml_str );
-    my $collation = $doc->documentElement();
-    my $xpc = XML::LibXML::XPathContext->new( $collation );
+    my $graphml = $doc->documentElement();
+    my $xpc = XML::LibXML::XPathContext->new( $graphml );
     $xpc->registerNs( 'g', 'http://graphml.graphdrawing.org/xmlns' );
     
     # First get the ID keys, for witnesses and for collation data
@@ -56,7 +56,7 @@ sub parse {
 
     # Add the nodes to the graph.  First delete the start node, because
     # GraphML graphs will have their own start nodes.
-    $graph->del_node( $graph->start() );
+    $collation->del_reading( $collation->start() );
     # Map from XML IDs to node name/identity
     my %node_name;
     # Keep track of whatever extra info we're passed
@@ -66,7 +66,7 @@ sub parse {
        my $lookup_xpath = './g:data[@key="%s"]/child::text()';
        my $id = $xpc->findvalue( sprintf( $lookup_xpath, $nodedata{'number'} ), $n );
        my $label = $xpc->findvalue( sprintf( $lookup_xpath, $nodedata{'token'} ), $n );
-       my $gnode = $graph->add_node( $id );
+       my $gnode = $collation->add_reading( $id );
        $node_name{ $n->getAttribute('id') } = $id;
        $gnode->set_attribute( 'label', $label );
 
@@ -90,7 +90,7 @@ sub parse {
        my @wit_names = map { $witnesses{ $_->getValue() } } @wit_ids;
        my $label = join( ', ', @wit_names );
            
-       $graph->add_edge( $from, $to, $label );
+       $collation->add_path( $from, $to, $label );
     }
 
     ## Reverse the node_name hash so that we have two-way lookup.
@@ -102,16 +102,16 @@ sub parse {
     foreach my $tn ( @$transposition_nodes ) {
        my $id_xpath = sprintf( './g:data[@key="%s"]/text()', 
                                $nodedata{'identical'} );
-       $graph->set_identical_node( $node_name{ $tn->getAttribute( 'id' ) },
-                                   $node_name{ $xpc->findvalue( $id_xpath, 
-                                                                $tn ) } );
+       $collation->reading( $node_id{ $tn->getAttribute( 'id' ) } )->
+           set_identical( $collation->reading( 
+                              $node_name{ $xpc->findvalue( $id_xpath, $tn ) } ) );
     }
 
 
     # Find the beginning and end nodes of the graph.  The beginning node
     # has no incoming edges; the end node has no outgoing edges.
     my( $begin_node, $end_node );
-    foreach my $gnode ( $graph->nodes() ) {
+    foreach my $gnode ( $collation->readings() ) {
        print STDERR "Checking node " . $gnode->name . "\n";
        my @outgoing = $gnode->outgoing();
        my @incoming = $gnode->incoming();
@@ -123,7 +123,7 @@ sub parse {
            warn "XPath did not find a node for id $node_xml_id"
                unless scalar @bn;
            $begin_node = $bn[0];
-           $graph->start( $gnode );
+           $collation->start( $gnode );
            $node_name{ $begin_node->getAttribute( 'id' ) } = '#START#';
            $node_id{'#START#'} = $begin_node->getAttribute( 'id' );
        }
@@ -176,15 +176,15 @@ sub parse {
     # Mark all the nodes as either common or not.
     foreach my $cn ( @common_nodes ) {
        print STDERR "Setting $cn as common node\n";
-       $graph->node( $cn )->set_attribute( 'class', 'common' );
+       $collation->reading( $cn )->set_attribute( 'class', 'common' );
     }
-    foreach my $n ( $graph->nodes() ) {
+    foreach my $n ( $collation->readings() ) {
        $n->set_attribute( 'class', 'variant' )
            unless $n->get_attribute( 'class' ) eq 'common';
     }
 
     # Now calculate graph positions.
-    $graph->make_positions( \@common_nodes, $paths );
+    # $collation->make_positions( \@common_nodes, $paths );
 
 }
     
index 90db6ca..1eccbb0 100644 (file)
@@ -21,6 +21,7 @@ has 'text' => (
 has 'source' => (
     is => 'ro',
     isa => 'Str',
+    predicate => 'has_source',
     );
 
 sub BUILD {
index f579a17..0c1ce26 100644 (file)
--- a/t/graph.t
+++ b/t/graph.t
@@ -3,7 +3,7 @@
 use strict; use warnings;
 use Test::More;
 use lib 'lib';
-use Text::Tradition::Graph;
+use Text::Tradition;
 use XML::LibXML;
 use XML::LibXML::XPathContext;
 
@@ -12,12 +12,13 @@ my $datafile = 't/data/Collatex-16.xml';
 open( GRAPHFILE, $datafile ) or die "Could not open $datafile";
 my @lines = <GRAPHFILE>;
 close GRAPHFILE;
-my $graph = Text::Tradition::Graph->new( 'GraphML' => join( '', @lines ) );
+my $tradition = Text::Tradition->new( 'GraphML' => join( '', @lines ) );
+my $collation = $tradition->collation;
 
 # Test the svg creation
 my $parser = XML::LibXML->new();
 $parser->load_ext_dtd( 0 );
-my $svg = $parser->parse_string( $graph->as_svg() );
+my $svg = $parser->parse_string( $collation->as_svg() );
 is( $svg->documentElement->nodeName(), 'svg', 'Got an svg document' );
 
 # Test for the correct number of nodes in the SVG
@@ -30,13 +31,15 @@ is( scalar @svg_nodes, 24, "Correct number of nodes in the graph" );
 my @svg_edges = $svg_xpc->findnodes( '//svg:g[@class="edge"]' );
 is( scalar @svg_edges, 30, "Correct number of edges in the graph" );
 
+__END__
+
 # Test for the correct common nodes
 my @expected_nodes = map { [ $_, 1 ] } qw/ #START# n1 n5 n6 n7 n12 n13
                                             n16 n19 n20 n23 n27 /;
 foreach my $idx ( qw/2 3 4 8 11 13 16 18/ ) {
     splice( @expected_nodes, $idx, 0, [ "node_null", undef ] );
 }
-my @active_nodes = $graph->active_nodes();
+my @active_nodes = $collation->active_nodes();
 # is_deeply( \@active_nodes, \@expected_nodes, "Initial common points" );
 subtest 'Initial common points' => \&compare_active;
 my $string = '# when ... ... ... showers sweet with ... fruit the ... of ... has pierced ... the ... #';
@@ -60,7 +63,7 @@ sub make_text {
     my @words;
     foreach my $n ( @_ ) {
        if( $n->[1] ) {
-           push( @words, $graph->text_of_node( $n->[0] ) );
+           push( @words, $collation->text_of_node( $n->[0] ) );
        } elsif ( !defined $n->[1] ) {
            push( @words, '...' );
        }
@@ -72,9 +75,9 @@ sub make_text {
 my $wit_a = '# when april with his showers sweet with fruit the drought of march has pierced unto the root #';
 my $wit_b = '# when showers sweet with april fruit the march of drought has pierced to the root #';
 my $wit_c = '# when showers sweet with april fruit the drought of march has pierced the rood #';
-is( $graph->text_for_witness( "A" ), $wit_a, "Correct path for witness A" );
-is( $graph->text_for_witness( "B" ), $wit_b, "Correct path for witness B" );
-is( $graph->text_for_witness( "C" ), $wit_c, "Correct path for witness C" );
+is( $collation->text_for_witness( "A" ), $wit_a, "Correct path for witness A" );
+is( $collation->text_for_witness( "B" ), $wit_b, "Correct path for witness B" );
+is( $collation->text_for_witness( "C" ), $wit_c, "Correct path for witness C" );
 
 # Test the transposition identifiers
 my $transposition_pools = [ [ 'n2', 'n11' ], [ 'n14', 'n18' ], 
@@ -86,89 +89,89 @@ my $transposed_nodes = { 'n2' => $transposition_pools->[0],
                         'n17' => $transposition_pools->[2],
                         'n18' => $transposition_pools->[1],
 };
-foreach my $n ( $graph->nodes() ) {
+foreach my $n ( $collation->readings() ) {
     $transposed_nodes->{ $n->name() } = [ $n->name() ]
        unless exists $transposed_nodes->{ $n->name() };
 }
-is_deeply( $graph->{'identical_nodes'}, $transposed_nodes, "Found the right transpositions" );
+is_deeply( $collation->{'identical_nodes'}, $transposed_nodes, "Found the right transpositions" );
 
 # Test turning on a node
-my @off = $graph->toggle_node( 'n25' );
+my @off = $collation->toggle_node( 'n25' );
 $expected_nodes[ 18 ] = [ "n25", 1 ];
-@active_nodes = $graph->active_nodes( @off );
+@active_nodes = $collation->active_nodes( @off );
 subtest 'Turned on node for new location' => \&compare_active;
 $string = '# when ... ... ... showers sweet with ... fruit the ... of ... has pierced ... the rood #';
 is( make_text( @active_nodes ), $string, "Got the right text" );
  
 # Test the toggling effects of same-column
-@off = $graph->toggle_node( 'n26' );
+@off = $collation->toggle_node( 'n26' );
 splice( @expected_nodes, 18, 1, ( [ "n25", 0 ], [ "n26", 1 ] ) );
-@active_nodes = $graph->active_nodes( @off );
+@active_nodes = $collation->active_nodes( @off );
 subtest 'Turned on other node in that location' => \&compare_active;
 $string = '# when ... ... ... showers sweet with ... fruit the ... of ... has pierced ... the root #';
 is( make_text( @active_nodes ), $string, "Got the right text" );
 
 # Test the toggling effects of transposition
 
-@off = $graph->toggle_node( 'n14' );
+@off = $collation->toggle_node( 'n14' );
 # Add the turned on node
 $expected_nodes[ 11 ] = [ "n14", 1 ];
 # Remove the 'off' for the previous node
 splice( @expected_nodes, 18, 1 );
-@active_nodes = $graph->active_nodes( @off );
+@active_nodes = $collation->active_nodes( @off );
 subtest 'Turned on transposition node' => \&compare_active;
 $string = '# when ... ... ... showers sweet with ... fruit the drought of ... has pierced ... the root #';
 is( make_text( @active_nodes ), $string, "Got the right text" );
 
-@off = $graph->toggle_node( 'n18' );
+@off = $collation->toggle_node( 'n18' );
 # Toggle on the new node
 $expected_nodes[ 13 ] = [ "n18", 1 ];
 # Toggle off the transposed node
 $expected_nodes[ 11 ] = [ "n14", undef ];
-@active_nodes = $graph->active_nodes( @off );
+@active_nodes = $collation->active_nodes( @off );
 subtest 'Turned on that node\'s partner' => \&compare_active;
 $string = '# when ... ... ... showers sweet with ... fruit the ... of drought has pierced ... the root #';
 is( make_text( @active_nodes ), $string, "Got the right text" );
 
-@off = $graph->toggle_node( 'n14' );
+@off = $collation->toggle_node( 'n14' );
 # Toggle on the new node
 $expected_nodes[ 11 ] = [ "n14", 1 ];
 # Toggle off the transposed node
 $expected_nodes[ 13 ] = [ "n18", undef ];
-@active_nodes = $graph->active_nodes( @off );
+@active_nodes = $collation->active_nodes( @off );
 subtest 'Turned on the original node' => \&compare_active;
 $string = '# when ... ... ... showers sweet with ... fruit the drought of ... has pierced ... the root #';
 is( make_text( @active_nodes ), $string, "Got the right text" );
 
-@off = $graph->toggle_node( 'n15' );
+@off = $collation->toggle_node( 'n15' );
 # Toggle on the new node, and off with the old
 splice( @expected_nodes, 11, 1, [ "n14", 0 ], [ "n15", 1 ] );
-@active_nodes = $graph->active_nodes( @off );
+@active_nodes = $collation->active_nodes( @off );
 subtest 'Turned on the colocated node' => \&compare_active;
 $string = '# when ... ... ... showers sweet with ... fruit the march of ... has pierced ... the root #';
 is( make_text( @active_nodes ), $string, "Got the right text" );
 
-@off = $graph->toggle_node( 'n3' );
+@off = $collation->toggle_node( 'n3' );
 # Toggle on the new node
 splice( @expected_nodes, 3, 1, [ "n3", 1 ] );
 # Remove the old toggle-off
 splice( @expected_nodes, 11, 1 );
-@active_nodes = $graph->active_nodes( @off );
+@active_nodes = $collation->active_nodes( @off );
 subtest 'Turned on a singleton node' => \&compare_active;
 $string = '# when ... with ... showers sweet with ... fruit the march of ... has pierced ... the root #';
 is( make_text( @active_nodes ), $string, "Got the right text" );
 
-@off = $graph->toggle_node( 'n3' );
+@off = $collation->toggle_node( 'n3' );
 # Toggle off this node
 splice( @expected_nodes, 3, 1, [ "n3", 0 ] );
-@active_nodes = $graph->active_nodes( @off );
+@active_nodes = $collation->active_nodes( @off );
 subtest 'Turned off a singleton node' => \&compare_active;
 $string = '# when ... ... showers sweet with ... fruit the march of ... has pierced ... the root #';
 is( make_text( @active_nodes ), $string, "Got the right text" );
 
-@off = $graph->toggle_node( 'n21' );
+@off = $collation->toggle_node( 'n21' );
 splice( @expected_nodes, 16, 1, [ "n21", 1 ] );
-@active_nodes = $graph->active_nodes( @off );
+@active_nodes = $collation->active_nodes( @off );
 subtest 'Turned on a new node after singleton switchoff' => \&compare_active;
 $string = '# when ... ... showers sweet with ... fruit the march of ... has pierced unto the root #';
 is( make_text( @active_nodes ), $string, "Got the right text" );