File Coverage

blib/lib/Object/Trampoline.pm
Criterion Covered Total %
statement 43 48 89.5
branch 8 12 66.6
condition n/a
subroutine 15 16 93.7
pod n/a
total 66 76 86.8


line stmt bran cond sub pod time code
1             ########################################################################
2             # Object::Trampoline
3             # delay construction of objects until they are needed.
4             #
5             # the only purpose for the top two classes is having an autoload
6             # that blesses things into O::T::Bounce.
7             ########################################################################
8             ########################################################################
9             # housekeeping
10             ########################################################################
11              
12             package Object::Trampoline v1.30.2;
13 4     4   2870 use v5.24;
  4         27  
14              
15 4     4   17 use Carp;
  4         7  
  4         805  
16              
17             ########################################################################
18             # package variables
19             ########################################################################
20              
21             ########################################################################
22             # AUTOLOAD is the only public interface
23             ########################################################################
24             ########################################################################
25             #
26             # start by grabbing the destination class and its arguments
27             # off the stack. the constructor name is whatever is
28             # being autoloaded.
29             #
30             # there may not be any arguments... either way i need
31             # to make a lexical copy of the stack for use in the
32             # closure.
33             #
34             # the closure delays actual contstruction until it is
35             # dereferenced in O::T::Bounce::AUTOLOAD.
36             #
37             # $sub is syntatic sugar but is inexpensive enough to
38             # construct.
39             #
40             # Note: there are no DESTROY blocks in the constructing
41             # classes since no objects ever live there: they begin
42             # life in O::T::Bounce.
43              
44             our $AUTOLOAD = '';
45              
46             AUTOLOAD
47             {
48             # discard this class: once here it is used up.
49             # lacking a prototype is fatal; anything else becomes
50             # a run-time error.
51              
52 10     10   2183 my ( undef, $proto, @argz ) = @_;
53              
54 10 100       28 $proto
55             or croak "Object::Trampoline: false prototype.";
56              
57 7         23 my $method = ( split /::/, "$AUTOLOAD" )[ -1 ];
58              
59 7     11   26 my $sub = sub { $proto->$method( @argz ) };
  11         39  
60              
61 7         21 bless $sub, 'Object::Trampoline::Bounce'
62             }
63              
64             ########################################################################
65             # same gizmo as O::T except that the class is use-ed before the
66             # constructor is called.
67             ########################################################################
68             # housekeeping
69             ########################################################################
70              
71             package Object::Trampoline::Use;
72 4     4   45 use v5.24;
  4         11  
73              
74 4     4   17 use Carp;
  4         7  
  4         846  
75              
76             ########################################################################
77             # package variables
78             ########################################################################
79              
80             *VERSION = \$Object::Trampoline::VERSION;
81              
82             ########################################################################
83             # AUTOLOAD is the only public interface
84             ########################################################################
85            
86             our $AUTOLOAD = '';
87              
88             AUTOLOAD
89             {
90             # this version does slightly more work since it
91             # has to put using the module into the caller's
92             # class before calling the constructor.
93              
94 4     4   1958 my ( undef, $proto, @argz ) = @_;
95              
96 4 100       15 $proto
97             or croak "Object::Trampoline::Use: false prototype.";
98              
99 1         3 my $method = ( split /::/, $AUTOLOAD )[ -1 ];
100 1         3 my $caller = caller;
101              
102 1         4 my $init
103             = qq
104             {
105             package $caller;
106             use $proto
107             };
108              
109             my $sub =
110             sub
111             {
112 1 50   1   66 eval "$init"
  1     1   240  
  0            
  0            
113             or croak "Failed: $init\n$@";
114            
115 0         0 $proto->$method( @argz )
116 1         4 };
117              
118 1         5 bless $sub, 'Object::Trampoline::Bounce'
119             }
120              
121             ########################################################################
122             # where the object ends up. All this does is possibly use the package
123             # then construct the object and dispatch the call to whatever the
124             # caller was looking for -- which may fail if the package doesn't
125             # implement the method.
126             #
127             # $_[0] = $_[0]->() replaces the trampoline argument
128             # with the real thing by calling its constructor -- call
129             # by reference is a Very Good Thing.
130             #
131             # after that it can be shifted off and used to access
132             # the method. note that this is necessary in order
133             # to allow for classes which implement their methods
134             # via AUTOLOAD (which will defeat using $obj->can( $name )).
135             #
136             # note that it's up to the caller to deal with any exceptions
137             # that come out of calling the method.
138             #
139             # goto is a more effecient way to get there if the class
140             # has an explicit method for handling the call; otherwise
141             # use the name to dispatch the call.
142              
143             package Object::Trampoline::Bounce;
144              
145 4     4   45 use v5.24;
  4         14  
146              
147 4     4   18 use Carp;
  4         5  
  4         210  
148              
149 4     4   19 use Scalar::Util qw( blessed );
  4         8  
  4         313  
150 4     4   1541 use Symbol qw( qualify_to_ref );
  4         2559  
  4         1123  
151              
152             # version is defined by the main package.
153              
154             *VERSION = \$Object::Trampoline::VERSION;
155              
156             our $AUTOLOAD = '';
157              
158             AUTOLOAD
159             {
160             # caller gets back any execption as-is.
161              
162 12 50   12   1894 $_[0] = $_[0]->()
163             or croak "Failed constructor";
164              
165 11 50       149 my $class = blessed $_[0]
166             or croak "Failed constructor: '$_[0]' not blessed";
167              
168 11         35 my $method = ( split /::/, $AUTOLOAD )[ -1 ];
169              
170 11 50       48 if( my $sub = $class->can( $method ) )
171             {
172 11         56 goto &$sub
173             }
174             else
175             {
176             # deal with autoloaded methods, or die trying...
177              
178 0         0 my $obj = shift;
179              
180 0         0 $obj->$method( @_ )
181             }
182             }
183              
184             # re-route methods from UNIVERSAL through the bounce.
185             # allows $trampoline->VERSION to do the right thing.
186              
187             our $is_override
188             = sub
189             {
190             # sub allows testing w/o reproducing the
191             # sanity checks in every test.
192             #
193             # basic checks: non-empty name w/o non-word
194             # chars that has as a coderef in UNIVERSAL.
195              
196             my $name = shift
197             or return;
198              
199             $name =~ /\W/
200             and return;
201              
202             defined &{ "UNIVERSAL::$name" }
203             or return;
204              
205             1
206             };
207              
208             for my $name ( keys %{ $::{ 'UNIVERSAL::' } } )
209             {
210             # skip stash entries which cannot map to
211             # valid method names.
212              
213             $is_override->( $name )
214             and
215             *{ qualify_to_ref $name }
216             = sub
217             {
218 8     8   8471 $AUTOLOAD = $name;
219 8         24 goto &AUTOLOAD
220             };
221             }
222              
223             # stub destroy dodges AUTOLOAD for unused trampolines.
224              
225       0     DESTROY {}
226              
227             # keep require happy
228             1
229             __END__