File Coverage

blib/lib/Class/Maker.pm
Criterion Covered Total %
statement 52 167 31.1
branch 0 50 0.0
condition 0 11 0.0
subroutine 18 33 54.5
pod 0 1 0.0
total 70 262 26.7


line stmt bran cond sub pod time code
1             # Author: Murat Uenalan (muenalan@cpan.org)
2             #
3             # Copyright (c) 2001 Murat Uenalan. All rights reserved.
4             #
5             # Note: This program is free software; you can redistribute
6             #
7             # it and/or modify it under the same terms as Perl itself.
8            
9 1     1   727 require 5.005_62; use strict; use warnings;
  1     1   2  
  1         46  
  1         6  
  1         2  
  1         90  
10            
11             package Class::Maker;
12            
13             our $VERSION = '0.5.14';
14            
15 1     1   670 use Class::Maker::Basic::Handler::Attributes;
  1         4  
  1         30  
16            
17 1     1   657 use Class::Maker::Basic::Fields;
  1         2  
  1         28  
18            
19 1     1   5 use Exporter;
  1         2  
  1         41  
20            
21 1     1   985 use subs qw(class);
  1         22  
  1         5  
22            
23             our $DEBUG = 0;
24            
25             our $TRACE = ( \*STDOUT, \*STDERR )[ ($ENV{CLASSMAKER_TRACE}||2) - 1 ];
26            
27             our %EXPORT_TAGS = ( 'all' => [ qw(class) ] );
28            
29             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
30            
31             our @EXPORT = ();
32            
33             our @ISA = qw( Exporter );
34            
35             our $pkg = '';
36            
37             our $cpkg = $pkg;
38            
39             our $explicit = 0;
40            
41             # Preloaded methods go here.
42            
43             sub import
44             {
45 1     1   1601 Class::Maker->export_to_level( 1, @_ );
46             }
47            
48             sub class
49             {
50 0     0     class_import( scalar caller, @_ );
51             }
52            
53             sub class_import
54             {
55             # $class is the caller package
56            
57 0     0 0   my ( $class, @args ) = @_;
58            
59 0 0         return unless @args;
60            
61             # construct the destination package for the classes:
62             #
63             # - we create the class within the current package (default)
64             # - or create it in the current package
65             # - or when starting with 'main::' or '::' we create it with the main package
66            
67 0 0         unless( ref $args[0] )
68             {
69 0 0         $pkg = ( $args[0] =~ s/^(?:main)?::// ) ? $args[0] : $class.'::'.$args[0];
70             }
71             else
72             {
73             # We had no explicit destination package, so create the class in the current package
74            
75 0           $pkg = $class;
76             }
77            
78             #remember caller package
79            
80 0           $cpkg = $class;
81            
82             # init class 'cause somebody could give an empty parameter
83             # list for abstract classes
84            
85 0           Class::Maker::Basic::Fields::isa( [] );
86            
87 0           Class::Maker::Basic::Fields::configure( { ctor => 'new', dtor => 'delete' } );
88            
89 0           foreach my $arg ( @args )
90             {
91 0 0         if( ref($arg) eq 'HASH' )
92             {
93 1     1   403 no strict 'refs';
  1         3  
  1         149  
94            
95 0           Class::Maker::Reflection::install( $arg );
96            
97 0           foreach my $func ( sort { $b cmp $a } keys %$arg )
  0            
98             {
99             # fields for the class attributes/isa/configure/..
100            
101 0           "Class::Maker::Basic::Fields::${func}"->( $arg->{$func}, $arg );
102             }
103             }
104             }
105             }
106            
107             sub _make_method
108             {
109 1     1   5 no strict 'refs';
  1         1  
  1         62  
110            
111 0     0     my $type = shift;
112            
113 0           my $name = shift;
114            
115 0 0         $Class::Maker::Basic::Handler::Attributes::name = $explicit ? "${pkg}::$name" : $name;
116            
117 1     1   4 no strict 'refs';
  1         2  
  1         216  
118            
119 0 0         if( *{ "Class::Maker::Basic::Handler::Attributes::${type}" }{CODE} )
  0            
120             {
121 0           return *{ "${pkg}::$name" } = Class::Maker::Basic::Handler::Attributes->$type;
  0            
122             }
123            
124 0           return *{ "${pkg}::$name" } = Class::Maker::Basic::Handler::Attributes->default;
  0            
125             }
126            
127             #
128             # Reflection
129             #
130            
131             package Class::Maker::Reflex; # returned by Class::Maker::Reflection::reflect
132            
133             sub definition : method
134             {
135 0     0     my $this = shift;
136            
137 0           return $this->{def};
138             }
139            
140             sub parents : method
141             {
142 0     0     my $this = shift;
143            
144 0 0         return unless exists $this->{isa};
145            
146 0           return Class::Maker::Reflection::inheritance_isa( @{ $this->{isa} } );
  0            
147             }
148            
149             package Class::Maker::Reflection;
150            
151             our $DEBUG = $Class::Maker::DEBUG;
152            
153             # DEEP : Whether reflect should traverse the @ISA tree and return all parent reflex's
154            
155             our $DEEP = 0;
156            
157             our $DEFINITION = 'CLASS';
158            
159             sub _get_definition
160             {
161 0     0     my $class = shift;
162            
163 1     1   4 no warnings;
  1         2  
  1         57  
164            
165 1     1   4 no strict 'refs';
  1         2  
  1         81  
166            
167 0           return \${ "${class}::${DEFINITION}" };
  0            
168             }
169            
170             sub _get_isa
171             {
172 1     1   6 no strict 'refs';
  1         2  
  1         196  
173            
174 0     0     return @{ $_[0].'::ISA'};
  0            
175             }
176            
177             sub install
178             {
179 0     0     ${ Class::Maker::Reflection::_get_definition( $pkg ) } = $_[0];
  0            
180             }
181            
182             sub reflect
183             {
184 0   0 0     my $class = ref( $_[0] ) || $_[0] || die;
185            
186 0           my $rfx = bless { name => $class }, 'Class::Maker::Reflex';
187            
188             # - First get the "${$DEFINITION}" href containing the class definition
189             # - find the functions of that class declerated with ': method'
190             # - catch up the parent class reflection if DEEP is activated
191             # - update "${$DEFINITION}"->{isa} with its real @ISA
192            
193 0           $rfx->{def} = ${ Class::Maker::Reflection::_get_definition( $class ) };
  0            
194            
195 0           $rfx->{methods} = find_methods( $rfx->{name} );
196            
197 1     1   6 no strict 'refs';
  1         2  
  1         155  
198            
199 0 0 0       if( $DEEP && defined *{ "${class}::ISA" }{ARRAY} )
  0            
200             {
201 0           $rfx->{isa} = \@{ *{ "${class}::ISA" }{ARRAY} };
  0            
  0            
202            
203 0           $rfx->{parents}->{$_} = reflect( $_ ) for @{ $rfx->{isa} };
  0            
204             }
205            
206 0           return $rfx;
207             }
208            
209             sub classes
210             {
211 1     1   5 no strict 'refs';
  1         2  
  1         280  
212            
213 0     0     my @found;
214            
215 0 0         my $path = shift if @_ > 1;
216            
217 0           foreach my $pkg ( @_ )
218             {
219 0 0         next unless $pkg =~ /::$/;
220            
221 0           $path .= $pkg;
222            
223 0 0         if( $path =~ /(.*)::$/ )
224             {
225 0           my $clean_path = $1;
226            
227 0 0         if( $path ne 'main::' )
228             {
229 0 0         if( my $href_cls = reflect( $clean_path ) )
230             {
231 0           push @found, { $clean_path => $href_cls };
232             }
233             }
234            
235 0           foreach my $symbol ( sort keys %{$path} )
  0            
236             {
237 0 0 0       if( $symbol =~ /::$/ && $symbol ne 'main::' )
238             {
239 0           push @found, classes( $path, $symbol );
240             }
241             }
242             }
243             }
244            
245 0           return @found;
246             }
247            
248 1     1   997 use attributes;
  1         1427  
  1         5  
249            
250             sub find_methods
251             {
252 0     0     my $class = shift;
253            
254 0           my $methods = [];
255            
256 1     1   71 no strict 'refs';
  1         2  
  1         215  
257            
258 0           foreach my $pkg ( $class.'::' )
259             {
260 0           foreach ( sort keys %{$pkg} )
  0            
261             {
262 0 0         unless( /::$/ )
263             {
264 0 0         if( defined *{ "$pkg$_" }{CODE} )
  0            
265             {
266 0 0         if( my $type = attributes::get( \&{ "$pkg$_" } ) )
  0            
267             {
268 0 0         push @$methods, "$_" if $type =~ /method/i;
269             }
270             }
271             }
272             }
273             }
274            
275 0           return $methods;
276             }
277            
278             sub find
279             {
280 0     0     my %request = @_;
281            
282 0           my @result;
283            
284             # parsing all references in a package (via symbol table)
285            
286 0           while( my ( $where, $what ) = each %request )
287             {
288 1     1   6 no strict 'refs';
  1         1  
  1         464  
289            
290 0           foreach my $pkg ( $where.'::' )
291             {
292 0 0         print $Class::Maker::TRACE "Searching in package '$where' for '$what' instances\n" if $DEBUG;
293            
294 0           foreach ( sort keys %{$pkg} )
  0            
295             {
296 0 0         unless( /::$/ )
297             {
298 0 0         if( defined *{ "$pkg$_" } )
  0            
299             {
300 0           my $sref = \${ "$pkg$_" };
  0            
301            
302 0 0         if( ref( $sref ) eq 'REF' )
303             {
304 0           my $type = ref( $$sref );
305            
306 0 0 0       printf $Class::Maker::TRACE "%20s %10s %s isa($what)\n", '$'.$_, $type if $$sref->isa( $what ) and $DEBUG;
307            
308 0           push @result, $$sref;
309             }
310             }
311             }
312             }
313             }
314             }
315            
316 0           return \@result;
317             }
318            
319             # helpers
320            
321             sub _isa_tree
322             {
323 0     0     my $list = shift;
324            
325 0           my $level = shift;
326            
327 0           for my $child ( @_ )
328             {
329 0           my @parents = Class::Maker::Reflection::_get_isa( $child );
330            
331 0           $level++;
332            
333 0           push @{ $list->{$level} }, $child;
  0            
334            
335 0 0         warn sprintf "\@%s::ISA = qw(%s);",$child , join( ' ', @parents ) if $Class::Maker::DEBUG;
336            
337 0           _isa_tree( $list, $level, @parents );
338            
339 0           $level--;
340             }
341             }
342            
343             # returns the isa tree sorted by level of recursion
344            
345             # 5 -> Exporter
346             # 4 -> Object::Debugable
347             # 3 -> Person, Exporter
348             # 2 -> Employee, Exporter, Object::Debugable
349             # 1 -> Doctor
350            
351             sub isa_tree
352             {
353 0     0     my $list = {};
354            
355 0           _isa_tree( $list, 0, @_ );
356            
357 0           return $list;
358             }
359            
360             # returns the isa tree in a planar list (for con-/destructor queue's)
361            
362             sub inheritance_isa
363             {
364 0 0   0     warn sprintf "SCANNING ISA FOR (%s);", join( ', ', @_ ) if $Class::Maker::DEBUG;
365            
366 0           my $construct_list = isa_tree( @_ );
367            
368 0           my @ALL;
369            
370 0           foreach my $level ( sort { $b <=> $a } keys %$construct_list )
  0            
371             {
372 0           push @ALL, @{ $construct_list->{$level} };
  0            
373             }
374            
375 0           return \@ALL;
376             }
377            
378             1;
379            
380             __END__