File Coverage

blib/lib/Object/Destroyer.pm
Criterion Covered Total %
statement 50 54 92.5
branch 26 30 86.6
condition 5 7 71.4
subroutine 11 11 100.0
pod 3 4 75.0
total 95 106 89.6


line stmt bran cond sub pod time code
1             package Object::Destroyer;
2              
3             # See POD at end for details
4              
5 5     5   101585 use 5.006;
  5         19  
  5         196  
6 5     5   27 use strict;
  5         9  
  5         169  
7 5     5   40 use Carp ();
  5         10  
  5         112  
8             ##use Scalar::Util ();
9              
10 5     5   30 use vars qw{$VERSION};
  5         8  
  5         361  
11             BEGIN {
12 5     5   5121 $VERSION = '2.01';
13             }
14              
15             if ( eval { require Scalar::Util } ) {
16             Scalar::Util->import('blessed');
17             } else {
18             *blessed = sub {
19             my $ref = ref($_[0]);
20             return $ref
21             if $ref && ($ref ne 'SCALAR') && ($ref ne 'ARRAY') &&
22             ($ref ne 'HASH') && ($ref ne 'CODE') &&
23             ($ref ne 'REF') && ($ref ne 'GLOB') &&
24             ($ref ne 'LVALUE');
25             return;
26             }
27             }
28              
29             sub new {
30 47 100   47 1 20902 if ( ref $_[0] ) {
31             # This is a method called on an existing
32             # Destroyer, and should actually be passed through
33             # to the encased object via the AUTOLOAD
34 1         3 $Object::Destroyer::AUTOLOAD = '::new';
35 1         3 goto &AUTOLOAD;
36             }
37              
38             # *ahem*... where were we...
39 46         62 my $destroyer = shift;
40 46   50     184 my $ref = shift || '';
41 46         63 my $self = {};
42            
43 46 100       198 if ( ref($ref) eq 'CODE' ) {
    100          
44             ##
45             ## Object::Destroyer->new( sub {...} )
46             ##
47 24         42 $self->{code} = $ref;
48             } elsif ( my $class = blessed($ref) ) {
49             ##
50             ## Object::Destroyer->new( $object, 'optional_method' )
51             ##
52 21   100     80 my $method = shift || 'DESTROY';
53 21 100       172 Carp::croak("Second argument to constructor must be a method name")
54             if ref($method);
55 20 100       508 Carp::croak("Object::Destroyer requires that $class has a $method method")
56             unless $class->can($method);
57 18         47 $self->{object} = $ref;
58 18         45 $self->{method} = $method;
59             } else {
60             ##
61             ## And what is this?
62             ##
63 1         143 Carp::croak("You should pass an object or code reference to constructor");
64             }
65 42 100       327 Carp::croak("Extra arguments to constructor") if @_;
66              
67 40         145 return bless $self, $destroyer;
68             }
69              
70             # Hand off general method calls to the encased object.
71             # Rather than just doing a $self->{object}->$method(@_), which
72             # would leave us in the call stack, find the actual subroutine
73             # that will be executed, and goto that directly.
74             sub AUTOLOAD {
75 16     16   7466 my $self = shift;
76            
77 16         73 my ($method) = $Object::Destroyer::AUTOLOAD =~ /^.*::(.*)$/;
78 16 50       49 if (my $object = $self->{object}) {
79 16 100       146 if (my $function = $object->can($method)) {
    50          
80             ##
81             ## Rearrange stack - instead of
82             ## $object_destroy->method(@params)
83             ## make it look like
84             ## $underlying_object->method(@params)
85             ##
86 10         16 unshift @_, $object;
87 10         68 goto &$function;
88             } elsif ( $object->can("AUTOLOAD") ) {
89             ##
90             ## We can't just goto to AUTOLOAD method in unknown
91             ## package (it may be in base class of $object).
92             ## We have to preserve the method's name.
93             ##
94 6 100       15 if (wantarray) {
    50          
95             ## List context
96 3         11 return $object->$method(@_);
97             } elsif ( defined wantarray ) {
98             ## Scalar context
99 3         12 return scalar $object->$method(@_);
100             } else {
101             ## Void context
102 0         0 $object->$method(@_);
103             }
104             } else {
105             ##
106             ## Probably this is a caller's error
107             ##
108 0         0 my $package = ref $self->{object};
109 0         0 Carp::croak(qq[Can't locate object method "$method" via package "$package"]);
110             }
111             }
112            
113             ##
114             ## No object at all. Either we have a $coderef instead of object
115             ## or DESTROY has been called already.
116             ##
117 0         0 Carp::croak("Can't locate object to call method '$method'");
118             }
119              
120             sub dismiss{
121 1     1 1 342 $_[0]->{dismissed} = 1;
122             }
123              
124             ##
125             ## Use our automatically triggered DESTROY to call the
126             ## non-automatically triggered clean-up method of the encased object
127             ##
128             sub DESTROY {
129 42     42   5471 my $self = shift;
130              
131 42 100       157 if ( $self->{dismissed} ) {
    100          
    100          
132             ## do nothing
133             } elsif ( $self->{code} ) {
134 23         50 $self->{code}->();
135             } elsif ( my $object = $self->{object} ) {
136 16         28 my $method = $self->{method};
137 16         55 $object->$method();
138             }
139              
140 42         372 %$self = ();
141             }
142              
143             ##
144             ## Catch a couple of specific cases that would be handled by UNIVERSAL
145             ## before our AUTOLOAD got a chance to dispatch it.
146             ##
147             ## We are both 'Object::Destroyer' (or it's derived class)
148             ## and underlying object's class
149             ##
150             sub isa {
151 4     4 1 2313 my $self = shift;
152 4         5 my $class = shift;
153              
154 4   66     41 return $class eq __PACKAGE__ ||
155             ($self->{object} && $self->{object}->isa($class));
156             }
157              
158             sub can {
159 6     6 0 125 my $self = shift;
160 6 50       47 return $self->{object}->can(@_) if $self->{object};
161             }
162              
163             1;
164              
165             __END__