File Coverage

blib/lib/Fukurama/Class/Tree.pm
Criterion Covered Total %
statement 108 108 100.0
branch 21 26 80.7
condition 6 9 66.6
subroutine 21 21 100.0
pod 7 7 100.0
total 163 171 95.3


line stmt bran cond sub pod time code
1             package Fukurama::Class::Tree;
2 8     8   22968 use Fukurama::Class::Version(0.03);
  8         17  
  8         48  
3 8     8   46 use Fukurama::Class::Rigid;
  8         21  
  8         40  
4 8     8   48 use Fukurama::Class::Carp;
  8         14  
  8         61  
5              
6             my $CHECK = {};
7             my $BUILD = {};
8             my $IS_BUILD = 0;
9             my $EXEC_ONCE = {};
10             my $CLASSTREE = {};
11              
12             my $FORBID_SUB_TYPES = ['system', 'tie', 'thread'];
13             my $FORBIDDEN_SUBS = {
14             system => {
15             import => 1,
16             unimport => 1,
17             can => 1,
18             isa => 1,
19             VERSION => 1,
20             BEGIN => 1,
21             UNITCHECK => 1,
22             CHECK => 1,
23             INIT => 1,
24             END => 1,
25             DESTROY => 1,
26             AUTOLOAD => 1,
27             MODIFY_CODE_ATTRIBUTES => 1,
28             MODIFY_SCALAR_ATTRIBUTES => 1,
29             MODIFY_ARRAY_ATTRIBUTES => 1,
30             MODIFY_HASH_ATTRIBUTES => 1,
31             MODIFY_GLOB_ATTRIBUTES => 1,
32             FETCH_CODE_ATTRIBUTES => 1,
33             FETCH_SCALAR_ATTRIBUTES => 1,
34             FETCH_ARRAY_ATTRIBUTES => 1,
35             FETCH_HASH_ATTRIBUTES => 1,
36             FETCH_GLOB_ATTRIBUTES => 1,
37             },
38             thread => {
39             CLONE => 1,
40             CLONE_SKIP => 1,
41             },
42             tie => {
43             TIESCALAR => 1,
44             FETCH => 1,
45             STORE => 1,
46             UNTIE => 1,
47             TIEARRAY => 1,
48             FETCHSIZE => 1,
49             STORESIZE => 1,
50             EXTEND => 1,
51             EXISTS => 1,
52             DELETE => 1,
53             CLEAR => 1,
54             PUSH => 1,
55             POP => 1,
56             SHIFT => 1,
57             UNSHIFT => 1,
58             SPLICE => 1,
59             TIEHASH => 1,
60             FIRSTKEY => 1,
61             NEXTKEY => 1,
62             SCALAR => 1,
63             TIEHANDLE => 1,
64             WRITE => 1,
65             PRINT => 1,
66             PRINTF => 1,
67             READ => 1,
68             READLINE => 1,
69             GETC => 1,
70             CLOSE => 1,
71             },
72             };
73             =head1 NAME
74              
75             Fukurama::Class::Tree - Helper-class to register class-handler
76              
77             =head1 VERSION
78              
79             Version 0.03 (beta)
80              
81             =head1 SYNOPSIS
82              
83             my $BUILD_HANDLER = sub {
84             my $classname = $_[0];
85             my $classdef = $_[1];
86            
87             no strict 'refs';
88            
89             $classdef->{'implements'} = \@{$classname . '::INTERFACES'};
90             return;
91             };
92             my $CHECK_HANDLER = sub {
93             my $classname = $_[0];
94             my $classdef = $_[1];
95            
96             my $paths = $classdef->{'implements'};
97             return if(ref($paths) ne 'ARRAY');
98             # Do what ever you want (for interfaces, see Fukurama::Class::Implements)
99             # ...
100             return;
101             };
102             Fukurama::Class::Tree->register_build_handler($BUILD_HANDLER);
103             Fukurama::Class::Tree->register_check_handler($CHECK_HANDLER);
104              
105             =head1 DESCRIPTION
106              
107             This module register class-definitions, read the inheritation-trees and execute checks to the registered class-defintions.
108             You can register handler to create you own class defintions and handler to check something at this classes.
109             It's a central helper class for most of Fukurama::Class - modules.
110              
111             =head1 CONFIG
112              
113             -
114            
115             =head1 EXPORT
116              
117             -
118              
119             =head1 METHODS
120              
121             =over 4
122              
123             =item get_all_subs( class:STRING ) return:STRING()
124              
125             Get all methods from the given class.
126              
127             =item get_class_subs( class:STRING ) return:STRING[]
128              
129             Get all methods for the given class. It omit all special-methods. See is_special_sub().
130              
131             =item get_inheritation_path( class:STRING ) return:[ STRING[] ]
132              
133             Return all inheritation class-paths for the given class.
134             For example, if a class B (multiple-)inherit from B and B, it will return these two inheritation-class-paths.
135             If the given class doesn't use any multi inheritation, you will get an arrayref with one classpath and these classpath will be
136             an array of all parents and grandparents etc. the given class.
137              
138             =item is_special_sub( subname:STRING ) return:BOOLEAN
139              
140             Check, if the given subroutine(-name) is from an special method which is used perl "magically".
141             For example it returns true for I, I, I etc.
142              
143             =item register_build_handler( handler:CODE ) return:VOID
144              
145             Register a handler subroutine to build your own class-defintion. For example you can implement an own syntax to define
146             interface-implementations. The build-handler takes two parameters: the name and the definition-hash (which you can extend)
147             for each loaded class.
148              
149             =item register_check_handler( handler:CODE ) return:VOID
150              
151             Register a handler subroutine to check the classes. For example you can check an self-defined interface syntax. The check-handler
152             takes two parameters: the name and the definition-hash, which was build via bild-handler, for each loaded class.
153              
154             =item run_check() return:VOID
155              
156             Helper method for static perl (see Fukurama::Class > BUGS)
157             This method will find all loades classes, run all registered build-handler for each loaded class and, when this is finished,
158             it runs all registered check-handler (even for each loaded class).
159              
160             =back
161              
162             =head1 AUTHOR, BUGS, SUPPORT, ACKNOWLEDGEMENTS, COPYRIGHT & LICENSE
163              
164             see perldoc of L
165              
166             =cut
167              
168             # void
169             sub run_check {
170 45     45 1 97 my $class = $_[0];
171 45         78 my $type = $_[1];
172            
173 45 100       138 $type = 'MANUAL' if(!defined($type));
174            
175 45 100       7235 return if($EXEC_ONCE->{$type});
176 22         95 $class->_build();
177 22         139 $class->_check();
178 18         70 $EXEC_ONCE->{$type} = 1;
179 18         2580 return;
180             }
181             # void
182             sub register_build_handler {
183 34     34 1 209 my $class = $_[0];
184 34         51 my $handler = $_[1];
185            
186 34 50       108 _croak("Can only register subrefs as handler, not '$handler'") if(ref($handler) ne 'CODE');
187 34         142 $BUILD->{int($handler)} = $handler;
188 34         92 return;
189             }
190             # void
191             sub register_check_handler {
192 37     37 1 62 my $class = $_[0];
193 37         53 my $handler = $_[1];
194            
195 37 50       103 _croak("Can only register subrefs as handler, not '$handler'") if(ref($handler) ne 'CODE');
196 37         98 $CHECK->{int($handler)} = $handler;
197 37         150 return;
198             }
199             # void
200             sub _build {
201 22     22   46 my $class = $_[0];
202            
203 8     8   49 no warnings 'recursion';
  8         17  
  8         940  
204            
205 22         56 $CLASSTREE = {};
206 22         1906 $class->_read_class('', $CLASSTREE);
207 22         60 $IS_BUILD = 1;
208            
209 22         63 return;
210             }
211             # void
212             sub _read_class {
213 4375     4375   6366 my $class = $_[0];
214 4375         5296 my $parent_class = $_[1];
215 4375         4800 my $classtree = $_[2];
216            
217 8     8   56 no strict 'refs';
  8         21  
  8         3369  
218            
219 4375         4690 foreach my $child_class (keys %{$parent_class . '::'}) {
  4375         36382  
220 78285         138504 my $classname = ($parent_class . '::' . $child_class);
221 78285         115349 $classname =~ s/^(?:::)(?:main|)//;
222 78285         103646 $classname =~ s/::$//;
223 78285 100 100     300598 next if(!UNIVERSAL::isa($classname, $classname) || $classname =~ m/[^a-zA-Z0-9_:]/);
224 4377 100       10637 next if($classtree->{$classname});
225            
226 4353         11633 $classtree->{$classname} = {};
227 4353         9170 foreach my $build_handler (values(%$BUILD)) {
228 5287         15174 &$build_handler($classname, $classtree->{$classname});
229             }
230 4353         14883 $class->_read_class($classname, $classtree);
231             }
232 4375         17702 return;
233             }
234             # void
235             sub _check {
236 22     22   49 my $class = $_[0];
237            
238 22 50       84 _croak("Can't check classtree without build!") if(!$IS_BUILD);
239 22         1312 foreach my $class (keys(%$CLASSTREE)) {
240 4149         9913 foreach my $check_handler (values(%$CHECK)) {
241 6596         16659 &$check_handler($class, $CLASSTREE->{$class});
242             }
243             }
244 18         384 return;
245             }
246             # string()
247             sub get_class_subs {
248 95     95 1 3392 my $class = $_[0];
249 95         123 my $used_class = $_[1];
250            
251 95         240 return grep { !$class->is_special_sub($_) } $class->get_all_subs($used_class);
  286         635  
252             }
253             # string ()
254             sub get_all_subs {
255 258     258 1 333 my $class = $_[0];
256 258         420 my $used_class = $_[1];
257            
258 8     8   48 no strict 'refs';
  8         13  
  8         1664  
259            
260 258         406 my $subs = {};
261 258         499 foreach my $glob (%{$used_class . '::'}) {
  258         1062  
262 3476 100 33     17030 next if((ref($glob) && ref($glob) ne 'GLOB') || !*$glob{'CODE'});
      66        
263 700         1895 $subs->{*$glob{'NAME'}} = undef;
264             }
265 258         1313 return keys(%$subs);
266             }
267             # boolean
268             sub is_special_sub {
269 495     495 1 1407 my $class = $_[0];
270 495         575 my $subname = $_[1];
271            
272 495         731 foreach my $type (@$FORBID_SUB_TYPES) {
273 1257 100       3745 return 1 if($FORBIDDEN_SUBS->{$type}->{$subname});
274             }
275 381         1399 return 0;
276             }
277             # void
278             sub _get_inheritation_path {
279 4579     4579   5391 my $class = $_[0];
280 4579         5119 my $child = $_[1];
281 4579         5033 my $child_path = $_[2];
282 4579         4756 my $all_path_routes = $_[3];
283              
284 8     8   43 no strict 'refs';
  8         21  
  8         1563  
285            
286 4579         4957 my $parents = \@{$child . '::ISA'};
  4579         14903  
287 4579 100       10326 if(!scalar(@$parents)) {
288 2383 100       6560 push(@$all_path_routes, [@$child_path]) if(scalar(@$child_path));
289 2383         6553 return;
290             }
291            
292 2196         3714 foreach my $parent (@$parents) {
293 2456         3647 my $class_allways_in_path = grep({ $_ eq $parent } @$child_path);
  4793         8885  
294 2456 50       6858 next if($class_allways_in_path);
295 2456         8679 $class->_get_inheritation_path($parent, [@$child_path, $parent], $all_path_routes);
296             }
297 2196         5743 return;
298             }
299             # array[]
300             sub get_inheritation_path {
301 2123     2123 1 3678 my $class = $_[0];
302 2123         2380 my $child_class = $_[1];
303            
304 2123 50       3840 return [] if(!$child_class);
305 2123         2926 my $all_path_routes = [];
306 2123         5495 $class->_get_inheritation_path($child_class, [], $all_path_routes);
307 2123         5929 return $all_path_routes;
308             }
309              
310 8     8   50 no warnings 'void'; # avoid 'Too late to run CHECK/INIT block'
  8         13  
  8         780  
311              
312             # AUTOMAGIC void
313             CHECK {
314 8     8   4844 __PACKAGE__->run_check('CHECK');
315             }
316             # AUTOMAGIC void
317             END {
318 8     8   3069 __PACKAGE__->run_check('END');
319             }
320             1;