Class-Observable
view release on metacpan or search on metacpan
lib/Class/Observable.pm view on Meta::CPAN
use strict; use warnings;
package Class::Observable;
our $VERSION = '2.004';
use Scalar::Util 'refaddr';
use Class::ISA;
# Unused; kept for backward compatibility only
my ( $DEBUG );
sub DEBUG { return $DEBUG; }
sub SET_DEBUG { $DEBUG = $_[0] }
sub observer_log { shift; $DEBUG && warn @_, "\n" }
sub observer_error { shift; die @_, "\n" }
my ( %O, %registry );
BEGIN {
require Config;
if ( $^O eq 'Win32' or $Config::Config{'useithreads'} ) {
*NEEDS_REGISTRY = sub () { 1 };
*CLONE = sub {
my $have_warned;
foreach my $oldaddr ( keys %registry ) {
my $invocant = delete $registry{ $oldaddr };
my $observers = delete $O{ $oldaddr };
if ( defined $invocant ) {
my $addr = refaddr $invocant;
$O{ $addr } = $observers;
Scalar::Util::weaken( $registry{ $addr } = $invocant );
} else {
$have_warned++ or warn
"*** Inconsistent state ***\n",
"Observed instances have gone away " .
"without invoking Class::Observable::DESTROY\n";
}
}
};
} else {
*NEEDS_REGISTRY = sub () { 0 };
}
}
sub DESTROY {
my $invocant = shift;
my $addr = refaddr $invocant;
delete $registry{ $addr } if NEEDS_REGISTRY and $addr;
delete $O{ $addr || "::$invocant" };
}
sub add_observer {
my $invocant = shift;
my $addr = refaddr $invocant;
Scalar::Util::weaken( $registry{ $addr } = $invocant ) if NEEDS_REGISTRY and $addr;
push @{ $O{ $addr || "::$invocant" } }, @_;
}
sub delete_observer {
my $invocant = shift;
my $addr = refaddr $invocant;
my $observers = $O{ $addr || "::$invocant" } or return 0;
my %removal = map +( refaddr( $_ ) || "::$_" => 1 ), @_;
@$observers = grep !$removal{ refaddr( $_ ) || "::$_" }, @$observers;
if ( ! @$observers ) {
delete $registry{ $addr } if NEEDS_REGISTRY and $addr;
delete $O{ $addr || "::$invocant" };
}
scalar @$observers;
}
sub delete_all_observers {
my $invocant = shift;
my $addr = refaddr $invocant;
delete $registry{ $addr } if NEEDS_REGISTRY and $addr;
my $removed = delete $O{ $addr || "::$invocant" };
$removed ? scalar @$removed : 0;
}
# Backward compatibility
*delete_observers = \&delete_all_observers;
sub notify_observers {
for ( $_[0]->get_observers ) {
ref eq 'CODE' ? $_->( @_ ) : $_->update( @_ );
}
}
my %supers;
sub get_observers {
my ( @self, $class );
if ( my $pkg = ref $_[0] ) {
@self = $_[0];
$class = $pkg;
} else {
$class = $_[0];
}
# We only find the parents the first time, so if you muck with
# @ISA you'll get unexpected behavior...
my $cached_supers = $supers{ $class } ||= [
grep $_->isa( 'Class::Observable' ), Class::ISA::super_path( $class )
];
map $_->get_direct_observers, @self, $class, @$cached_supers;
}
sub copy_observers {
my ( $src, $dst ) = @_;
my @observer = $src->get_observers;
$dst->add_observer( @observer );
scalar @observer;
}
sub count_observers { scalar $_[0]->get_observers }
( run in 0.527 second using v1.01-cache-2.11-cpan-39bf76dae61 )