# Copyright 2008-2010 Tim Rayner # # This file is part of Bio::MAGETAB. # # Bio::MAGETAB is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # Bio::MAGETAB is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Bio::MAGETAB. If not, see . # # $Id: Edge.pm 340 2010-07-23 13:19:27Z tfrayner $ package Bio::MAGETAB::Edge; use Moose; use MooseX::FollowPBP; use List::Util qw(first); use MooseX::Types::Moose qw( ArrayRef ); BEGIN { extends 'Bio::MAGETAB::BaseClass' }; sub BUILD { my ( $self, $params ) = @_; if ( defined $params->{'inputNode'} ) { $self->_reciprocate_nodes_to_edges( $params->{'inputNode'}, 'inputNode', 'outputEdges', ); } if ( defined $params->{'outputNode'} ) { $self->_reciprocate_nodes_to_edges( $params->{'outputNode'}, 'outputNode', 'inputEdges', ); } return; } has 'inputNode' => ( is => 'rw', isa => 'Bio::MAGETAB::Node', weak_ref => 1, required => 1 ); has 'outputNode' => ( is => 'rw', isa => 'Bio::MAGETAB::Node', weak_ref => 1, required => 1 ); has 'protocolApplications' => ( is => 'rw', isa => ArrayRef['Bio::MAGETAB::ProtocolApplication'], auto_deref => 1, clearer => 'clear_protocolApplications', predicate => 'has_protocolApplications', required => 0 ); # We use an "around" method to wrap this, rather than a trigger, so # that we can search through the old edges from the old node # and remove this edge. around 'set_inputNode' => sub { my ( $attr, $self, $node ) = @_; # Note that we have to handle the implicit delete here where a # member of $self->get_inputNode is being dropped during an update. my $old_node = $self->get_inputNode(); if ( $old_node && $node ne $old_node ) { my @new_rows = grep { $_ ne $self } $old_node->get_outputEdges(); $old_node->{ 'outputEdges' } = \@new_rows; } # Set the appropriate $self attribute to point to $node. $attr->( $self, $node ); $self->_reciprocate_nodes_to_edges( $node, 'inputNode', 'outputEdges', ); }; # We use an "around" method to wrap this, rather than a trigger, so # that we can search through the old edges from the old node # and remove this edge. around 'set_outputNode' => sub { my ( $attr, $self, $node ) = @_; # Note that we have to handle the implicit delete here where a # member of $self->get_outputNode is being dropped during an update. my $old_node = $self->get_outputNode(); if ( $old_node && $node ne $old_node ) { my @new_rows = grep { $_ ne $self } $old_node->get_inputEdges(); $old_node->{ 'inputEdges' } = \@new_rows; } # Set the appropriate $self attribute to point to $node. $attr->( $self, $node ); $self->_reciprocate_nodes_to_edges( $node, 'outputNode', 'inputEdges', ); }; # This method is used as a wrapper to ensure that reciprocating # relationships are maintained, even when updating object attributes. sub _reciprocate_nodes_to_edges { # $node: The node with which $self has a reciprocal relationship. # This can be either a scalar or an arrayref. # $self_slot: The name of the slot pointing from $self to $node. # $node_slot: The name of the slot pointing from $node to $self. my ( $self, $node, $self_slot, $node_slot ) = @_; my $self_getter = "get_$self_slot"; my $node_getter = "get_$node_slot"; # Remove $self from the list held by the old $node. my $old_node = $self->$self_getter(); if ( $old_node ) { my @cleaned; foreach my $item ( $old_node->$node_getter() ) { push @cleaned, $item unless ( $item eq $self ); } $old_node->{ $node_slot } = \@cleaned; } # Make sure $node points to us. my @current = $node->$node_getter(); unless ( first { $_ eq $self } @current ) { push @current, $self; $node->{ $node_slot } = \@current; } return; } __PACKAGE__->meta->make_immutable(); no Moose; =pod =head1 NAME Bio::MAGETAB::Edge - MAGE-TAB edge class =head1 SYNOPSIS use Bio::MAGETAB::Edge; =head1 DESCRIPTION This class is used to store information on edges in the experimental design graph described by a MAGE-TAB SDRF. Each Edge must link to both an input and an output Node. See the L class for superclass methods. =head1 ATTRIBUTES =over 2 =item inputNode (required) The Node object which feeds into this edge (data type: Bio::MAGETAB::Node). =item outputNode (required) The Node object leading away from this edge (data type: Bio::MAGETAB::Node) =item protocolApplications (optional) A list of ProtocolApplications associated with the edge (data type: Bio::MAGETAB::ProtocolApplication). =back =head1 METHODS Each attribute has accessor (get_*) and mutator (set_*) methods, and also predicate (has_*) and clearer (clear_*) methods where the attribute is optional. Where an attribute represents a one-to-many relationship the mutator accepts an arrayref and the accessor returns an array. =head1 SEE ALSO L =head1 AUTHOR Tim F. Rayner =head1 LICENSE This library is released under version 2 of the GNU General Public License (GPL). =cut 1;