File Coverage

blib/lib/Class/Adapter/Builder.pm
Criterion Covered Total %
statement 112 129 86.8
branch 39 52 75.0
condition 4 9 44.4
subroutine 25 26 96.1
pod 1 9 11.1
total 181 225 80.4


line stmt bran cond sub pod time code
1             package Class::Adapter::Builder;
2             # ABSTRACT: Generate Class::Adapter classes
3              
4             #pod =pod
5             #pod
6             #pod =head1 SYNOPSIS
7             #pod
8             #pod package My::Adapter;
9             #pod
10             #pod use strict;
11             #pod use Class::Adapter::Builder
12             #pod ISA => 'Specific::API',
13             #pod METHODS => [ qw{foo bar baz} ],
14             #pod method => 'different_method';
15             #pod
16             #pod 1;
17             #pod
18             #pod =head1 DESCRIPTION
19             #pod
20             #pod C is another mechanism for letting you create
21             #pod I classes of your own.
22             #pod
23             #pod It is intended to act as a toolkit for generating the guts of many varied
24             #pod and different types of I classes.
25             #pod
26             #pod For a simple base class you can inherit from and change a specific method,
27             #pod see L.
28             #pod
29             #pod =head2 The Pragma Interface
30             #pod
31             #pod The most common method for defining I classes, as shown in the
32             #pod synopsis, is the pragma interface.
33             #pod
34             #pod This consists of a set of key/value pairs provided when you load the module.
35             #pod
36             #pod # The format for building Adapter classes
37             #pod use Class::Adapter::Builder PARAM => VALUE, ...
38             #pod
39             #pod =over 4
40             #pod
41             #pod =item ISA
42             #pod
43             #pod The C param is provided as either a single value, or a reference
44             #pod to an C containing is list of classes.
45             #pod
46             #pod Normally this is just a straight list of classes. However, if the value
47             #pod for C is set to C<'_OBJECT_'> the object will identify itself as
48             #pod whatever is contained in it when the C<-Eisa> and C<-Ecan> method
49             #pod are called on it.
50             #pod
51             #pod =item NEW
52             #pod
53             #pod Normally, you need to create your C objects separately:
54             #pod
55             #pod # Create the object
56             #pod my $query = CGI->new( 'param1', 'param2' );
57             #pod
58             #pod # Create the Decorator
59             #pod my $object = My::Adapter->new( $query );
60             #pod
61             #pod If you provide a class name as the C param, the Decorator will
62             #pod do this for you, passing on any constructor arguments.
63             #pod
64             #pod # Assume we provided the following
65             #pod # NEW => 'CGI',
66             #pod
67             #pod # We can now do the above in one step
68             #pod my $object = My::Adapter->new( 'param1', 'param2' );
69             #pod
70             #pod =item AUTOLOAD
71             #pod
72             #pod By default, a C does not pass on any methods, with the
73             #pod methods to be passed on specified explicitly with the C<'METHODS'>
74             #pod param.
75             #pod
76             #pod By setting C to true, the C will be given the
77             #pod standard C function to to pass through all unspecified
78             #pod methods to the parent object.
79             #pod
80             #pod By default the AUTOLOAD will pass through any and all calls, including
81             #pod calls to private methods.
82             #pod
83             #pod If the AUTOLOAD is specifically set to 'PUBLIC', the AUTOLOAD setting
84             #pod will ONLY apply to public methods, and any private methods will not
85             #pod be passed through.
86             #pod
87             #pod =item METHODS
88             #pod
89             #pod The C param is provided as a reference to an array of all
90             #pod the methods that are to be passed through to the parent object as is.
91             #pod
92             #pod =back
93             #pod
94             #pod Any params other than the ones specified above are taken as translated
95             #pod methods.
96             #pod
97             #pod # If you provide the following
98             #pod # foo => bar
99             #pod
100             #pod # It the following are equivalent
101             #pod $decorator->foo;
102             #pod $decorator->_OBJECT_->bar;
103             #pod
104             #pod This capability is provided primarily because in Perl one of the main
105             #pod situations in which you hit the limits of Perl's inheritance model is
106             #pod when your class needs to inherit from multiple different classes that
107             #pod containing clashing methods.
108             #pod
109             #pod For example:
110             #pod
111             #pod # If your class is like this
112             #pod package Foo;
113             #pod
114             #pod use base 'This', 'That';
115             #pod
116             #pod 1;
117             #pod
118             #pod If both Cmethod> exists and Cmethod> exists,
119             #pod and both mean different things, then Cmethod> becomes
120             #pod ambiguous.
121             #pod
122             #pod A C could be used to wrap your C object, with
123             #pod the C becoming the C sub-class, and passing
124             #pod C<$decorator-Emethod> through to C<$object-Ethat_method>.
125             #pod
126             #pod =head1 METHODS
127             #pod
128             #pod Yes, C has public methods and later on you will
129             #pod be able to access them directly, but for now they are remaining
130             #pod undocumented, so that I can shuffle things around for another few
131             #pod versions.
132             #pod
133             #pod Just stick to the pragma interface for now.
134             #pod
135             #pod =cut
136              
137 8     8   216117 use 5.005;
  8         46  
138 8     8   65 use strict;
  8         278  
  7         131  
139 7     8   31 use Carp ();
  8         17  
  8         94  
140 8     8   1741 use Class::Adapter ();
  8         18  
  8         6614  
141              
142             our $VERSION = '1.09';
143              
144              
145              
146              
147              
148             #####################################################################
149             # Constructor
150              
151             sub new {
152 12   33 10 0 359 my $class = ref $_[0] || $_[0];
153 11         59 return bless {
154             target => $_[1],
155             isa => [ 'Class::Adapter' ],
156             modules => {},
157             methods => {},
158             }, $class;
159             }
160              
161             sub import {
162 10     10   1220 my $class = shift;
163              
164             # Must have at least one param
165 10 100       59 return 1 unless @_;
166              
167             # Create the Builder object
168 9         292 my $target = caller;
169 8         19 my $self = $class->new( $target );
170 8 50       44 unless ( $self ) {
171 0         0 Carp::croak("Failed to create Class::Adapter::Builder object");
172             }
173              
174             # Process the option pairs
175 8         20 while ( @_ ) {
176 21         29 my $key = shift;
177 21         33 my $value = shift;
178 21 100       53 if ( $key eq 'NEW' ) {
    100          
    50          
    0          
179 5         10 $self->set_NEW( $value );
180             } elsif ( $key eq 'ISA' ) {
181 8         15 $self->set_ISA( $value );
182             } elsif ( $key eq 'AUTOLOAD' ) {
183 8         18 $self->set_AUTOLOAD( $value );
184             } elsif ( $key eq 'METHODS' ) {
185 0         0 $self->set_METHODS( $value );
186             } else {
187 0         0 $self->set_method( $key, $value );
188             }
189             }
190              
191             # Generate the code
192 8 50       15 my $code = $self->make_class or Carp::croak(
193             "Failed to generate Class::Adapter::Builder class"
194             );
195              
196             # Compile the combined code via a temp file so that debugging works
197             #require File::Temp;
198             #my ($fh, $filename) = File::Temp::tempfile();
199             #$fh->print("$code");
200             #close $fh;
201             #require $filename;
202             #print "Loaded '$filename'\n";
203              
204 7 50 66 7 1 57 eval "$code";
  7 100 33 7 0 253  
  8 100   7   106  
  8 100   6   38  
  7     7   87  
  7     10   82  
  7     2   295  
  6     12   17  
  6         132  
  6         612  
  8         492  
  7         3430  
  7         27  
  7         18  
  0         0  
  7         16  
  10         3092  
  4         15  
  6         151  
  13         1734  
205 8 50       24 $@ and Carp::croak(
206             "Error while compiling Class::Adapter::Builder class '$target' ($@)"
207             );
208              
209 8         2234 $target;
210             }
211              
212              
213              
214              
215              
216             #####################################################################
217             # Main Methods
218              
219             sub set_NEW {
220 13     7 0 113 my $self = shift;
221 7         69 $self->{new} = shift;
222              
223             # We always need Scalar::Util to pass through new
224 6         998 $self->{modules}->{'Scalar::Util'} = 1;
225              
226             # Add a use for the module unless it is already loaded.
227             # We test with the can call instead of just blindly require'ing in
228             # case we want to NEW to something that doesn't have it's own
229             # .pm file.
230 6 100       36 unless ( $self->{new}->can('new') ) {
231 2         24 $self->{modules}->{ $self->{new} } = 1;
232             }
233              
234 6         17 return 1;
235             }
236              
237             sub set_ISA {
238 10     10 0 2665 my $self = shift;
239 10 100       29 my $array = ref $_[0] eq 'ARRAY' ? shift : [ @_ ];
240 10         41 $self->{isa} = $array;
241 10         26 return 1;
242             }
243              
244             sub set_AUTOLOAD {
245 10     10 0 441 my $self = shift;
246 10 100       20 if ( $_[0] ) {
247 9         46 $self->{autoload} = 1;
248 9         26 $self->{modules}->{Carp} = 1;
249 8 100       22 if ( $_[0] eq 'PUBLIC' ) {
250 1         2 $self->{autoload_public} = 1;
251             }
252             } else {
253 1         2 delete $self->{autoload};
254             }
255 9         22 return 1;
256             }
257              
258             sub set_METHODS {
259 0     1 0 0 my $self = shift;
260 0 50       0 my $array = ref $_[0] eq 'ARRAY' ? shift : [ @_ ];
261 0         0 foreach my $name ( @$array ) {
262 0 50       0 $self->set_method( $name, $name ) or return undef;
263             }
264 0         0 return 1;
265             }
266              
267             sub set_method {
268 0     1 0 0 my $self = shift;
269 0 50       0 if ( @_ == 1 ) {
    50          
270 0         0 $self->{methods}->{$_[0]} = $_[0];
271             } elsif ( @_ == 2 ) {
272 0         0 $self->{methods}->{$_[0]} = $_[1];
273             } else {
274 0         0 return undef;
275             }
276 0         0 return 1;
277             }
278              
279              
280              
281              
282              
283             #####################################################################
284             # Code Generation Functions
285              
286             sub make_class {
287 9     9 0 12 my $self = shift;
288              
289             # Generate derived lists
290 9         17 my %seen = ();
291             $self->{load} = [
292 14         34 grep { $_ !~ /^Class::Adapter(?:::Builder)?$/ }
293 14         54 sort grep { ! $seen{$_}++ }
294 9         11 keys %{$self->{modules}}
  9         34  
295             ];
296             $self->{fake} = [
297 9         24 grep { ! $seen{$_} } grep { $_ ne '_OBJECT_' } @{$self->{isa}}
  3         7  
  9         22  
  9         16  
298             ];
299              
300             # Build up the parts of the class
301 9         30 my @parts = (
302             "package $self->{target};\n\n"
303             . "# Generated by Class::Abstract::Builder\n"
304             );
305              
306 9 50       14 if ( keys %{$self->{modules}} ) {
  9         22  
307 9         28 push @parts, $self->_make_modules;
308             }
309              
310 9 100       30 if ( $self->{new} ) {
311 5         10 push @parts, $self->_make_new( $self->{new} );
312             }
313              
314 9         16 my $methods = $self->{methods};
315 9         21 foreach my $name ( keys %$methods ) {
316 0         0 push @parts, $self->_make_method( $name, $methods->{$name} );
317             }
318              
319 9 50       11 if ( @{$self->{isa}} == 1 ) {
  9         22  
320 9 100       30 if ( $self->{isa}->[0] eq '_OBJECT_' ) {
321 6         13 push @parts, $self->_make_OBJECT;
322             } else {
323 3         5 push @parts, $self->_make_ISA( @{$self->{isa}} );
  3         6  
324             }
325             }
326              
327 9 100       21 if ( $self->{autoload} ) {
328 8         25 push @parts, $self->_make_AUTOLOAD( $self->{target}, $self->{autoload_public} );
329             }
330              
331 9         67 return join( "\n", @parts, "1;\n" );
332             }
333              
334             sub _make_modules {
335 9     9   13 my $self = shift;
336 9         12 my $pkg = $self->{target};
337             my $load = join '',
338 14         44 map { "\nuse $_ ();" }
339 9         13 @{$self->{load}};
  9         15  
340              
341             # Foo->isa('Foo') returns false if the namespace does not exist
342             # Use the package command in a scope to create namespaces where needed.
343             my $namespaces = join '',
344 2         6 map { "\n\t$_->isa('$_') or do { package $_ };" }
345 9         16 @{$self->{fake}};
  9         17  
346              
347 9         30 return <<"END_MODULES";
348             use strict;${load}
349             use Class::Adapter ();
350              
351             BEGIN {
352             \@${pkg}::ISA = 'Class::Adapter';${namespaces}
353             }
354             END_MODULES
355             }
356              
357 5     5   10 sub _make_new { <<"END_NEW" }
358             sub new {
359             my \$class = ref \$_[0] ? ref shift : shift;
360             my \$object = $_[1]\->new(\@_);
361             Scalar::Util::blessed(\$object) or return undef;
362             \$class->SUPER::new(\$object);
363             }
364             END_NEW
365              
366              
367              
368 0     0   0 sub _make_method { <<"END_METHOD" }
369             sub $_[1] { shift->_OBJECT_->$_[2](\@_) }
370             END_METHOD
371              
372              
373              
374 6     6   13 sub _make_OBJECT { <<"END_OBJECT" }
375             sub isa {
376             ref(\$_[0])
377             ? shift->_OBJECT_->isa(\@_)
378             : shift->isa(\@_);
379             }
380              
381             sub can {
382             ref(\$_[0])
383             ? shift->_OBJECT_->can(\@_)
384             : shift->can(\@_);
385             }
386             END_OBJECT
387              
388             sub _make_ISA {
389 3     3   3 my $self = shift;
390             my @lines = (
391             "sub isa {\n",
392 3         13 ( map { "\treturn 1 if \$_[1]->isa('$_');\n" } @_ ),
393             "\treturn undef;\n",
394             "}\n",
395             "\n",
396             "sub can {\n",
397             # If we are pretending to be a fake ISA, and we get a can call,
398             # we should try to require the module (even if it doesn't exist)
399             # so that we can provide an accurate answer in the case where
400             # we are faking a module that exists.
401 2         5 ( map { "\trequire $_ unless $_->isa('UNIVERSAL');\n" } @{$self->{fake}} ),
  3         6  
402             "\treturn 1 if \$_[0]->SUPER::can(\$_[1]);\n",
403 3         13 ( map { "\treturn 1 if $_->can(\$_[1]);\n" } @_ ),
  3         9  
404             "\treturn undef;\n",
405             "}\n",
406             );
407 3         11 return join '', @lines;
408             }
409              
410              
411              
412 8 100   8   28 sub _make_AUTOLOAD { my $pub = $_[2] ? 'and substr($method, 0, 1) ne "_"' : ''; return <<"END_AUTOLOAD" }
  8         31  
413             sub AUTOLOAD {
414             my \$self = shift;
415             my (\$method) = \$$_[1]::AUTOLOAD =~ m/^.*::(.*)\\z/s;
416             unless ( ref(\$self) $pub) {
417             Carp::croak(
418             qq{Can't locate object method "\$method" via package "\$self" }
419             . qq{(perhaps you forgot to load "\$self")}
420             );
421             }
422             \$self->_OBJECT_->\$method(\@_);
423             }
424              
425             sub DESTROY {
426             if ( defined \$_[0]->{OBJECT} and \$_[0]->{OBJECT}->can('DESTROY') ) {
427             undef \$_[0]->{OBJECT};
428             }
429             }
430             END_AUTOLOAD
431              
432             1;
433              
434             __END__