File Coverage

blib/lib/POOF.pm
Criterion Covered Total %
statement 241 411 58.6
branch 45 172 26.1
condition 8 33 24.2
subroutine 59 85 69.4
pod 21 28 75.0
total 374 729 51.3


line stmt bran cond sub pod time code
1             package POOF;
2              
3 6     6   649 use 5.007;
  6         17  
  6         293  
4 7     5   457 use strict;
  5         9  
  5         121  
5 7     5   41 use warnings;
  5         558  
  5         89  
6              
7 5     5   24 use B::Deparse;
  5         18  
  5         701  
8 5     3   5947 use Attribute::Handlers;
  5         40595  
  5         34  
9 3     3   161 use Scalar::Util qw(blessed refaddr);
  3         7  
  3         415  
10 3     3   24 use Carp qw(croak confess cluck);
  3         8  
  3         208  
11 3     3   3828 use Class::ISA;
  3         10139  
  3         93  
12              
13              
14 3     3   2011 use POOF::Properties;
  3         10  
  3         113  
15 3     3   27 use POOF::DataType;
  3         8  
  3         181  
16              
17             our $VERSION = '1.4';
18             our $TRACE = 0;
19             our $RAISE_EXCEPTION = 'trap';
20              
21              
22             #-------------------------------------------------------------------------------
23 3     3   17 use constant PROPERTIES => { };
  3         8  
  3         371  
24 3     3   19 use constant PROPERTYINDEX => { };
  3         5  
  3         142  
25 3     3   18 use constant METHODS => { };
  3         7  
  3         175  
26 3     3   16 use constant GROUPS => { };
  3         5  
  3         164  
27 3     3   17 use constant PROPBACKREF => { };
  3         7  
  3         237  
28 3     3   16 use constant PROPBACKDOOR => { };
  3         7  
  3         140  
29 3     3   17 use constant CLASSES => { };
  3         4  
  3         128  
30 3     3   16 use constant METHODDISPATCH => { };
  3         6  
  3         240  
31 3     3   17 use constant ENCFQCLASSNAMES => { };
  3         7  
  3         151  
32 3     3   17 use constant PROCESSEDFILES => { };
  3         7  
  3         417  
33              
34              
35             #-------------------------------------------------------------------------------
36             # access levels
37 3         21891 use constant ACCESSLEVEL =>
38             {
39             'Private' => 0,
40             'Protected' => 1,
41             'Public' => 2,
42 3     3   18 };
  3         5  
43              
44             #-------------------------------------------------------------------------------
45             sub new
46             {
47 5     5 1 1198 my $class = shift;
48 5         13 my %args = @_;
49            
50 5 50       16 confess "This class cannot be instantiated as a stand along object, it must be inherited instead"
51             if $class eq 'POOF';
52            
53             # define main constructor property definition array
54 5         17 my @properties = _processParentProperties($class,{});
55            
56             # deal with self
57 5         10 foreach my $property (@{ +PROPERTIES->{ $class } })
  5         12  
58             {
59 15 50       49 if (exists $property->{'name'})
60             {
61             # add to Properties.pm constructor args
62 15         77 push(@properties,{
63             'class' => $class,
64             'name' => $property->{'name'},
65             'access' => $property->{'data'}->{'access'},
66             'virtual' => $property->{'data'}->{'virtual'},
67             'data' => POOF::DataType->new($property->{'data'}),
68             'datadef' => $property->{'data'}
69             });
70             }
71             }
72            
73 5         10 my $obj;
74 5         6 tie %{$obj}, 'POOF::Properties', \@properties, $class, \&pErrors, \+GROUPS, \+PROPBACKREF, @_;
  5         36  
75 3         5 bless $obj,$class;
76            
77 3         6 $obj->{'___refobj___'} = $obj;
78            
79 3 50 33     9 $RAISE_EXCEPTION = $args{'RaiseException'}
80             if exists $args{'RaiseException'} && defined $args{'RaiseException'};
81            
82 3         23 $obj->_init( @_ );
83              
84 0         0 return $obj;
85             }
86              
87             sub _processParentProperties
88             {
89 10     5   24 my $class = shift;
90 10         48 my $seen = shift;
91 10         31 my @properties = @_;
92            
93             # deal with parents
94 10         45 foreach my $parent (reverse Class::ISA::super_path($class))
95             {
96 10 50       225 next if $seen->{$parent}++;
97            
98             # process it's parents first
99 5 100 33     17 @properties = _processParentProperties($parent,$seen,@properties)
100             if (exists +PROPERTIES->{ $parent } && $parent ne 'POOF');
101            
102             # skip any non-defined parent
103 5 100       17 next unless exists +PROPERTIES->{ $parent };
104            
105             # deal with each parent property
106 0         0 foreach my $property (@{ +PROPERTIES->{ $parent } })
  0         0  
107             {
108 0 50       0 if (exists $property->{'name'})
109             {
110             # add to Properties.pm constructor args
111 0         0 push(@properties,{
112             'class' => $parent,
113             'name' => $property->{'name'},
114             'access' => $property->{'data'}->{'access'},
115             'virtual' => $property->{'data'}->{'virtual'},
116             'data' => POOF::DataType->new($property->{'data'}),
117             'datadef' => $property->{'data'}
118             });
119             }
120             }
121             }
122            
123 5         14 return (@properties);
124             }
125              
126             sub _init
127             {
128 5     5   6 my $obj = shift;
129 5         11 my %args = @_;
130 5         13 return (@_);
131             }
132              
133              
134             #-------------------------------------------------------------------------------
135             # Error handling
136              
137             my $ERRORS;
138             sub pErrors
139             {
140 0     3 1 0 my $obj = shift;
141 0         0 my ($k,$e) = @_;
142            
143 0 0       0 $e->{'description'} = "$e->{'description'}"
144             if ref($e);
145            
146             return
147 0         0 @_ == 0
148 0 0       0 ? scalar keys %{$ERRORS->{ refaddr($obj) }}
    0          
    0          
149             : @_ == 1
150             ? delete $ERRORS->{ refaddr($obj) }->{ $k }
151             : @_ == 2
152             ? $obj->_AddError($k,$e)
153             : undef;
154             }
155              
156             sub pGetErrors
157             {
158 0     0 1 0 my $obj = shift;
159             return
160 0 0       0 ref $ERRORS->{ refaddr($obj) }
161             ? $ERRORS->{ refaddr($obj) }
162             : { };
163             }
164              
165             sub pAllErrors
166             {
167 0     0 0 0 my ($obj) = @_;
168 0         0 return scalar(keys %{$obj->pGetAllErrors});
  0         0  
169             }
170              
171             sub pGetAllErrors
172             {
173 0     0 1 0 my ($obj,$parent) = @_;
174 0         0 my $errors = {};
175              
176 0 0       0 $parent =
177             $parent
178             ? "$parent-"
179             : '';
180            
181 0 0       0 if ($obj->_Relationship(ref($obj),'POOF::Collection') =~ /^(?:self|child)$/)
182             {
183 0         0 for(my $i=0; $i<=$#{$obj}; $i++)
  0         0  
184             {
185             # skip non initialized elements of collection
186 0 0       0 next unless exists $obj->[$i];
187 0 0       0 if ($obj->_Relationship(ref($obj->[$i]),'POOF') =~ /^(?:self|child)$/)
188             {
189 0         0 my $error = $obj->[$i]->pGetAllErrors("$parent$i");
190 0 0       0 %{$errors} = (%{$errors},%{$error})
  0         0  
  0         0  
  0         0  
191             if $error;
192             }
193             }
194             }
195             else
196             {
197 0         0 foreach my $prop (@{+PROPERTIES->{ ref($obj) }})
  0         0  
198             {
199 0 0       0 if ($obj->_Relationship(ref($obj->{$prop->{'name'}}),'POOF') =~ /^(?:self|child)$/)
200             {
201 0         0 my $error = $obj->{$prop->{'name'}}->pGetAllErrors("$parent$prop->{'name'}");
202 0 0       0 %{$errors} = (%{$errors},%{$error})
  0         0  
  0         0  
  0         0  
203             if $error;
204             }
205             }
206             }
207            
208 0         0 my $myErrors = $obj->pGetErrors;
209 0         0 map { $errors->{"$parent$_"} = $myErrors->{$_} } keys %{$myErrors};
  0         0  
  0         0  
210 0         0 return $errors;
211             }
212              
213             sub _AddError
214             {
215 0     0   0 my ($obj,$k,$e) = @_;
216 0 0       0 unless ($RAISE_EXCEPTION eq 'trap')
217             {
218 0 0       0 my $error_string = "\nException for " . ref($obj) . "->{'$k'}\n" . "-"x50 . "\n"
219             . "\n\tcode = $e->{'code'}"
220             . "\n\tvalue = " . (defined $e->{'value'} ? $e->{'value'} : 'undef')
221             . "\n\tdescription = $e->{'description'}";
222            
223 0 0       0 if ($RAISE_EXCEPTION eq 'warn')
    0          
    0          
    0          
    0          
    0          
224             {
225 0         0 warn $error_string;
226             }
227             elsif($RAISE_EXCEPTION eq 'print')
228             {
229 0         0 print $error_string;
230             }
231             elsif($RAISE_EXCEPTION eq 'cluck')
232             {
233 0         0 cluck $error_string ."\n\tstack = ";
234             }
235             elsif($RAISE_EXCEPTION eq 'confess')
236             {
237 0         0 confess $error_string ."\n\tstack = ";
238             }
239             elsif($RAISE_EXCEPTION eq 'croak')
240             {
241 0         0 croak $error_string;
242             }
243             elsif($RAISE_EXCEPTION eq 'die')
244             {
245 0         0 die $error_string;
246             }
247             }
248            
249 0         0 return $ERRORS->{ refaddr($obj) }->{ $k } = $e;
250             }
251              
252             sub pRaiseException
253             {
254 0     0 0 0 my ($obj,$val) = @_;
255             return
256 0 0       0 defined $val
257             ? $RAISE_EXCEPTION = $val
258             : $RAISE_EXCEPTION;
259             }
260              
261             #-------------------------------------------------------------------------------
262             # Group operations
263              
264             sub pGetPropertiesOfGroups
265             {
266 0     0 1 0 my $obj = shift;
267 0         0 my %props;
268 0         0 @props{ $obj->pGetNamesOfGroup(@_) } = $obj->pGetValuesOfGroup(@_);
269 0         0 return (%props);
270             }
271              
272             sub pGetGroups
273             {
274 0     0 1 0 my ($obj) = @_;
275 0         0 return (keys %{ +GROUPS->{ ref $obj } });
  0         0  
276             }
277              
278             sub pGetNamesOfGroup
279             {
280 2     2 1 3 my ($obj,$group) = @_;
281            
282             return
283 2         10 defined $group && exists +GROUPS->{ ref $obj }->{ $group }
284 2 50 33     13 ? (@{ +GROUPS->{ ref $obj }->{ $group } })
285             : ();
286             }
287              
288             sub pGroup
289             {
290 2     2 1 3 my ($obj,$group) = @_;
291 2         8 return $obj->pGetNamesOfGroup($group);
292             }
293              
294             sub pGroupEncoded
295             {
296 0     0 1 0 my ($obj,$group) = @_;
297 0         0 return (map { $obj->_encodeFullyQualifyClassName . '-' . $_ } $obj->pGetNamesOfGroup($group));
  0         0  
298             }
299              
300             sub pPropertyNamesEncoded
301             {
302 0     0 1 0 my ($obj,$refObj,@names) = @_;
303 0         0 my $class = ref $refObj;
304 0         0 return (map { $obj->_encodeFullyQualifyClassName($refObj) . '-' . $_ } @names );
  0         0  
305             }
306              
307             sub pGetValuesOfGroup
308             {
309 0     0 1 0 my ($obj,$group) = @_;
310             return
311 0         0 defined $group && $obj->pGetNamesOfGroup($group)
312 0 0 0     0 ? (@{$obj}{ $obj->pGetNamesOfGroup($group) })
313             : ();
314             }
315              
316             sub pValidGroupName
317             {
318 0 0   0 1 0 my $obj = ref $_[0] ? +shift : undef;
319 0         0 my ($name) = @_;
320             return
321 0 0       0 $name !~ /^\s*$/
322             ? 1
323             : 0;
324             }
325              
326             #-------------------------------------------------------------------------------
327              
328              
329             sub pSetPropertyDeeply
330             {
331 0     0 1 0 my ($obj,$ref,$val,@path) = @_;
332 0         0 my $level = shift @path;
333              
334 0 0       0 if (@path)
335             {
336             # look ahead to see if this is a collection
337 0 0 0     0 if (ref($ref->{$level}) && $obj->_Relationship($ref->{$level},'POOF::Collection') =~ /^(?:self|child)$/o )
338             {
339             # it's a collection
340 0         0 $obj->pSetPropertyDeeply($ref->{$level}->[ shift @path ],$val,@path);
341             }
342             else
343             {
344             # no it's not
345 0         0 $obj->pSetPropertyDeeply($ref->{$level},$val,@path)
346             }
347             }
348             else
349             {
350 0         0 $ref->{$level} = $val;
351             }
352             }
353              
354             sub pGetPropertyDeeply
355             {
356 0     0 1 0 my ($obj,$ref,@path) = @_;
357 0         0 my $level = shift @path;
358             return
359 0 0       0 scalar (@path)
    0          
    0          
360             ? ref($ref) eq 'ARRAY'
361             ? $obj->pGetPropertyDeeply($ref->[$level],@path)
362             : $obj->pGetPropertyDeeply($ref->{$level},@path)
363             : ref($ref) eq 'ARRAY'
364             ? $ref->[$level]
365             : $ref->{$level};
366             }
367              
368             sub pInstantiate
369             {
370 0     0 0 0 my ($obj,$prop) = @_;
371             return
372 0         0 $obj->pPropertyDefinition($prop)->{'otype'}->new
373             (
374             $obj->pGetPropertiesOfGroups('Application'),
375             RaiseException => $POOF::RAISE_EXCEPTION
376             );
377             }
378              
379             sub pReInstantiateSelf
380             {
381 0     0 0 0 my ($obj,%args) = @_;
382             return
383 0         0 ref($obj)->new(
384             $obj->pGetPropertiesOfGroups('Application'),
385             %args
386             );
387             }
388              
389             #-------------------------------------------------------------------------------
390             # property definitions
391              
392             sub pPropertyEnumOptions
393             {
394 0     0 1 0 my ($obj,$propName) = @_;
395 0 0       0 confess "There are no properties associated with " . ref($obj)
396             unless exists +PROPBACKREF->{ ref($obj) };
397 0         0 return +PROPBACKREF->{ ref($obj) }->EnumOptions($propName);
398             }
399              
400             sub pPropertyDefinition
401             {
402 0     0 1 0 my ($obj,$propName) = @_;
403 0 0       0 confess "There are no properties associated with " . ref($obj)
404             unless exists +PROPBACKREF->{ ref($obj) };
405            
406 0         0 return +PROPBACKREF->{ ref($obj) }->Definition($propName);
407             }
408              
409             #-------------------------------------------------------------------------------
410             our $AUTOLOAD;
411             sub AUTOLOAD
412             {
413 0     0   0 my $obj = shift;
414            
415 0         0 my $name = $AUTOLOAD;
416 0         0 $name =~ s/.*://; # strip fully-qualified portion
417            
418 0 0       0 my $super =
419             $AUTOLOAD =~ /\:SUPER\:/o
420             ? 1
421             : 0;
422            
423 0   0     0 my $class = ref($obj) || confess "$obj is not an object";
424              
425             # TDB: handle super correctly, if the parent does not have the method
426             # then try his parent and so on until we hit the top, if no method
427             # is found then throw and exeption.
428 0         0 my $package =
429             $super
430 0 0       0 ? shift @{[ Class::ISA::super_path( $class ) ]}
431             : $class;
432            
433             # just return undef if we are dealing with built in methods like DESTROY
434 0 0       0 return if $name eq 'DESTROY';
435              
436 0 0       0 if ($TRACE)
437             {
438 3     3   214 no warnings;
  3         21  
  3         4655  
439 0         0 warn qq|$AUTOLOAD for ($package) called from | . (caller(0))[0] . "\n";
440 0         0 warn qq|$AUTOLOAD for ($package) called from | . (caller(1))[0] . "\n";
441 0         0 warn qq|$AUTOLOAD for ($package) called from | . (caller(2))[0] . "\n";
442 0         0 warn qq|$AUTOLOAD for ($package) called from | . (caller(3))[0] . "\n";
443 0         0 warn qq|$AUTOLOAD for ($package) called from | . (caller(4))[0] . "\n";
444 0         0 warn "\twith " . scalar(@_) . " parameters\n";
445             }
446            
447            
448             # make sure we apply the inheritance rules the first time a class is used.
449 0 0       0 $obj->_BuildMethodDispatch( $package )
450             unless exists +METHODDISPATCH->{ $package };
451            
452 0 0 0     0 confess "$name method does not exist in class $package"
453             unless (
454             exists +METHODDISPATCH->{ $package }->{ $name }
455             and exists +METHODDISPATCH->{ $package }->{ $name }->{'code'}
456             );
457            
458 0         0 my $method = +METHODDISPATCH->{ $package }->{ $name }->{'code'};
459 0         0 my $access = +METHODDISPATCH->{ $package }->{ $name }->{'access'};
460            
461 0 0       0 $access =
462             exists ACCESSLEVEL->{ $access }
463             ? ACCESSLEVEL->{ $access }
464             : ACCESSLEVEL->{ 'Public' };
465            
466 0         0 my $context = $obj->_AccessContext;
467              
468 0 0       0 confess "Illegal access of method $name"
469             unless $access >= $context;
470            
471 0         0 return &{$method}($obj,@_);
  0         0  
472             }
473              
474              
475             sub _BuildMethodDispatch
476             {
477 0     0   0 my $obj = shift;
478 0         0 my $package = shift;
479            
480             # get all parents
481 0         0 my @parents = Class::ISA::super_path($package);
482            
483             # go through each class on the chain
484 0         0 foreach my $parent (reverse @parents)
485             {
486             # non-defined parent will simply get and empty hash
487             # and we'll skip to the next parent
488 0 0       0 unless (exists +METHODS->{ $parent })
489             {
490 0         0 +METHODDISPATCH->{ $parent } = { };
491 0         0 next;
492             }
493            
494             # deal with each parent methods
495 0         0 foreach my $name (keys %{ +METHODS->{ $parent } })
  0         0  
496             {
497 0         0 my $method = +METHODS->{ $parent }->{ $name };
498             # skip any private property since they are not accessible
499             # from this context, they are only accessible from the class in
500             # which they are defined.
501 0 0       0 next if $method->{'access'} eq 'Private';
502            
503             # croak if a method is redefined and it's not marked at virtual
504 0 0 0     0 confess "A non-virtual $name has been redefined in $parent"
505             if (exists +METHODDISPATCH->{ $package }->{ $name }
506             and +METHODDISPATCH->{ $package }->{ $name }->{'virtual'} != 1);
507            
508             # add method to dispatch table
509 0         0 +METHODDISPATCH->{ $package }->{ $name } = $method;
510             }
511             }
512            
513             # deal with each method in this package
514 0         0 foreach my $name (keys %{ +METHODS->{ $package } })
  0         0  
515             {
516 0         0 my $method = +METHODS->{ $package }->{ $name };
517            
518             # croak if a method is redefined and it's not marked at virtual
519 0 0 0     0 confess "A non-virtual $name has been redefined in $package"
520             if (exists +METHODDISPATCH->{ $package }->{ $name }
521             and +METHODDISPATCH->{ $package }->{ $name }->{'virtual'} != 1);
522            
523             # add method to dispatch table
524 0         0 +METHODDISPATCH->{ $package }->{ $name } = $method;
525             }
526             }
527              
528              
529             sub _AccessContext
530             {
531 0     0   0 my ($obj) = @_;
532 0         0 my $self = ref($obj);
533            
534 0         0 my ($caller) = (caller(1))[0];
535            
536 0         0 my $relationship = $obj->_Relationship($caller,$self);
537            
538             return
539 0 0       0 $relationship eq 'self'
    0          
    0          
540             ? 0 # 'private'
541             : $relationship eq 'child'
542             ? 1 # 'protected'
543             : $relationship eq 'parent'
544             ? 1 # 'protected' This is wierd shit, but I'm too tired now to fix it.
545             : 2 # 'public';
546             }
547              
548             sub _CallerContext
549             {
550 0     0   0 my ($obj) = @_;
551 0 0       0 $obj->Trace if $TRACE;
552 0         0 return (caller(1))[0];
553             }
554              
555             sub _Relationship
556             {
557 0     0   0 my $obj = shift;
558 0 0       0 my ($class1,$class2) = map { $_ ? ref $_ ? ref $_ : $_ : '' } @_;
  0 0       0  
559              
560 0 0       0 return 'self' if $class1 eq $class2;
561              
562 0         0 my %family1 = map { $_ => 1 } Class::ISA::super_path( $class1 );
  0         0  
563 0         0 my %family2 = map { $_ => 1 } Class::ISA::super_path( $class2 );
  0         0  
564              
565             return
566 0 0       0 exists $family1{ $class2 }
    0          
567             ? 'child'
568             : exists $family2{ $class1 }
569             ? 'parent'
570             : 'unrelated';
571             }
572              
573              
574             sub _DumpAccessContext
575             {
576 0     0   0 my $obj = shift;
577 0         0 my %caller;
578              
579 0         0 for(2 .. 5)
580             {
581 0         0 @caller{ qw(
582             0-package
583             1-filename
584             2-line
585             3-subr
586             4-has_args
587             5-wantarray
588             6-evaltext
589             7-is_required
590             8-hints
591             9-bitmask
592             ) } = caller($_);
593              
594 0 0       0 last unless defined $caller{'0-package'};
595            
596 0         0 warn "\ncaller $_\n" . "-"x50 . "\n";
597 0         0 $obj->_DumpCaller(\%caller);
598             }
599             }
600              
601             sub _DumpCore
602             {
603 0     0   0 my ($obj) = @_;
604            
605             #warn "Dumping Core\n";
606             #warn "-"x50 . "\n";
607             #warn "METHODS: ",Dumper( +METHODDISPATCH), "\n";
608             #warn "PROPERTYINDEX: ",Dumper( +PROPERTYINDEX), "\n";
609             #warn "PROPERTIES: ",Dumper( +PROPERTIES), "\n";
610             }
611              
612              
613             #-------------------------------------------------------------------------------
614             # function attribute handlers
615              
616 3     3 1 26 sub Method : ATTR(CODE,BEGIN) { _processFile(@_) }
  3     6   6  
  3         33  
  6         790  
617 3     3 0 2024 sub Property : ATTR(CODE,BEGIN) { _processFile(@_) }
  3     14   8  
  3         19  
  14         2330  
618 3     3 1 1082 sub Private : ATTR(CODE,BEGIN) { _processFile(@_) }
  3     2   8  
  3         98  
  2         109  
619 3     3 1 1429 sub Protected : ATTR(CODE,BEGIN) { _processFile(@_) }
  3     0   6  
  3         14  
  0         0  
620 3     3 1 1729 sub Public : ATTR(CODE,BEGIN) { _processFile(@_) }
  3     17   7  
  3         13  
  17         944  
621 3     3 1 1464 sub Virtual : ATTR(CODE,BEGIN) { _processFile(@_) }
  3     6   7  
  3         13  
  6         272  
622 3     3 0 1358 sub Doc : ATTR(CODE,BEGIN) { _processFile(@_) }
  3     0   9  
  3         13  
  0         0  
623              
624              
625             sub _processFile
626             {
627 45     45   91 my ($package, $symbol, $referent, $attr, $data, $phase) = @_;
628            
629 45 100       164 return if $package =~ /POOF::TEMPORARYNAMESPACE/;
630            
631             # convert package name to a path
632 22 50       30 my ($filename) = map { exists $INC{"$_.pm"} ? $INC{"$_.pm"} : $0 } map { s!::!/!go; $_ } ($package);
  22         98  
  22         77  
  22         53  
633            
634             # just return if we already processed this file
635 22 100       103 return if +PROCESSEDFILES->{$filename}++;
636              
637 3         6 my $source;
638             my $exception;
639            
640             # read source from file and untaint it
641 3 50       277 open(SOURCEFILE,$filename) || confess "Could not open $filename\n";
642             {
643 3         8 local $/ = undef;
  3         17  
644 3         186 =~ /(.*)/ms; # put untainted code in $1
645 3         22 $source = $1;
646             }
647 3         39 close(SOURCEFILE);
648            
649             # let's rename the packages so we don't brack perl's inheritance stuff
650 3         22 $source =~ s/^package\s+/package POOF::TEMPORARYNAMESPACE/g;
651            
652             # now let's evaluate the source using the same nasty string eval which is
653             # the reason we have to jump through hoops here (caramba!).
654             {
655             # creating block to squelch perl's complaining
656 3     3   4763 no strict 'refs';
  3         8  
  3         226  
  3         36  
657 3     3   17 no warnings 'redefine';
  3         7  
  3         2047  
658 3     3   47 eval $source;
  3     3   8  
  3     3   617  
  3     3   18  
  3     2   5  
  3     4   223  
  3         19  
  3         14  
  3         333  
  3         16  
  3         6  
  3         468  
  2         18  
  3         480  
  0         0  
  0         0  
659 3 100       220 if($@)
660             {
661 1         2 $exception = $@;
662 1         10 my ($error,$file) = split /\(eval \d+\)/, $exception;
663 1         5 my ($replace,$line) = split /\] line /, $file;
664 1 50       7 $exception = qq|$error [$filename]| . ($line ? " line $line" : $replace);
665 1         56 die $exception;
666             }
667             }
668            
669             # split source into packages but keep the keyword package in each piece;
670 2         22 my @packages = map { "package $_" } split(/^package\s+/,$source);
  4         19  
671            
672             # process each package one at a time
673 2         7 foreach my $package (@packages)
674             {
675 4 100       20 next unless $package =~ m/^package\s+([^\s]+)\s*;/;
676 2         5 my $tempclass = $1;
677 2         5 my $class = $tempclass;
678            
679 2         297 $class =~ s/POOF::TEMPORARYNAMESPACE//g;
680            
681             # identify all properties and methods by steping through each line one at a time
682 2         549 my @lines = split(/(?:\x0A|\x0D\x0A)/o,$package);
683 2         14 foreach (@lines)
684             {
685 191         352 s/#.*$//;
686 191 100       445 if(/\bsub\b\s*([^\s\{\(\:]+)\s*:\s*([^\{]+)\s*(\{|$)?/o)
687             {
688            
689 9         19 chomp();
690 9 50       40 my ($sub,$end) = ($1,$3 ? $3 : '');
691 9         30 my %attrs = map { $_ => 1 } map { _trim($_) } split(/\s+/,$2);
  21         51  
  21         98  
692            
693             # classify into property or method
694 9 100       38 if (exists $attrs{'Method'}) # process method
    50          
695             {
696             # determine access
697 3         11 my $access = _determineAccess(%attrs);
698             # determine virtual
699 3         9 my $virtual = _determineVirtual(%attrs);
700            
701             # creating block to squelch perl's complaining
702             {
703 3     3   22 no strict 'refs';
  3         69  
  3         105  
  3         98  
704 3     3   20 no warnings 'redefine';
  3         6  
  3         440  
705 3         4 +METHODS->{ $class }->{ $sub }->{'code'} = \&{$class . '::' . $sub};
  3         38  
706             }
707            
708             # handle access
709 3         1573 +METHODS->{ $class }->{ $sub }->{'access'} = $access;
710            
711             # handle virtual
712 3         15 +METHODS->{ $class }->{ $sub }->{'virtual'} = $virtual;
713            
714             ## handle documentation
715             #+METHODS->{ $class }->{ $sub }->{'doc'} = $doc;
716            
717             }
718             elsif(exists $attrs{'Property'}) # process property
719             {
720            
721             # determine access
722 6         27 my $access = _determineAccess(%attrs);
723             # determine virtual
724 6         18 my $virtual = _determineVirtual(%attrs);
725            
726 6         10 my $objdef;
727             # creating block to squelch perl's complaining
728             {
729 3     3   17 no strict 'refs';
  3         5  
  3         104  
  6         8  
730 3     3   15 no warnings 'redefine';
  3         6  
  3         1309  
731            
732 6         170 $objdef =
733 6         251 ref(&{$tempclass . '::' . $sub}) eq 'HASH'
734 0         0 ? &{$tempclass . '::' . $sub}
735 6 50       13 : { &{$tempclass . '::' . $sub} };
736             }
737             # this should return the hash that defines the property
738 6 50       18 %{$objdef} || confess "Properties must be defined by returning a hash ref with their attributes";
  6         20  
739            
740 6 50       21 unless (exists +PROPERTYINDEX->{ $class }->{ $sub })
741             {
742 6         13 push(@{ +PROPERTIES->{ $class } },{ 'name' => $sub });
  6         73  
743 6         10 +PROPERTYINDEX->{ $class }->{ $sub } = $#{ +PROPERTIES->{ $class } };
  6         23  
744            
745             # handle groups
746 6 100 66     40 if (exists $objdef->{'groups'} && ref($objdef->{'groups'}) eq 'ARRAY')
747             {
748 3         2 foreach my $group (@{$objdef->{'groups'}})
  3         9  
749             {
750             #confess "Invalid group name ($group} used in property $sub"
751             # unless ValidGroupName($group);
752             }
753             }
754            
755 6         11 +PROPERTIES->{ $class }->[ +PROPERTYINDEX->{ $class }->{ $sub } ]->{ 'data' } = { %{$objdef},access => $access, virtual => $virtual };
  6         63  
756             }
757             }
758             else
759             {
760             # just skip, they might be using a non POOF function attribute or a Doc attribute
761 0         0 next;
762             }
763             }
764            
765             }
766            
767             {
768 3     3   17 no strict 'refs';
  3         6  
  3         90  
  2         5  
769 3     3   14 no warnings 'redefine';
  3         11  
  3         2304  
770 2         141 my $table = eval '\\%' . $class . '::';
771 2         6 foreach my $item (keys %{$table})
  2         10  
772             {
773 20 100 100     172 if (exists +PROPERTYINDEX->{ $class }->{ $item } || exists +METHODS->{ $class }->{ $item })
774             {
775 5         9 *{ $table->{$item} } = undef;
  5         506  
776             }
777             }
778             }
779             }
780             }
781              
782             sub _determineAccess
783             {
784 9     11   20 my %attrs = @_;
785             # go from most secure to least secure
786             return
787 9 50       51 exists $attrs{'Private'}
    50          
    100          
788             ? 'Private'
789             : exists $attrs{'Protected'}
790             ? 'Protected'
791             : exists $attrs{'Public'}
792             ? 'Public'
793             : 'Protected'; # will default to procted if nothing has been specified
794             }
795              
796             sub _determineVirtual
797             {
798 9     11   20 my %attrs = @_;
799             # we make a distinction between properties and methods as they have different defaults
800             return
801 9 50       47 exists $attrs{'Property'}
    100          
    50          
    50          
    100          
802             ? exists $attrs{'Virtual'}
803             ? 1
804             : exists $attrs{'NonVirtual'}
805             ? 0
806             : 0 # Properties default to Virtual
807             : exists $attrs{'Method'}
808             ? exists $attrs{'Virtual'}
809             ? 1
810             : 0 # Methods default to NonVirtual
811             : 0;
812             }
813              
814             sub _trim
815             {
816 21     21   26 my ($dat) = @_;
817 21         71 $dat =~ s/^\s*//go;
818 21         97 $dat =~ s/\s*$//go;
819 21         49 return $dat;
820             }
821              
822             sub log2file
823             {
824 0 0   2 0   open(FH,">>/tmp/debug_log") || die "Could not open debug_log to write\n($!)\n";
825 0           print FH join(' ', @_) . "\n";
826 0           close(FH)
827             }
828              
829              
830              
831              
832             1;
833             __END__