File Coverage

blib/lib/OO/InsideOut.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2             package OO::InsideOut;
3              
4 1     1   22889 use 5.008;
  1         4  
  1         35  
5              
6 1     1   5 use strict;
  1         2  
  1         31  
7 1     1   5 use warnings;
  1         6  
  1         35  
8              
9 1     1   5 use Exporter 'import';
  1         2  
  1         42  
10 1     1   4 use Carp qw(croak);
  1         2  
  1         58  
11 1     1   483 use Class::ISA ();
  0            
  0            
12             use Scalar::Util 1.09 qw(weaken refaddr);
13              
14             our $VERSION = '0.03';
15             our @EXPORT = qw();
16             our @EXPORT_OK = qw(Dumper id register);
17              
18             my (%Hash, %Object, %Method);
19              
20             my $Dumper = eval {
21             use Data::Dumper ();
22             return \&Data::Dumper::Dumper;
23             };
24              
25             ### Internal Functions ###
26              
27             my $classes = sub {
28             my $self = shift;
29              
30             # no point in returning classes that dont use OO::InsideOut
31             return
32             grep { exists $Object{ $_ } }
33             Class::ISA::self_and_super_path( ref $self || $self );
34             };
35              
36             my $register_object = sub {
37             my $self = shift;
38             my $id = id( $self );
39              
40             for my $class ( $self->$classes ) {
41             my $obj = $Object{ $class };
42              
43             # object allready registered, skip
44             exists $obj->{ $id }
45             && next;
46              
47             # to allow object destruction
48             weaken( $obj->{ $id } = $self );
49             }
50              
51             return $self;
52             };
53              
54             my $unregister_object = sub {
55             my $self = shift;
56             my $id = id( $self );
57              
58             for my $class ( $self->$classes ) {
59             # Even if there's no new, there can be stored values
60             map { delete $_->{ $id } }
61             @{ $Hash{ $class } };
62              
63             my $obj = $Object{ $class };
64              
65             # object may allready been destroyed, skip
66             exists $obj->{ $id }
67             || next;
68              
69             delete $obj->{ $id };
70              
71             # force cleanup on classes with no active objects
72             unless ( keys %{ $Object{ $class } } ) {
73             delete $Hash{ $class };
74             delete $Object{ $class };
75             }
76             }
77              
78             return $self;
79             };
80              
81             my $register_new = sub {
82             my $class = shift;
83              
84             my $new = $class->can('new');
85              
86             # no new defined, no object registration needed
87             defined $new
88             || return;
89            
90             # we allready wrapped new
91             exists $Method{ refaddr $new }
92             && return;
93              
94             my $method = sub {
95             return shift->$new( @_ )->$register_object;
96             };
97              
98             no strict 'refs';
99             no warnings 'redefine';
100             *{ $class . '::new' } = $method;
101              
102             return ++$Method{ refaddr $method };
103             };
104              
105             my $register_destroy = sub {
106             my $class = shift;
107              
108             my $DESTROY = $class->can('DESTROY');
109            
110             # allready exists a DESTROY method and we allready wrapped it, skip
111             $DESTROY
112             && exists $Method{ refaddr $DESTROY }
113             && return;
114              
115             my $method = sub {
116             my $self = shift;
117              
118             $DESTROY
119             && $self->$DESTROY();
120              
121             $self->$unregister_object;
122              
123             return 1;
124             };
125              
126             no strict 'refs';
127             no warnings 'redefine';
128             *{ $class . '::DESTROY' } = $method;
129              
130             $Method{ refaddr $method }++;
131              
132             return 1;
133             };
134              
135             my $register_hashes = sub {
136             my $class = shift;
137             my @hashes = @_;
138              
139             # no hashes, no joy, skip
140             scalar @hashes
141             or return;
142              
143             # we may allready registered this class, skip if so
144             unless ( exists $Hash{ $class } ) {
145             $class->$register_new;
146             $class->$register_destroy;
147             }
148              
149             # register this class to avoid re-registering
150             $Object{ $class } ||= {};
151             push @{ $Hash{ $class } }, @hashes;
152              
153             # if they ask for it, return this class's object registry
154             return defined wantarray ? $Object{ $class } : 1;
155             };
156              
157             ### Exportable Functions ###
158              
159             sub Dumper {
160             my $object = shift;
161             my $id = id( $object );
162            
163             my %dump;
164             for my $class ( $object->$classes ) {
165             exists $Hash{ $class }
166             || next;
167              
168             push @{ $dump{ $class } },
169             map { $_->{ $id } }
170             grep { exists $_->{ $id } }
171             @{ $Hash{ $class } };
172             }
173              
174             return $Dumper->( \%dump );
175             }
176              
177             sub id { return refaddr shift; }
178              
179             sub register {
180             my @args = @_;
181              
182             my @hashes = grep { ref eq 'HASH' } @args;
183            
184             scalar @hashes
185             or croak 'must provide, at least, one hash ref!';
186              
187             my $caller = caller(0);
188             return $caller->$register_hashes( @hashes );
189             }
190              
191             ### Methods ###
192              
193             sub CLONE {
194             my $class = shift;
195              
196             for my $class ( keys %Object ) {
197             my $obj = $Object{ $class };
198              
199             for my $old ( keys %{ $obj } ) {
200             my $new = delete $obj->{ $old };
201              
202             map { $_->{ id $new } = delete $_->{ $old } }
203             @{ $Hash{ $class } };
204              
205             $new->$register_object;
206             }
207              
208             return ;
209             }
210             }
211              
212             1; # End of OO::InsideOut
213              
214             __END__