Web::Chain project: Web/Node.pm
package Web::Node;
# doom@kzsu.stanford.edu
# 07 Aug 2004
# Tue Aug 24, 2004
=pod
=head1 NAME
Web::Node - Basic doomfiles node object, a bundle of all the
information that makes up a doomfiles page
=head1 SYNOPSIS
use Web::Node;
$node1 = Web::Node->new($node_name_1);
$node1->body($main_body_ref);
print $node1->prev(); # undef
$node2 = Web::Node->new(); # node name left blank for now
$node2->name($node_name_2);
$node2->prev($node1);
$node1->next($node2);
print $node2->next(); # shows undef
# Chained methods access:
$name_from_node4 = $node1->next->next->next->name;
=head1 DESCRIPTION
Basic node class for the DF project: holds the data that make up a
DF page. The methods here consist entirely of accessor/getters
(and some deprecated mutators).
Every node should have a name which matches the
$DF_NODE_NAME_PINNED_RULE
which is defined in Web::Definitions.
A typical example of a matching name: UPPERCASE_WITH_UNDERSCORES
TODO - make it impossible to create a node without a name.
This is used by the "Chain" class, which has methods to
generate and parse different formats, to manipulate
chains, and to validate them. (The Chain class is
reponsible for ensuring that the names are unique
for each node of the chain.)
=head1 METHODS
=over
=cut
use 5.006;
use strict;
use warnings;
use Carp;
use Data::Dumper;
use Web::Definitions qw( $DF_NODE_NAME_PINNED_RULE $DF_VERSION );
our $DEBUG = 0;
our $VERBOSE = 1;
our $VERSION = $DF_VERSION;
=item B<new> - creates a new node object with given name if
supplied (this can be deferred and set with "name" later).
=cut
sub new {
my $class = shift;
my $name = shift if $_[0];
($DEBUG) && print STDERR "new called with $name\n";
($VERBOSE) && print STDERR " Creating new doomfiles node: $name\n";
_check_name($name);
bless {
_name => $name, # unique node name (scalar, string)
_body => undef, # content of doomfile node (ref, string)
_prev => undef, # previous node in browse sequence (Node object - aka hashref)
_next => undef, # next node in browse sequence (Node object - aka hashref)
} , $class
}
=back
=head2 SETTERS and GETTERS
Simple routines that either assign values to an object
attribute (named set_*), or return existing values (named
get_*).
Originally I thought mutators would be more convenient but
(a) there's a clear need to be able to assign undefs, so the
undef can't be used to signal a get; (b) I believe the code
reads a little more clearly if assignment is flagged with
the prefix "set_". (Though to my eye, the need for "get_"
is a little less clear.)
The setters (which all take Web::Node objects as arguments):
=over
=item B<set_prev> - sets the pointer to the previous node in the chain
=cut
sub set_prev {
my ($self, $prev) = @_;
my $subname = ( caller(0) )[3];
if ( ref($prev) eq ref($self) ) { # $prev is a node object
$self -> {_prev} = $prev;
} elsif ($prev) { # $prev is defined (but not a node object)
croak "$subname: $prev is not a Web::Node object";
} else { # set to undefined (also if arg was blank, or 0)
$self -> {_prev} = undef;
}
return( $self->{_prev} ); # return what was just set
}
=item B<set_next> - sets the pointer to the next node in the chain
=cut
sub set_next {
my ($self, $next) = @_;
my $subname = ( caller(0) )[3];
if ( ref($next) eq ref($self) ) { # $next is a node object
$self -> {_next} = $next;
} elsif ($next) { # $next is ~ defined (but not a node object)
croak "$subname: $next is not a Web::Node object";
} else { # set to undefined (also if arg was blank, or 0)
$self -> {_next} = undef;
}
return( $self->{_next} ); # return what was just set
}
=back
Setters that take references, and croak if they're given anything else.
=over
=item B<set_body> - sets the main body (the text content) of the doomfiles
node expected to be a reference to a string.
=cut
sub set_body {
my ($self, $body) = @_;
my $subname = ( caller(0) )[3];
($DEBUG) && print STDERR "$subname: argument ref is ". ref($body) . "\n";
if (ref $body) { # TODO further, could make sure it's SCALAR...
$self -> {_body} = $body;
} elsif ($body) { # $body is ~ defined but not a ref
croak "$subname: $body is not a reference";
} else { # set to undefined (also if arg was blank, or 0)
$self -> {_next} = undef;
}
return( $self->{_body} );
}
=back
=head2 GETTERS
Methods that return the value of an object attribute.
=over
=item B<get_name> - return the name of the node
(A short string, typically uppercase with underscores).
=cut
sub get_name {
my $self = shift;
return( $self->{_name} );
}
=item B<get_prev> - returns the pointer to the previous node in the chain
(Another node object)
=cut
sub get_prev {
my $self = shift;
return( $self->{_prev} );
}
=item B<get_next> - returns the pointer to the next node in the chain
(Another node object)
=cut
sub get_next {
my $self = shift;
return( $self->{_next} );
}
=item B<body> - gets the main body (the text content) of the doomfiles node
expected to be a reference to a string.
=cut
sub get_body {
my $self = shift;
return( $self->{_body} );
}
=back
=head2 MUTATORS
Mutators set arguments if provided, and if not just return existing setting.
First, mutators that take simple strings (not a ref):
TODO: make sure that these are no longer in use, and delete them.
Then rename getters from get_foo to just foo.
=over
=item B<name> -
=cut
sub name {
my ($self, $name) = @_;
my $subname = ( caller(0) )[3];
if ($name and not ref $name) {
_check_name($name); # must match the node name pattern
$self->{_name} = $name;
} elsif (ref $name) {
carp "$subname: $name should be a simple string, not a reference";
}
return( $self->{_name} );
}
=back
Mutators that take Web::Node objects:
=over
=item B<prev> - sets or gets the pointer to the previous node in the chain
=cut
sub prev {
my ($self, $prev) = @_;
my $subname = ( caller(0) )[3];
if ( ref($prev) eq ref($self) ) {
$self -> {_prev} = $prev;
} elsif ($prev) {
carp "$subname: $prev is not a Web::Node object";
}
return( $self->{_prev} );
}
=item B<next> - sets or gets the pointer to the next node in the chain
=cut
sub next {
my ($self, $next) = @_;
my $subname = ( caller(0) )[3];
if ( ref($next) eq ref($self) ) {
$self -> {_next} = $next;
} elsif ($next) {
carp "$subname: $next is not a Web::Node object";
}
return( $self->{_next} );
}
=back
Mutators that take references,
and carp if they're given anything else.
=over
=item B<body> - sets or gets the main body (the text content) of the doomfiles node
=cut
sub body {
my ($self, $body) = @_;
my $subname = ( caller(0) )[3];
($DEBUG) && print STDERR "$subname: argument ref is ". ref($body) . "\n";
if (ref $body) { # further, could make sure it's SCALAR...
$self -> {_body} = $body;
} elsif ($body) {
carp "$subname: $body is not a reference";
}
return( $self->{_body} );
}
=back
=head2 INTERNAL ROUTINES
Note, some of the following are used internally
by this package, some are used "internally" by the
project.
=over
=item B<_dump_contents_to_stderr> - used internally by this project to assist
in debugging. Dumps node contents (name of node, name of previous and next nodes,
the node body) in a simple textual form to STDERR.
=cut
sub _dump_contents_to_stderr {
my $node = shift;
# stub code, dumps node contents to stderr
print STDERR '|<|' . $node->get_prev->get_name . '|<|' . "\n" if $node->get_prev;
print STDERR '|||' . $node->get_name . '|||' . "\n";
print STDERR '|b>|'. "\n";
print STDERR ${ $node->get_body } . "\n";
print STDERR '|<b|' . "\n";
print STDERR '|>|' . $node->get_next->get_name . '|>|' . "\n" if $node->get_next;;
print STDERR '===' . "\n\n";
}
=back
=head2 PROCEEDURAL FUNCTIONS
Some proceedural (i.e. not OOP) utilities , used internally by this package
=over
=item B<_check_name> -
Makes sure the name is in doomfiles node name format
Croaks in the event of a bad name argument.
=cut
sub _check_name {
my $name = shift;
my $subname = ( caller(1) )[3]; # Name of for sub that calls this _check_name routine
unless( $name =~ qr/$DF_NODE_NAME_PINNED_RULE/ ) { # Exported from Web::Definitions
croak "$subname: $name is not in doomfiles node name format (e.g. all caps with underscores)";
}
}
1;
__END__
=back
=head1 FUTURE DEVELOPMENT
Notes for future development:
As written nothing prevents next or prev from pointing
at any node. Possibly the linear chain idea should be
enforced on the level of the Chain object...
Idea: in the future, add a capability to store the names
of identified links in the body, perhaps with locations,
e.g. byte offset(s). This "out of band" link info might
be used for a link checking in the Chain object
=head1 EXPORT
None by default.
=head1 SEE ALSO
=over
=item L<Project Documentation|Web::Project>
=item L<Web::Chain>
=back
=head1 AUTHOR
Joseph Brenner, E<lt>doom@kzsu.stanford.eduE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2004 by Joseph Brenner
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.2 or,
at your option, any later version of Perl 5 you may have available.
=head1 BUGS
None reported... yet.
=cut
Joseph Brenner,
Sat Nov 6 17:04:11 2004