Class-InsideOut
view release on metacpan or search on metacpan
lib/Class/InsideOut.pm view on Meta::CPAN
&_check_property;
$_[2] ||= {};
$_[2] = {
%{$_[2]},
privacy => 'public',
set_hook => sub { die "is read-only\n" }
};
goto &_install_property;
}
sub register {
my ($obj);
if ( @_ == 0 ) {
# register()
croak "Invalid call to register(): empty argument list"
}
elsif ( @_ == 1 ) {
# register( OBJECT | CLASSNAME )
if ( blessed $_[0] ) {
$obj = shift;
}
elsif ( ref \$_[0] eq 'SCALAR' ) {
$obj = \(my $scalar);
bless $obj, shift;
}
else {
croak "Invalid argument '$_[0]' to register(): " .
"must be an object or class name"
}
}
else {
# register( REFERENCE/OBJECT, CLASSNAME )
$obj = shift;
bless $obj, shift; # ok to rebless
}
weaken( $OBJECT_REGISTRY{ refaddr $obj } = $obj );
return $obj;
}
#--------------------------------------------------------------------------#
# private functions for implementation
#--------------------------------------------------------------------------#
# Registering is global to avoid having to register objects for each class.
# CLONE is not exported but CLONE in Class::InsideOut updates all registered
# objects for all properties across all classes
sub CLONE {
my $class = shift;
# assemble references to all properties for all classes
my @properties = map { values %$_ } values %PROP_DATA_FOR;
for my $old_id ( keys %OBJECT_REGISTRY ) {
# retrieve the new object and id
my $object = $OBJECT_REGISTRY{ $old_id };
my $new_id = refaddr $object;
# for all properties, relocate data to the new id if
# the property has data under the old id
for my $prop ( @properties ) {
next unless exists $prop->{ $old_id };
$prop->{ $new_id } = $prop->{ $old_id };
delete $prop->{ $old_id };
}
# update the registry to the new, cloned object
weaken ( $OBJECT_REGISTRY{ $new_id } = $object );
_deregister( $old_id );
}
}
sub _check_options{
my ($opt) = @_;
local $Carp::CarpLevel = $Carp::CarpLevel + 1;
croak "Invalid options argument '$opt': must be a hash reference"
if ref $opt ne 'HASH';
my @valid_keys = keys %_OPTION_VALIDATION;
for my $key ( keys %$opt ) {
croak "Invalid option '$key': unknown option"
if ! grep { $_ eq $key } @valid_keys;
eval { $_OPTION_VALIDATION{$key}->( $opt->{$key} ) };
croak "Invalid option '$key': $@" if $@;
}
return;
}
sub _check_property {
my ($label, $hash, $opt) = @_;
local $Carp::CarpLevel = $Carp::CarpLevel + 1;
croak "Invalid property name '$label': must be a perl identifier"
if $label !~ /\A[a-z_]\w*\z/i;
croak "Duplicate property name '$label'"
if grep { $_ eq $label } keys %{ $PROP_DATA_FOR{ caller(1) } };
_check_options( $opt ) if defined $opt;
return;
}
sub _class_tree {
my $class = shift;
$CLASS_ISA{ $class } ||= [ Class::ISA::self_and_super_path( $class ) ];
return @{ $CLASS_ISA{ $class } };
}
# take either object or object id
sub _deregister {
my ($arg) = @_;
my $obj_id = ref $arg ? refaddr $arg : $arg;
delete $OBJECT_REGISTRY{ $obj_id };
return;
}
# turn object into hash -- see _revert()
sub _evert {
my ( $obj ) = @_;
( run in 3.666 seconds using v1.01-cache-2.11-cpan-71847e10f99 )