File Coverage

blib/lib/Fukurama/Class/Attributes.pm
Criterion Covered Total %
statement 121 133 90.9
branch 10 16 62.5
condition 4 14 28.5
subroutine 21 24 87.5
pod 4 4 100.0
total 160 191 83.7


line stmt bran cond sub pod time code
1             package Fukurama::Class::Attributes;
2 4     4   28547 use Fukurama::Class::Version(0.01);
  4         9  
  4         27  
3 4     4   23 use Fukurama::Class::Rigid;
  4         6  
  4         25  
4 4     4   19 use Fukurama::Class::Carp;
  4         8  
  4         31  
5            
6 4     4   650 use Fukurama::Class::Tree();
  4         9  
  4         80  
7 4     4   2329 use Fukurama::Class::AttributesHandler();
  4         11  
  4         76  
8 4     4   2631 use Fukurama::Class::Attributes::OOStandard();
  4         15  
  4         129  
9 4     4   1046 use Fukurama::Class::Extends();
  4         12  
  4         75  
10 4     4   763 use Fukurama::Class::Implements();
  4         10  
  4         348  
11            
12             our $LEVEL_CHECK_NONE = 0;
13             our $LEVEL_CHECK_ALL = 1;
14            
15             our $CHECK_LEVEL = $LEVEL_CHECK_ALL;
16            
17             my $INIT;
18             my $REGISTERED_ATTRIBUTE_HANDLER;
19             my $REGISTERED_CLASSES;
20             my $INHERITATION_CHECK_SUBS;
21             my $INHERIT_METHOD_NAME;
22             BEGIN {
23 4     4   15 $REGISTERED_ATTRIBUTE_HANDLER = {
24             'Fukurama::Class::Attributes::OOStandard' => 0,
25             };
26 4         10 $REGISTERED_CLASSES = {};
27 4         9 $INHERITATION_CHECK_SUBS = {};
28 4         9671 $INHERIT_METHOD_NAME = 'check_inheritation';
29             }
30            
31             =head1 NAME
32            
33             Fukurama::Class::Attributes - Pragma like module to extend code attributes
34            
35             =head1 VERSION
36            
37             Version 0.01 (beta)
38            
39             =head1 SYNOPSIS
40            
41             package MyAttributeClass;
42             sub MyFirstCodeAttribute {
43             my $class = $_[0];
44             my $subroutine_data = $_[1];
45            
46             ... do some checks or what you want ...
47            
48             return;
49             }
50             sub check_inheritation {
51             my $class = $_[0];
52             my $method_name = $_[1];
53             my $parent_class = $_[2];
54             my $child_class = $_[3];
55             my $inheritation_type = $_[4];
56            
57             return;
58             }
59            
60             package MyClass;
61             BEGIN {
62             use Fukurama::Class::Attributes();
63             Fukurama::Class::Attributes->add_attribute_handler('MyAttributeClass');
64             }
65             use Fukurama::Class::Attributes;
66             sub my_method : Method(static|void|) {
67             return;
68             }
69             sub other_sub : MyFirstCodeAttribute() {
70             ...
71             }
72            
73             =head1 DESCRIPTION
74            
75             This pragma-like module provides functions to extend code attributes for yourself and check
76             the inheritation. It includes Fukurama::Attributes::OOStandard, which enables Method and Constructor
77             definitoins for subroutines. Use Fukurama::Class instead, to get all the features for OO.
78            
79             =head1 CONFIG
80            
81             You can disables all checks, which includes syntax and inheritation check by saying:
82            
83             $Fukurama::Class::Attributes::CHECK_LEVEL = $Fukurama::Class::Attributes::LEVEL_CHECK_NONE;
84            
85             =head1 EXPORT
86            
87             -
88            
89             =head1 METHODS
90            
91             =over 4
92            
93             =item add_attribute_handler( handler_class:CLASS ) return:BOOLEAN
94            
95             Add all defined attribute-methods of the given class, so you can use their code attributes in
96             all of your subroutines. See section L for the rules.
97            
98             =item remove_attribute_handler( handler_class:CLASS ) return:BOOLEAN
99            
100             Remove all defined attribute-methods of the given class, so you can't use their anymore as code attributes.
101            
102             =item register_class( export_to_class:CLASS ) return:BOOLEAN
103            
104             Export all code attributes to the given class so you can use all registered code attributes in there.
105             Every child of the given class can even use this behavior.
106            
107             =item run_check( ) return:VOID
108            
109             Helper method for static perl (see Fukurama::Class > BUGS)
110             Its calls Fukurama::Class::AttributeHandler->run_check(), which check the correct syntax of all registered
111             code attributes and check some defined conventions.
112            
113             E.g. for code attribute B: it will check that the access level of child class methods are the same or
114             stricter than the parent class method.
115            
116             =back
117            
118             =head1 CREATE AN OWN ATTRIBUTE CLASS
119            
120             An "attribute class" describe one (or many) code attributes, which you can use like B
121             There are the following rules for the class-methods:
122            
123             -Every Attribute has to start with an uppercase letter
124            
125             -Only on other method can be there, the subroutine B
126            
127             -All these methods have to be void
128            
129             Attribute methods take on parameter, a hash reference, which contain informations about the method, which uses
130             the actual code-attribute. For every subroutine which contains this code attribute, the corresponding
131             method in your attribute class would be called. The parameter contain the following data:
132            
133             resolved => BOOLEAN, # the subroutine is resolved (only internal use to avoid calls without the name of the subroutine)
134             data => STRING, # the attribute-data. If you say B it youd contain B
135             sub_name => STRING, # the name of the subroutine, which call this attribute
136             executed => BOOLEAN, # this attribute for this subroutine is allways called (only internal use to avoid double callings)
137             attribute => STRING, # the name of the attribute. Its the same like the name of your code-attribute-method.
138             handler => HASHREF, # Contain a reference to your code-attribute method and class. Only for internal use.
139             sub => CODEREF, # The code referense of the subroutine, which contain the code attribute.
140             class => STRING, # The class in which the subroutine is declared, which contain the code attribute.
141            
142             There are many things which you can do with code attributes, e.g. the Method and Constructor definitions from
143             Fukurama::Class::Attributes::OOStandard or some simple things like in Catalyst. So, do what you need.
144            
145             The check_inheritation() method is optional, check the code attribute inheritation for each class and take the following parameters:
146            
147             $method_name : STRING # the methodname which is checked
148             $parent_class : STRING # the parent class of the actual checked class
149             $child_class : STRING # the actual checked class
150             $inheritation_type : STRING # the type of inheritation. extend is standard, implement even exists
151            
152             With this method you can compare every subroutine, which contain a code attribute, with all parents. If you use multi
153             inheritation or interfaces there can be more than one parent. And it even compares in the same way all grandparents etc.
154            
155             =head1 AUTHOR, BUGS, SUPPORT, ACKNOWLEDGEMENTS, COPYRIGHT & LICENSE
156            
157             see perldoc of L
158            
159             =cut
160            
161             # STATIC boolean
162             sub add_attribute_handler {
163 0     0 1 0 my $class = $_[0];
164 0         0 my $handler_class = $_[1];
165            
166 0   0     0 $REGISTERED_ATTRIBUTE_HANDLER ||= {};
167 0 0       0 return 0 if(defined($REGISTERED_ATTRIBUTE_HANDLER->{$handler_class}));
168 0         0 $REGISTERED_ATTRIBUTE_HANDLER->{$handler_class} = 1;
169 0         0 return 1;
170             }
171             # STATIC boolean
172             sub remove_attribute_handler {
173 0     0 1 0 my $class = $_[0];
174 0         0 my $handler_class = $_[1];
175            
176 0 0 0     0 return 0 if(!$REGISTERED_ATTRIBUTE_HANDLER || !defined($REGISTERED_ATTRIBUTE_HANDLER->{$handler_class})
      0        
177             || $REGISTERED_ATTRIBUTE_HANDLER->{$handler_class});
178 0         0 delete($REGISTERED_ATTRIBUTE_HANDLER->{$handler_class});
179 0         0 return 1;
180             }
181             # AUTOMAGIC void
182             sub import {
183            
184 4     4   1224 my $caller_class = caller();
185 4         10 __PACKAGE__->register_class($caller_class);
186 4         146 return;
187             }
188             # STATIC void
189             sub register_class {
190 28     28 1 43 my $class = $_[0];
191 28         37 my $export_to_class = $_[1];
192            
193 28 100       65 if(!$INIT) {
194 3         49 $class->_init();
195 3         5 $INIT = 1;
196             }
197            
198 28         118 foreach my $handler_class (keys(%$REGISTERED_ATTRIBUTE_HANDLER)) {
199 28 100       122 next if($REGISTERED_ATTRIBUTE_HANDLER->{$handler_class});
200 3         16 Fukurama::Class::AttributesHandler->register_attributes($handler_class);
201            
202 4     4   44 no strict 'refs';
  4         9  
  4         4098  
203            
204 3         5 my $inheritation_sub = *{$handler_class . '::' . $INHERIT_METHOD_NAME}{'CODE'};
  3         13  
205 3         9 $INHERITATION_CHECK_SUBS->{$handler_class} = $inheritation_sub;
206 3         15 $REGISTERED_ATTRIBUTE_HANDLER->{$handler_class} = 1;
207             }
208            
209 28         75 $REGISTERED_CLASSES->{$export_to_class} = 1;
210 28         130 Fukurama::Class::AttributesHandler->export($export_to_class);
211 28         60 return;
212             }
213             # STATIC void
214             sub _init {
215 3     3   6 my $class = $_[0];
216            
217 3         28 Fukurama::Class::AttributesHandler->register_helper_method($INHERIT_METHOD_NAME);
218 3 50       15 return if($CHECK_LEVEL == $LEVEL_CHECK_NONE);
219            
220             my $CHECK_HANDLER = sub {
221 1601     1601   1809 my $classname = $_[0];
222 1601         1687 my $classdef = $_[1];
223            
224 1601 50       2910 return if($CHECK_LEVEL == $LEVEL_CHECK_NONE);
225 1601         3367 __PACKAGE__->_check_inheritation($classname, $classdef);
226 1599         4133 return;
227 3         18 };
228            
229 3         26 Fukurama::Class::Extends->register_class_tree();
230 3         22 Fukurama::Class::Implements->register_class_tree();
231 3         11 Fukurama::Class::Tree->register_check_handler($CHECK_HANDLER);
232 3         6 return;
233             }
234             # STATIC void
235             sub _check_inheritation {
236 1601     1601   1740 my $class = $_[0];
237 1601         1581 my $classname = $_[1];
238 1601         1693 my $classdef= $_[2];
239            
240 1601         1549 foreach my $inherit_data (@{$class->_get_parent_classes($classname, $classdef)}) {
  1601         3001  
241 751         1735 my $filtered_inherit_path = $class->_get_registered_parents($inherit_data->{'path'});
242 751 100       2174 next if(!scalar(@$filtered_inherit_path));
243 39         59 push(@$filtered_inherit_path, $classname);
244            
245 39         58 my $parent_path = [];
246 39         100 while(scalar(@$filtered_inherit_path) > 1) {
247 64         102 my $parent = shift(@$filtered_inherit_path);
248 64         126 my $child = $filtered_inherit_path->[0];
249            
250 64         185 $class->_merge_class_definition($parent, $child, $parent_path, $inherit_data->{'type'});
251 62         270 push(@$parent_path, $parent);
252             }
253             }
254 1599         3066 return;
255             }
256             # STATIC void
257             sub _merge_class_definition {
258 64     64   96 my $class = $_[0];
259 64         75 my $parent = $_[1];
260 64         78 my $child = $_[2];
261 64         68 my $parent_path = $_[3];
262 64         77 my $inheritation_type = $_[4];
263            
264 64         83 my $parent_methods = {};
265 64         129 foreach my $parent_class (@$parent_path, $parent) {
266 99         317 foreach my $parent_method (Fukurama::Class::Tree->get_all_subs($parent_class)) {
267 267         596 $parent_methods->{$parent_method} = $parent_class;
268             }
269             }
270            
271 64         91 my $child_methods = {};
272 64         231 foreach my $child_method (Fukurama::Class::Tree->get_all_subs($child)) {
273 147         303 $child_methods->{$child_method} = $child;
274             }
275            
276 64         269 my @all_methods = (keys(%$child_methods), keys(%$parent_methods));
277 64         124 my %unique_methods = ();
278 64         282 @unique_methods{@all_methods} = (1) x scalar(@all_methods);
279            
280 64         139 foreach my $method (keys(%unique_methods)) {
281 198   66     731 my $parent_class = $parent_methods->{$method} || $parent;
282 198         376 foreach my $handler_class (keys(%$INHERITATION_CHECK_SUBS)) {
283 198         217 &{$INHERITATION_CHECK_SUBS->{$handler_class}}($handler_class, $method, $parent_class, $child, $inheritation_type);
  198         1213  
284             }
285             }
286 62         338 return;
287             }
288             # STATIC string[]
289             sub _get_parent_classes {
290 1601     1601   1759 my $class = $_[0];
291 1601         1633 my $check_class = $_[1];
292 1601         1549 my $classdef = $_[2];
293            
294 1601         1928 my @all_paths = ();
295 1601         4178 foreach my $type (keys(%$classdef)) {
296 551         573 foreach my $inherit_path (@{$classdef->{$type}}) {
  551         1112  
297 751         3011 push(@all_paths, {
298             type => $type,
299             path => $inherit_path,
300             });
301             }
302             }
303 1601         4374 return \@all_paths;
304             }
305             # STATIC string[]
306             sub _get_registered_parents {
307 751     751   854 my $class = $_[0];
308 751         835 my $path = $_[1];
309            
310 751         911 my @filtered_path = ();
311 751         809 my $was_registered = 0;
312 751         1098 foreach my $parent (reverse(@$path)) {
313 2308 100 66     8211 next if(!$was_registered && !$REGISTERED_CLASSES->{$parent});
314 64         70 $was_registered = 1;
315 64         137 push(@filtered_path, $parent);
316             }
317 751         1649 return \@filtered_path;
318             }
319             # STATIC void
320             sub run_check {
321 0     0 1   Fukurama::Class::AttributesHandler->run_check();
322             }
323            
324 4     4   39 no warnings 'void'; # avoid 'Too late to run CHECK/INIT block'
  4         18  
  4         475  
325            
326             # AUTOMAGIC
327             CHECK {
328 4     4   4017 Fukurama::Class::AttributesHandler->run_check();
329             }
330             # AUTOMAGIC
331             END {
332 4     4   2986 Fukurama::Class::AttributesHandler->run_check();
333             }
334             1;