File Coverage

lib/Pony/Object.pm
Criterion Covered Total %
statement 376 388 96.9
branch 103 118 87.2
condition 42 55 76.3
subroutine 54 56 96.4
pod 0 18 0.0
total 575 635 90.5


line stmt bran cond sub pod time code
1             package Pony::Object {
2             # "I am 100% sure that we're not completely sure"
3              
4 8     8   117386 use feature ':5.10';
  8         11  
  8         712  
5 8     8   4305 use Storable qw/dclone/;
  8         18063  
  8         441  
6 8     8   3370 use Module::Load;
  8         5844  
  8         38  
7 8     8   311 use Carp qw(confess);
  8         10  
  8         394  
8 8     8   30 use Scalar::Util qw(refaddr);
  8         9  
  8         539  
9              
10 8     8   29 use constant DEBUG => 0;
  8         9  
  8         793  
11              
12             BEGIN {
13 8     8   15443 if (DEBUG) {
14             say STDERR "\n[!] Pony::Object DEBUGing mode is turning on!\n";
15            
16             *{dumper} = sub {
17 8     8   3970 use Data::Dumper;
  8         50515  
  8         624  
18             $Data::Dumper::Indent = 1;
19             say Dumper(@_);
20             say '=' x 79;
21             }
22             }
23             }
24              
25             our $VERSION = "1.01";
26              
27             # Var: $DEFAULT
28             # Use it to redefine default Pony's options.
29             our $DEFAULT = {
30             '' => {
31             'withExceptions' => 0,
32             'baseClass' => [],
33             }
34             };
35              
36             # Function: import
37             # This function will runs on each use of this module.
38             # It changes caller - adds new keywords,
39             # makes caller more strict and modern,
40             # create from simple package almost normal class.
41             # Also it provides some useful methods.
42             #
43             # Don't forget: it's still OOP with blessed refs,
44             # but now it looks better - more sugar for your code.
45             sub import {
46 53     53   10773 my $this = shift;
47 53         76 my $call = caller;
48            
49             # Modify caller just once.
50             # We suppose, that only we can create function ALL.
51 53 100       48 return if defined *{$call.'::ALL'};
  53         270  
52            
53             # Parse parameters.
54 52         1774 my $default = dclone $DEFAULT;
55 52         52 my $profile;
56            
57             # Get predefined params.
58 52         139 for my $prefix (sort {length $b <=> length $a} keys %$DEFAULT) {
  3         8  
59 55 100       299 if ($call =~ /^$prefix/) {
60             my @doesnt_exist = grep {
61 105         227 not exists $profile->{$_}
62 53         38 } keys %{ $default->{$prefix} };
  53         122  
63            
64 53         161 $profile->{$_} = $default->{$prefix}->{$_} for @doesnt_exist;
65 53         95 next;
66             }
67            
68 2 50       2 last if keys %{$default->{''}} == keys %{$default->{$call}};
  2         4  
  2         6  
69             }
70            
71 52         54 $profile->{isAbstract} = 0; # don't do default object abstract.
72 52         45 $profile->{isSingleton} = 0; # don't do default object singleton.
73 52         89 $profile = parseParams($call, $profile, @_);
74            
75             # Keywords, base methods, attributes.
76 52         63 predefine($call, $profile);
77            
78             # Pony objects must be strict and modern.
79 52         254 strict ->import;
80 52         358 warnings->import;
81 52         2028 feature ->import(':5.10');
82 52 50       364 feature ->import('signatures') if $] >= 5.020;
83            
84 52 100       1203 unless ($profile->{noObject}) {
85             # Base classes and params.
86 51         113 prepareClass($call, "${call}::ISA", $profile);
87            
88 51         82 methodsInheritance($call);
89 51         74 propertiesInheritance($call);
90            
91 51     52   132 *{$call.'::new'} = sub { importNew($call, @_) };
  51         4116  
  52         6167  
92             }
93             }
94              
95             # Function: importNew
96             # Constructor for Pony::Objects.
97             #
98             # Parameters:
99             # $call - Str - caller package.
100             #
101             # Returns:
102             # self
103             sub importNew {
104 52     52 0 54 my $call = shift;
105            
106 52 100       114 if ($call->META->{isAbstract}) {
107 2         277 confess "Trying to use an abstract class $call";
108             } else {
109 50         107 $call->AFTER_LOAD_CHECK;
110             }
111            
112             # For singletons.
113 50 100       54 return ${$call.'::instance'} if defined ${$call.'::instance'};
  1         4  
  50         232  
114            
115 49         53 my $this = shift;
116 49         32 my $obj = dclone { %{${this}.'::ALL'} };
  49         1183  
117            
118 49         86 while (my ($k, $p) = each %{$this->META->{properties}}) {
  182         217  
119 133 100       101 if (grep {$_ eq 'static'} @{$p->{access}}) {
  140         330  
  133         195  
120             tie $obj->{$k}, 'Pony::Object::TieStatic',
121 7   66     10 $call->META->{static}, $k, $call->META->{static}->{$k} || $obj->{$k};
122             }
123             }
124            
125 49         82 $this = bless $obj, $this;
126            
127 49 100       59 ${$call.'::instance'} = $this if $call->META->{isSingleton};
  1         4  
128            
129             # 'After hook' for user.
130 49 100       269 $this->init(@_) if $call->can('init');
131 49         97 return $this;
132             }
133              
134             # Function: parseParams
135             # Load all base classes and read class params.
136             #
137             # Parameters:
138             # $call - Str - caller package.
139             # $profile - HashRef - profile of this use.
140             # @params - Array - import params.
141             #
142             # Returns:
143             # HashRef - $profile
144             sub parseParams {
145 52     52 0 69 my ($call, $profile, @params) = @_;
146            
147 52         53 for my $param (@params) {
148            
149             # Define singleton class.
150 41 100 100     288 if ($param =~ /^-?singleton$/) {
    100 100        
    100          
    100          
    100          
151 1         2 $profile->{isSingleton} = 1;
152 1         1 next;
153             }
154            
155             # Define abstract class.
156             elsif ($param =~ /^-?abstract$/) {
157 5         6 $profile->{isAbstract} = 1;
158 5         6 next;
159             }
160            
161             # Features:
162            
163             # Use exceptions featureset.
164             elsif ($param =~ /^:exceptions?$/ || $param =~ /^:try$/) {
165 3         3 $profile->{withExceptions} = 1;
166 3         4 next;
167             }
168            
169             # Don't use exceptions featureset.
170             elsif ($param =~ /^:noexceptions?$/ || $param =~ /^:notry$/) {
171 2         1 $profile->{withExceptions} = 0;
172 2         3 next;
173             }
174            
175             # Don't create an object.
176             # Just make package strict modern and add some staff.
177             elsif ($param =~ /^:noobject$/) {
178 1         1 $profile->{noObject} = 1;
179 1         1 next;
180             }
181            
182             # Base classes:
183            
184             # Save class' base classes.
185             else {
186 29         19 push @{$profile->{baseClass}}, $param;
  29         51  
187             }
188             }
189            
190 52         80 return $profile;
191             }
192              
193             # Function: prepareClass
194             # Load all base classes and process class params.
195             #
196             # Parameters:
197             # $call - Str - caller package.
198             # $isaRef - ArrayRef - ref to @ISA.
199             # $profile - HashRef - parsed params profile.
200             sub prepareClass {
201 51     51 0 55 my ($call, $isaRef, $profile) = @_;
202              
203 51   50     190 $call->META->{isSingleton} = $profile->{isSingleton} // 0;
204 51   50     106 $call->META->{isAbstract} = $profile->{isAbstract} // 0;
205              
206 51         38 for my $base (@{ $profile->{baseClass} }) {
  51         90  
207 31 100       49 next if $call eq $base;
208 30         61 load $base;
209 30 50       1444 $base->AFTER_LOAD_CHECK if $base->can('AFTER_LOAD_CHECK');
210 30         234 push @$isaRef, $base;
211             }
212             }
213              
214             # Function: predefine
215             # Predefine keywords and base methods.
216             #
217             # Parameters:
218             # $call - Str - caller package.
219             # $profile - HashRef
220             sub predefine {
221 52     52 0 49 my ($call, $profile) = @_;
222            
223             # Only for objects.
224 52 100       83 unless ($profile->{noObject}) {
225             # Predefine ALL and META.
226 51         40 %{$call.'::ALL' } = ();
  51         178  
227 51         34 %{$call.'::META'} = ();
  51         123  
228 51         33 ${$call.'::META'}{isSingleton}= 0;
  51         89  
229 51         38 ${$call.'::META'}{isAbstract} = 0;
  51         69  
230 51         52 ${$call.'::META'}{abstracts} = [];
  51         71  
231 51         45 ${$call.'::META'}{methods} = {};
  51         72  
232 51         33 ${$call.'::META'}{properties} = {};
  51         66  
233 51         41 ${$call.'::META'}{symcache} = {};
  51         62  
234 51         37 ${$call.'::META'}{checked} = 0;
  51         69  
235 51         31 ${$call.'::META'}{static} = {};
  51         102  
236            
237             # Access for properties.
238 51     17   128 *{$call.'::has'} = sub { addProperty ($call, @_) };
  51         141  
  17         186  
239 51     2   80 *{$call.'::static'} = sub { addStatic ($call, @_) };
  51         120  
  2         76  
240 51     6   75 *{$call.'::public'} = sub { addPublic ($call, @_) };
  51         122  
  6         89  
241 51     8   71 *{$call.'::private'} = sub { addPrivate ($call, @_) };
  51         124  
  8         109  
242 51     44   92 *{$call.'::protected'}= sub { addProtected($call, @_) };
  51         144  
  44         732  
243            
244             # Convert object's data into hash.
245             # Uses ALL() to get properties' list.
246 51         121 *{$call.'::toHash'} = *{$call.'::to_h'} = sub {
  51         103  
247 2     2   539 my $this = shift;
248 2         3 my %hash = map { $_, $this->{$_} } keys %{ $this->ALL() };
  4         10  
  2         5  
249 2         5 return \%hash;
250 51         143 };
251            
252 51     80   76 *{$call.'::AFTER_LOAD_CHECK'} = sub { checkImplementations($call) };
  51         103  
  80         127  
253            
254             # Save method's attributes.
255 51         141 *{$call.'::MODIFY_CODE_ATTRIBUTES'} = sub {
256 57     57   5679 my ($pkg, $ref, @attrs) = @_;
257 57         82 my $sym = findsym($pkg, $ref);
258            
259 57         75 $call->META->{methods}->{ *{$sym}{NAME} } = {
260 57         172 attributes => \@attrs,
261             package => $pkg
262             };
263            
264 57         68 for my $attr (@attrs) {
265 57 100       96 if ($attr eq 'Public' ) { makePublic ($pkg, $sym, $ref) }
  45 100       52  
    100          
    50          
266 3         4 elsif ($attr eq 'Protected') { makeProtected($pkg, $sym, $ref) }
267 3         7 elsif ($attr eq 'Private' ) { makePrivate ($pkg, $sym, $ref) }
268 6         10 elsif ($attr eq 'Abstract' ) { makeAbstract ($pkg, $sym, $ref) }
269             }
270 57         119 return;
271 51         106 };
272            
273             # Getters for REFs to special variables %ALL and %META.
274 51     41   74 *{$call.'::ALL'} = sub { \%{ $call.'::ALL' } };
  51         70  
  41         800  
  41         122  
275 51     1317   76 *{$call.'::META'} = sub { \%{ $call.'::META'} };
  51         79  
  1317         789  
  1317         3159  
276             }
277            
278             # Try, Catch, Finally.
279             # Define them if user wants.
280 52 100       771 if ($profile->{withExceptions}) {
281 6         12 *{$call.'::try'} = sub (&;@) {
282 22     22   1109 my($try, $catch, $finally) = @_;
283 22         20 local $@;
284            
285             # If some one wanna to get some
286             # values from try/catch/finally blocks.
287 22 100       42 if (defined wantarray) {
288 12 100       27 if (wantarray == 0) {
    50          
289 8         8 my $ret = eval{ $try->() };
  8         14  
290 8 100 100     75 $ret = $catch->($@) if $@ && defined $catch;
291 8 100       30 $ret = $finally->() if defined $finally;
292 8         27 return $ret;
293             }
294             elsif (wantarray == 1) {
295 4         5 my @ret = eval{ $try->() };
  4         9  
296 4 100 100     37 @ret = $catch->($@) if $@ && defined $catch;
297 4 100       12 @ret = $finally->() if defined $finally;
298 4         11 return @ret;
299             }
300             }
301             else {
302 10         13 eval{ $try->() };
  10         20  
303 10 100 100     90 $catch->($@) if $@ && defined $catch;
304 9 100       1708 $finally->() if defined $finally;
305             }
306 6         16 };
307 6     17   10 *{$call.'::catch'} = sub (&;@) { @_ };
  6         15  
  17         2406  
308 6     5   7 *{$call.'::finally'} = sub (&) { @_ };
  6         12  
  5         567  
309             }
310            
311             # This method provides deep copy
312             # for Pony::Objects
313 52     2   78 *{$call.'::clone'} = sub { dclone shift };
  52         138  
  2         78  
314            
315             # Simple Data::Dumper wrapper.
316 52         116 *{$call.'::dump'} = sub {
317 8     8   42 use Data::Dumper;
  8         1319  
  8         9591  
318 1     1   1037 $Data::Dumper::Indent = 1;
319 1         8 Dumper(@_);
320 52         75 };
321             }
322              
323             # Function: methodsInheritance
324             # Inheritance of methods.
325             #
326             # Parameters:
327             # $this - Str - caller package.
328             sub methodsInheritance {
329 51     51 0 41 my $this = shift;
330            
331 51         28 for my $base ( @{$this.'::ISA'} ) {
  51         154  
332             # All Pony-like classes.
333 30 50       80 if ($base->can('META')) {
334 30         35 my $methods = $base->META->{methods};
335            
336 30         69 while (my($k, $v) = each %$methods) {
337             $this->META->{methods}->{$k} = $v
338 51 100       72 unless exists $this->META->{methods}->{$k};
339             }
340            
341             # Abstract classes.
342 30 100       39 if ($base->META->{isAbstract}) {
343 7         10 my $abstracts = $base->META->{abstracts};
344 7         7 push @{ $this->META->{abstracts} }, @$abstracts;
  7         8  
345             }
346             }
347             }
348             }
349              
350             # Function: checkImplementations
351             # Check for implementing abstract methods
352             # in our class in non-abstract classes.
353             #
354             # Parameters:
355             # $this - Str - caller package.
356             sub checkImplementations {
357 80     80 0 69 my $this = shift;
358            
359 80 100       92 return if $this->META->{checked};
360 42         54 $this->META->{checked} = 1;
361            
362             # Check: does all abstract methods implemented.
363 42         34 for my $base (@{$this.'::ISA'}) {
  42         110  
364 29 100 33     168 if ( $base->can('META') && $base->META->{isAbstract} ) {
365 7         10 my $methods = $base->META->{abstracts};
366 7         6 my @bad;
367            
368             # Find Abstract methods,
369             # which was not implements.
370 7         12 for my $method (@$methods) {
371             # Get Abstract methods.
372             push @bad, $method
373 11         31 if grep { $_ eq 'Abstract' }
374 11 50       11 @{ $base->META->{methods}->{$method}->{attributes} };
  11         14  
375            
376             # Get abstract methods,
377             # which doesn't implement.
378 11         9 @bad = grep { !exists $this->META->{methods}->{$_} } @bad;
  11         15  
379             }
380            
381 7 50       17 if (@bad) {
382             my @messages = map
383 0         0 {"Didn't find method ${this}::$_() defined in $base."}
  0         0  
384             @bad;
385 0         0 push @messages, "You should implement abstract methods before.\n";
386 0         0 confess join("\n", @messages);
387             }
388             }
389             }
390             }
391              
392             # Function: addProperty
393             # Guessing access type of property.
394             #
395             # Parameters:
396             # $this - Str - caller package.
397             # $attr - Str - name of property.
398             # $value - Mixed - default value of property.
399             sub addProperty {
400 17     17 0 21 my ($this, $attr, $value) = @_;
401            
402             # Properties
403 17 100       27 if (ref $value ne 'CODE') {
404 9 50       20 if ($attr =~ /^__/) {
    50          
405 0         0 return addPrivate(@_);
406             } elsif ($attr =~ /^_/) {
407 0         0 return addProtected(@_);
408             } else {
409 9         12 return addPublic(@_);
410             }
411             }
412            
413             # Methods
414             else {
415 8         4 *{$this."::$attr"} = $value;
  8         28  
416 8         9 my $sym = findsym($this, $value);
417 8         16 my @attrs = qw/Public/;
418            
419 8 100       19 if ($attr =~ /^__/) {
    100          
420 2         3 @attrs = qw/Private/;
421 2         3 return makePrivate($this, $sym, $value);
422             } elsif ($attr =~ /^_/) {
423 1         1 @attrs = qw/Protected/;
424 1         2 return makeProtected($this, $sym, $value);
425             } else {
426 5         7 return makePublic($this, $sym, $value);
427             }
428            
429 0         0 $this->META->{methods}->{ *{$sym}{NAME} } = {
430 0         0 attributes => \@attrs,
431             package => $this
432             };
433             }
434             }
435              
436             # Function: addStatic
437             # Add static property or make property static.
438             #
439             # Parameters:
440             # $call - Str - caller package.
441             # $name - Str - property's name.
442             # $value - Mixed - default value.
443             #
444             # Returns:
445             # $name - Str - property's name.
446             # $value - Mixed - default value.
447             sub addStatic {
448 2     2 0 2 my $call = shift;
449 2         2 my ($name, $value) = @_;
450 2         2 push @{ $call->META->{statics} }, $name;
  2         3  
451 2         4 addPropertyToMeta('static', $call, @_);
452 2         4 return @_;
453             }
454              
455             # Function: addPropertyToMeta
456             # Save property's info into META
457             #
458             # Parameters:
459             # $access - Str - property's access type.
460             # $call - Str - caller package.
461             # $name - Str - property's name.
462             # $value - Mixed - property's default value.
463             sub addPropertyToMeta {
464 69     69 0 58 my $access = shift;
465 69         40 my $call = shift;
466 69         51 my ($name, $value) = @_;
467            
468 69         84 my $props = $call->META->{properties};
469            
470             # Delete inhieritated properties for polymorphism.
471             delete $call->META->{properties}->{$name} if
472             exists $call->META->{properties}->{$name} &&
473 69 100 100     70 $call->META->{properties}->{$name}->{package} ne $call;
474            
475             # Create if doesn't exist
476             %$props = (%$props, $name => {access => []}) if
477             not exists $props->{$name} ||
478 69 50 33     427 ( $props->{$name}->{package} && $props->{$name}->{package} ne $call );
      66        
479            
480 69         81 push @{$props->{$name}->{access}}, $access;
  69         99  
481 69         95 $props->{$name}->{package} = $call;
482             }
483              
484             # Function: addPublic
485             # Create public property with accessor.
486             # Save it in special variable ALL.
487             #
488             # Parameters:
489             # $call - Str - caller package.
490             # $name - Str - name of property.
491             # $value - Mixed - default value of property.
492             sub addPublic {
493 15     15 0 11 my $call = shift;
494 15         12 my ($name, $value) = @_;
495 15         18 addPropertyToMeta('public', $call, @_);
496            
497             # Save pair (property name => default value)
498 15         11 %{ $call.'::ALL' } = ( %{ $call.'::ALL' }, $name => $value );
  15         35  
  15         27  
499 15     102   32 *{$call."::$name"} = sub : lvalue { my $call = shift; $call->{$name} };
  15         40  
  102         4280  
  102         314  
500 15         28 return @_;
501             }
502              
503             # Function: addProtected
504             # Create protected property with accessor.
505             # Save it in special variable ALL.
506             # Can die on wrong access attempt.
507             #
508             # Parameters:
509             # $pkg - Str - caller package.
510             # $name - Str - name of property.
511             # $value - Mixed - default value of property.
512             sub addProtected {
513 44     44 0 42 my $pkg = shift;
514 44         41 my ($name, $value) = @_;
515 44         46 addPropertyToMeta('protected', $pkg, @_);
516            
517             # Save pair (property name => default value)
518 44         26 %{$pkg.'::ALL'} = (%{$pkg.'::ALL'}, $name => $value);
  44         107  
  44         93  
519            
520 44         113 *{$pkg."::$name"} = sub : lvalue {
521 281     281   1173 my $this = shift;
522 281         196 my $call = caller;
523 281 100 100     1414 confess "Protected ${pkg}::$name called"
      66        
524             unless ($call->isa($pkg) || $pkg->isa($call)) and $this->isa($pkg);
525 279         1070 $this->{$name};
526 44         116 };
527 44         62 return @_;
528             }
529              
530             # Function: addPrivate
531             # Create private property with accessor.
532             # Save it in special variable ALL.
533             # Can die on wrong access attempt.
534             #
535             # Parameters:
536             # $pkg - Str - caller package.
537             # $name - Str - name of property.
538             # $value - Mixed - default value of property.
539             sub addPrivate {
540 8     8 0 9 my $pkg = shift;
541 8         7 my ($name, $value) = @_;
542 8         11 addPropertyToMeta('private', $pkg, @_);
543            
544             # Save pair (property name => default value)
545 8         6 %{ $pkg.'::ALL' } = ( %{ $pkg.'::ALL' }, $name => $value );
  8         28  
  8         16  
546            
547 8         21 *{$pkg."::$name"} = sub : lvalue {
548 7     7   639 my $this = shift;
549 7         9 my $call = caller;
550 7 100 66     122 confess "Private ${pkg}::$name called"
551             unless $pkg->isa($call) && $this->isa($pkg);
552 6         19 $this->{$name};
553 8         24 };
554 8         12 return @_;
555             }
556              
557             # Function: makeProtected
558             # Function's attribute.
559             # Uses to define, that this code can be used
560             # only inside this class and his childs.
561             #
562             # Parameters:
563             # $pkg - Str - name of package, where this function defined.
564             # $symbol - Symbol - reference to perl symbol.
565             # $ref - CodeRef - reference to function's code.
566             sub makeProtected {
567 4     4 0 5 my ($pkg, $symbol, $ref) = @_;
568 4         4 my $method = *{$symbol}{NAME};
  4         5  
569            
570 8     8   42 no warnings 'redefine';
  8         13  
  8         1200  
571            
572 4         17 *{$symbol} = sub {
573 19     19   1266 my $this = $_[0];
574 19         20 my $call = caller;
575 19 100 66     451 confess "Protected ${pkg}::$method() called"
      66        
576             unless ($call->isa($pkg) || $pkg->isa($call)) and $this->isa($pkg);
577 15         27 goto &$ref;
578             }
579 4         12 }
580              
581             # Function: makePrivate
582             # Function's attribute.
583             # Uses to define, that this code can be used
584             # only inside this class. NOT for his childs.
585             #
586             # Parameters:
587             # $pkg - Str - name of package, where this function defined.
588             # $symbol - Symbol - reference to perl symbol.
589             # $ref - CodeRef - reference to function's code.
590             sub makePrivate {
591 5     5 0 6 my ($pkg, $symbol, $ref) = @_;
592 5         5 my $method = *{$symbol}{NAME};
  5         7  
593            
594 8     8   32 no warnings 'redefine';
  8         7  
  8         1251  
595            
596 5         14 *{$symbol} = sub {
597 9     9   1463 my $this = $_[0];
598 9         10 my $call = caller;
599 9 100 66     413 confess "Private ${pkg}::$method() called"
600             unless $pkg->isa($call) && $this->isa($pkg);
601 6         15 goto &$ref;
602             }
603 5         19 }
604              
605             # Function: makePublic
606             # Function's attribute.
607             # Uses to define, that this code can be used public.
608             #
609             # Parameters:
610             # $pkg - Str - name of package, where this function defined.
611             # $symbol - Symbol - reference to perl symbol.
612             # $ref - CodeRef - reference to function's code.
613       50 0   sub makePublic {
614             # do nothing
615             }
616              
617             # Function: makeAbstract
618             # Function's attribute.
619             # Define abstract attribute.
620             # It means, that it doesn't conteins realisation,
621             # but none abstract class, which will extends it,
622             # MUST implement it.
623             #
624             # Parameters:
625             # $pkg - Str - name of package, where this function defined.
626             # $symbol - Symbol - reference to perl symbol.
627             # $ref - CodeRef - reference to function's code.
628             sub makeAbstract {
629 6     6 0 6 my ($pkg, $symbol, $ref) = @_;
630 6         4 my $method = *{$symbol}{NAME};
  6         6  
631            
632             # Can't define abstract method
633             # in none-abstract class.
634             confess "Abstract ${pkg}::$method() defined in non-abstract class"
635 6 50       8 unless $pkg->META->{isAbstract};
636            
637             # Push abstract method
638             # into object meta.
639 6         5 push @{ $pkg->META->{abstracts} }, $method;
  6         6  
640            
641 8     8   27 no warnings 'redefine';
  8         9  
  8         3970  
642            
643             # Can't call abstract method.
644 6     0   18 *{$symbol} = sub { confess "Abstract ${pkg}::$method() called" };
  6         15  
  0         0  
645             }
646              
647             # Function: propertiesInheritance
648             # This function calls when we need to get
649             # properties (with thier default values)
650             # form classes which our class extends to our class.
651             #
652             # Parameters:
653             # $this - Str - caller package.
654             sub propertiesInheritance {
655 51     51 0 33 my $this = shift;
656 51         41 my %classes;
657 51         34 my @classes = @{ $this.'::ISA' };
  51         101  
658 51         50 my @base;
659             my %props;
660            
661             # Get all parent's properties
662 51         81 while (@classes) {
663 42         39 my $c = pop @classes;
664 42 100       68 next if exists $classes{$c};
665 36         65 %classes = (%classes, $c => 1);
666 36         35 push @base, $c;
667 36         25 push @classes, @{$c.'::ISA'};
  36         81  
668             }
669            
670 51         77 for my $base (reverse @base) {
671 36 50       91 if ($base->can('ALL')) {
672             # Default values
673 36         48 my $all = $base->ALL();
674 36         64 for my $k (keys %$all) {
675 77 100       47 unless (exists ${$this.'::ALL'}{$k}) {
  77         148  
676 56         32 %{$this.'::ALL'} = (%{$this.'::ALL'}, $k => $all->{$k});
  56         133  
  56         105  
677             }
678             }
679             # Statics
680 36         49 $all = $base->META->{properties};
681 36         52 for my $k (keys %$all) {
682 77 100       72 unless (exists $this->META->{properties}->{$k}) {
683 56         50 %{$this->META->{properties}} = (%{$this->META->{properties}},
  56         52  
684 56         36 $k => $base->META->{properties}->{$k});
685             }
686             }
687             }
688             }
689             }
690              
691             # Function: findsym
692             # Get perl symbol by ref.
693             #
694             # Parameters:
695             # $pkg - Str - package, where it defines.
696             # $ref - CodeRef - reference to method.
697             #
698             # Returns:
699             # Symbol
700             sub findsym {
701 65     65 0 45 my ($pkg, $ref) = @_;
702 65         90 my $symcache = $pkg->META->{symcache};
703            
704 65 50       194 return $symcache->{$pkg, $ref} if $symcache->{$pkg, $ref};
705            
706 65         46 my $type = 'CODE';
707            
708 65         50 for my $sym (values %{$pkg."::"}) {
  65         163  
709 623 50       762 next unless ref ( \$sym ) eq 'GLOB';
710            
711             return $symcache->{$pkg, $ref} = \$sym
712 623 100 100     355 if *{$sym}{$type} && *{$sym}{$type} == $ref;
  623         1148  
  552         1771  
713             }
714             }
715             }
716              
717              
718             ###############################################################################
719             # Class: Pony::Object::TieStatic
720             # Tie class. Use for make properties are static.
721             package Pony::Object::TieStatic {
722             # "When you see me again, it won't be me"
723              
724             # Method: TIESCALAR
725             # tie constructor
726             #
727             # Parameters:
728             # $storage - HashRef - data storage
729             # $name - Str - property's name
730             # $val - Mixed - Init value
731             #
732             # Returns:
733             # Pony::Object::TieStatic
734             sub TIESCALAR {
735 7     7   22 my $class = shift;
736 7         10 my ($storage, $name, $val) = @_;
737 7 100       13 $storage->{$name} = $val unless exists $storage->{$name};
738              
739 7         27 bless {name => $name, storage => $storage}, $class;
740             }
741              
742             # Method: FETCH
743             # Defines fetch for scalar.
744             #
745             # Returns:
746             # Mixed - property's value
747             sub FETCH {
748 42     42   27 my $self = shift;
749 42         71 return $self->{storage}->{ $self->{name} };
750             }
751              
752             # Method: STORE
753             # Defines store for scalar.
754             #
755             # Parameters:
756             # $val - Mixed - property's value
757             sub STORE {
758 0     0     my $self = shift;
759 0           my $val = shift;
760 0           $self->{storage}->{ $self->{name} } = $val;
761             }
762             }
763              
764             1;
765              
766             __END__