On the fly Perl Classes with Type restricted attributes
There is a CPAN module Class::Struct that can give you this same functionality. But fool that I am I like to do things the hard way. Now the differences in the useage of this implementation, while it may not do things as automatically as Class Struct does, will allow you to create simple type restricted attributes on the fly in your code with a simple one line class method. You could even bundle this in with an AUTOLOAD function to build the attributes as you need them. Also the class attributes are added at runtime and with a little extra work you can even specify such things as typed arrays or hashes. Ok enough Pro's and Cons lets take a look at the code. First we take a look at our base class that does most of the work.
package Class::Builder;
sub new {
my $class = ref($[0]) || $[0];
my $self = {};
return bless($self, $class);
}
sub attribute {
my $self = $[0];
my $type = $[1];
my $attribute = $[2];
my $value = $[3];
if ($value) {
#handle INT case
if ($type eq "INT") {
if ($value =~ /^[0-9]+$/) {
$[0]->{$attribute} = $[3];
return $[0]->{$attribute};
} else {
$[0]->err("Not a $type value for $attribute");
return undef;
}
} elsif ($type eq "SCALAR") {
#handle simple SCALAR case $[0]->{$attribute} = $[3];
return $[0]->{$attribute};
} elsif (ref($value) eq $type) {
#handle other types
$[0]->{$attribute} = $[3];
return $[0]->{$attribute};
} else {
$[0]->err("Not a $type value for $attribute");
return undef;
}
} else {
$[0]->err("No value passed for $attribute in ".ref($[0]));
}
return $[0]->{$attribute};
}
sub err {
$[0]->{err} = $[1] if $[1];
return $[0]->{err};
}
return 1;
Now lets see how we can use it.
package Document;
use Class::Builder;
use Document::Section;
use base qw(Class::Builder);
#Attribute Methods
example of a SCALAR Typed Attribute implementation
sub Name { return $[0]->attribute('SCALAR', 'Name', $[1]); } #Example of a ARRAY Typed Attribute with a further simple check #that the array elements are of type = "Document::Section" sub Sections { my $arraytype = 'Document::Section'; my $sections_old = $[0]->Sections(); my $sections = $[0]->attribute('ARRAY', 'Sections', $[1]); foreach (@$[0]) { if (ref($) ne $arraytype) { #throw an error here $[0]->err("Invalid Array Element $arraytype"); $_[0]->attribute('ARRAY', 'Sections', $sections_old); # reset the Sections array return undef; } }
die "sections is a ".ref($sections);
return $sections; }
example of a HASH typed Attribute
sub Meta { return $[0]->attribute('HASH', 'Meta', $[1]); }
example of an INT typed Attribute;
sub Cursor { return $[0]->attribute('INT', 'Cursor', $[1]); } I'm not finished modifying this concept so I may post some additional enhancments later. But you can get the idea now.