File Coverage

blib/lib/Object/Destroyer.pm
Criterion Covered Total %
statement 53 53 100.0
branch 30 30 100.0
condition 5 7 71.4
subroutine 10 10 100.0
pod 3 4 75.0
total 101 104 97.1


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