package Variant1::RootClass; our $VERSION = 1.00; use strict; use warnings; use Carp; use vars qw( $AUTOLOAD ); { # Encapsulated class data my %_attr_data = # DEFAULT ACCESSIBILITY ( '_root_entry1' => [ undef , 'read/write'], '_root_entry2' => [ undef , 'read'] ); # 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; } } # Constructor may be called as a class method or object method sub new { my ($caller, %arg) = @_; my $caller_is_obj = ref($caller); my $class = $caller_is_obj || $caller; my $self = bless {}, $ class; foreach my $attr_name ( $self->_standard_keys() ) { my ($arg_name) = ($attr_name =~ /^_(.*)/); # take value from given argument if available if( exists $arg{$arg_name} ) { $self->{$attr_name} = $arg{$arg_name}; } # take value from object if caller is an object elsif( $caller_is_obj ) { $self->{$attr_name} = $caller->{$attr_name}; } # take default value else { $self->{$attr_name} = $self->_default_for($attr_name); } } return $self; } sub DESTROY { # nothing to do here } # Implement get_... and set_... methods 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 a mistake then ... croak "No such method: $AUTOLOAD"; } 1;