if (%arg) { #performed only if obj. arguments given
my %_temp_hash = _return_attr_data(); #returns list of obj. attributes
foreach my $argcheck (keys %arg) {
next if exists $_temp_hash{"_" . $argcheck}; #checks if attribute exists
croak "An invalid CD attribute given"; #if not, croaks with error
}
}
####
package Music;
$VERSION = 1.00;
use strict;
use vars qw( $AUTOLOAD );
use Carp;
{
# Encapsulated class data
my %_attr_data = # DEFAULT ACCESSIBILITY
( _name => [ '???', 'read'],
_artist => [ '???', 'read'],
_publisher => [ '???', 'read'],
_ISBN => [ '???', 'read'],
_tracks => [ '???', 'read'],
_rating => [ -1, 'read/write'],
_room => [ 'uncatalogued', 'read/write'],
_shelf => [ "", 'read/write'],
);
my $_count = 0;
# Class methods, to operate on encapsulated class data
# Is a specified object attribute accessible in a given mode
sub _accessible {
my ($self, $attr, $mode) = @_;
$_attr_data{$attr}[1] =~ /$mode/
}
# Classwide default value for a specified object attribute
sub _default_for {
my ($self, $attr) = @_;
$_attr_data{$attr}[0];
}
# List of names of all specified object attributes
sub _standard_keys {
keys %_attr_data;
}
####
sub _return_attr_data {
%_attr_data;
}
####
# Retrieve object count
sub get_count {
$_count;
}
# Private count increment/decrement methods
sub _incr_count { ++$_count }
sub _decr_count { --$_count }
}
# Constructor may be called as a class method
# (in which case it uses the calss's default values),
# or an object method
# (in which case it gets defaults from the existing object)
sub new
{
my ($caller, %arg) = @_;
my $caller_is_obj = ref($caller);
my $class = $caller_is_obj || $caller;
my $self = bless {}, $class;
####
if (%arg) {
my %_temp_hash = _return_attr_data();
foreach my $argcheck (keys %arg) {
next if exists $_temp_hash{"_" . $argcheck};
croak "An invalid CD attribute given";
}
}
####
foreach my $attrname ( $self->_standard_keys() ) {
my ($argname) = ($attrname =~ /^_(.*)/);
if (exists $arg{$argname})
{ $self->{$attrname} = $arg{$argname} }
elsif ($caller_is_obj)
{ $self->{$attrname} = $caller->{$attrname} }
else { $self->{$attrname} = $self->_default_for($attrname) }
}
$self->_incr_count();
return $self;
}
# Destructor adjusts class count
sub DESTROY
{
$_[0]->_decr_count();
}
# get or set room&shelf together
sub get_location { ($_[0]->get_room(), $_[0]->get_shelf()) }
sub set_location {
my ($self, $room, $shelf) = @_;
$self->set_room($room) if $room;
$self->set_shelf($shelf) if $shelf;
return;
}
# Implement other get_... and set_... methods (create as necessary)
sub AUTOLOAD {
no strict "refs";
my ($self, $newval) = @_;
# Was it a get_... method?
if ($AUTOLOAD =~ /.*::get(_\w+)/ && $self->_accessible($1, 'read')) {
my $attr_name = $1;
*{$AUTOLOAD} = sub { return $_[0]->{$attr_name} };
return $self->{$attr_name};
}
# Was it a set_... method?
if ($AUTOLOAD =~ /.*::set(_\w+)/ && $self->_accessible($1, 'write')) {
my $attr_name = $1;
*{$AUTOLOAD} = sub { $_[0]->{$attr_name} = $_[1]; return };
$self->{$1} = $newval;
return;
}
# Must have been a mistake then...
croak "No such method: $AUTOLOAD";
}
1; # Ensure that the module can be succesfully use'd