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   115360 use feature ':5.10';
  8         9  
  8         646  
5 8     8   4206 use Storable qw/dclone/;
  8         17622  
  8         552  
6 8     8   3705 use Module::Load;
  8         6086  
  8         38  
7 8     8   302 use Carp qw(confess);
  8         8  
  8         373  
8 8     8   37 use Scalar::Util qw(refaddr);
  8         10  
  8         527  
9              
10 8     8   31 use constant DEBUG => 0;
  8         9  
  8         783  
11              
12             BEGIN {
13 8     8   14835 if (DEBUG) {
14             say STDERR "\n[!] Pony::Object DEBUGing mode is turning on!\n";
15            
16             *{dumper} = sub {
17 8     8   4072 use Data::Dumper;
  8         50512  
  8         692  
18             $Data::Dumper::Indent = 1;
19             say Dumper(@_);
20             say '=' x 79;
21             }
22             }
23             }
24              
25             our $VERSION = "1.02";
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   10952 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       43 return if defined *{$call.'::ALL'};
  53         299  
52            
53             # Parse parameters.
54 52         1838 my $default = dclone $DEFAULT;
55 52         51 my $profile;
56            
57             # Get predefined params.
58 52         154 for my $prefix (sort {length $b <=> length $a} keys %$DEFAULT) {
  3         9  
59 55 100       314 if ($call =~ /^$prefix/) {
60             my @doesnt_exist = grep {
61 105         221 not exists $profile->{$_}
62 53         36 } keys %{ $default->{$prefix} };
  53         142  
63            
64 53         159 $profile->{$_} = $default->{$prefix}->{$_} for @doesnt_exist;
65 53         92 next;
66             }
67            
68 2 50       3 last if keys %{$default->{''}} == keys %{$default->{$call}};
  2         4  
  2         5  
69             }
70            
71 52         59 $profile->{isAbstract} = 0; # don't do default object abstract.
72 52         43 $profile->{isSingleton} = 0; # don't do default object singleton.
73 52         96 $profile = parseParams($call, $profile, @_);
74            
75             # Keywords, base methods, attributes.
76 52         66 predefine($call, $profile);
77            
78             # Pony objects must be strict and modern.
79 52         256 strict ->import;
80 52         363 warnings->import;
81 52         2084 feature ->import(':5.10');
82 52 50       364 feature ->import('signatures') if $] >= 5.020;
83            
84 52 100       1410 unless ($profile->{noObject}) {
85             # Base classes and params.
86 51         131 prepareClass($call, "${call}::ISA", $profile);
87            
88 51         78 methodsInheritance($call);
89 51         69 propertiesInheritance($call);
90            
91 51     52   134 *{$call.'::new'} = sub { importNew($call, @_) };
  51         4298  
  52         5751  
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 56 my $call = shift;
105            
106 52 100       106 if ($call->META->{isAbstract}) {
107 2         285 confess "Trying to use an abstract class $call";
108             } else {
109 50         100 $call->AFTER_LOAD_CHECK;
110             }
111            
112             # For singletons.
113 50 100       42 return ${$call.'::instance'} if defined ${$call.'::instance'};
  1         4  
  50         231  
114            
115 49         47 my $this = shift;
116 49         37 my $obj = dclone { %{${this}.'::ALL'} };
  49         1186  
117            
118 49         81 while (my ($k, $p) = each %{$this->META->{properties}}) {
  182         202  
119 133 100       87 if (grep {$_ eq 'static'} @{$p->{access}}) {
  140         322  
  133         165  
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         70 $this = bless $obj, $this;
126            
127 49 100       61 ${$call.'::instance'} = $this if $call->META->{isSingleton};
  1         4  
128            
129             # 'After hook' for user.
130 49 100       283 $this->init(@_) if $call->can('init');
131 49         100 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 81 my ($call, $profile, @params) = @_;
146            
147 52         54 for my $param (@params) {
148            
149             # Define singleton class.
150 41 100 100     307 if ($param =~ /^-?singleton$/) {
    100 100        
    100          
    100          
    100          
151 1         1 $profile->{isSingleton} = 1;
152 1         2 next;
153             }
154            
155             # Define abstract class.
156             elsif ($param =~ /^-?abstract$/) {
157 5         5 $profile->{isAbstract} = 1;
158 5         5 next;
159             }
160            
161             # Features:
162            
163             # Use exceptions featureset.
164             elsif ($param =~ /^:exceptions?$/ || $param =~ /^:try$/) {
165 3         3 $profile->{withExceptions} = 1;
166 3         3 next;
167             }
168            
169             # Don't use exceptions featureset.
170             elsif ($param =~ /^:noexceptions?$/ || $param =~ /^:notry$/) {
171 2         1 $profile->{withExceptions} = 0;
172 2         2 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         2 next;
180             }
181            
182             # Base classes:
183            
184             # Save class' base classes.
185             else {
186 29         31 push @{$profile->{baseClass}}, $param;
  29         52  
187             }
188             }
189            
190 52         76 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 69 my ($call, $isaRef, $profile) = @_;
202              
203 51   50     189 $call->META->{isSingleton} = $profile->{isSingleton} // 0;
204 51   50     109 $call->META->{isAbstract} = $profile->{isAbstract} // 0;
205              
206 51         43 for my $base (@{ $profile->{baseClass} }) {
  51         84  
207 31 100       56 next if $call eq $base;
208 30         64 load $base;
209 30 50       1515 $base->AFTER_LOAD_CHECK if $base->can('AFTER_LOAD_CHECK');
210 30         226 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 53 my ($call, $profile) = @_;
222            
223             # Only for objects.
224 52 100       101 unless ($profile->{noObject}) {
225             # Predefine ALL and META.
226 51         32 %{$call.'::ALL' } = ();
  51         188  
227 51         36 %{$call.'::META'} = ();
  51         117  
228 51         40 ${$call.'::META'}{isSingleton}= 0;
  51         91  
229 51         38 ${$call.'::META'}{isAbstract} = 0;
  51         73  
230 51         47 ${$call.'::META'}{abstracts} = [];
  51         84  
231 51         42 ${$call.'::META'}{methods} = {};
  51         71  
232 51         32 ${$call.'::META'}{properties} = {};
  51         69  
233 51         37 ${$call.'::META'}{symcache} = {};
  51         69  
234 51         32 ${$call.'::META'}{checked} = 0;
  51         66  
235 51         41 ${$call.'::META'}{static} = {};
  51         100  
236            
237             # Access for properties.
238 51     17   129 *{$call.'::has'} = sub { addProperty ($call, @_) };
  51         136  
  17         176  
239 51     2   87 *{$call.'::static'} = sub { addStatic ($call, @_) };
  51         121  
  2         80  
240 51     6   83 *{$call.'::public'} = sub { addPublic ($call, @_) };
  51         132  
  6         92  
241 51     8   69 *{$call.'::private'} = sub { addPrivate ($call, @_) };
  51         124  
  8         112  
242 51     44   74 *{$call.'::protected'}= sub { addProtected($call, @_) };
  51         150  
  44         777  
243            
244             # Convert object's data into hash.
245             # Uses ALL() to get properties' list.
246 51         125 *{$call.'::toHash'} = *{$call.'::to_h'} = sub {
  51         104  
247 2     2   639 my $this = shift;
248 2         3 my %hash = map { $_, $this->{$_} } keys %{ $this->ALL() };
  4         11  
  2         4  
249 2         5 return \%hash;
250 51         127 };
251            
252 51     80   74 *{$call.'::AFTER_LOAD_CHECK'} = sub { checkImplementations($call) };
  51         104  
  80         117  
253            
254             # Save method's attributes.
255 51         120 *{$call.'::MODIFY_CODE_ATTRIBUTES'} = sub {
256 57     57   5739 my ($pkg, $ref, @attrs) = @_;
257 57         81 my $sym = findsym($pkg, $ref);
258            
259 57         89 $call->META->{methods}->{ *{$sym}{NAME} } = {
260 57         192 attributes => \@attrs,
261             package => $pkg
262             };
263            
264 57         72 for my $attr (@attrs) {
265 57 100       93 if ($attr eq 'Public' ) { makePublic ($pkg, $sym, $ref) }
  45 100       66  
    100          
    50          
266 3         4 elsif ($attr eq 'Protected') { makeProtected($pkg, $sym, $ref) }
267 3         6 elsif ($attr eq 'Private' ) { makePrivate ($pkg, $sym, $ref) }
268 6         9 elsif ($attr eq 'Abstract' ) { makeAbstract ($pkg, $sym, $ref) }
269             }
270 57         119 return;
271 51         94 };
272            
273             # Getters for REFs to special variables %ALL and %META.
274 51     41   68 *{$call.'::ALL'} = sub { \%{ $call.'::ALL' } };
  51         73  
  41         936  
  41         104  
275 51     1317   78 *{$call.'::META'} = sub { \%{ $call.'::META'} };
  51         74  
  1317         760  
  1317         3154  
276             }
277            
278             # Try, Catch, Finally.
279             # Define them if user wants.
280 52 100       96 if ($profile->{withExceptions}) {
281 6         14 *{$call.'::try'} = sub (&;@) {
282 22     22   1182 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       41 if (defined wantarray) {
288 12 100       27 if (wantarray == 0) {
    50          
289 8         10 my $ret = eval{ $try->() };
  8         14  
290 8 100 100     78 $ret = $catch->($@) if $@ && defined $catch;
291 8 100       28 $ret = $finally->() if defined $finally;
292 8         28 return $ret;
293             }
294             elsif (wantarray == 1) {
295 4         6 my @ret = eval{ $try->() };
  4         6  
296 4 100 100     38 @ret = $catch->($@) if $@ && defined $catch;
297 4 100       10 @ret = $finally->() if defined $finally;
298 4         10 return @ret;
299             }
300             }
301             else {
302 10         11 eval{ $try->() };
  10         19  
303 10 100 100     88 $catch->($@) if $@ && defined $catch;
304 9 100       1682 $finally->() if defined $finally;
305             }
306 6         16 };
307 6     17   11 *{$call.'::catch'} = sub (&;@) { @_ };
  6         13  
  17         2421  
308 6     5   9 *{$call.'::finally'} = sub (&) { @_ };
  6         16  
  5         572  
309             }
310            
311             # This method provides deep copy
312             # for Pony::Objects
313 52     2   76 *{$call.'::clone'} = sub { dclone shift };
  52         140  
  2         76  
314            
315             # Simple Data::Dumper wrapper.
316 52         120 *{$call.'::dump'} = sub {
317 8     8   611 use Data::Dumper;
  8         11  
  8         10293  
318 1     1   1080 $Data::Dumper::Indent = 1;
319 1         7 Dumper(@_);
320 52         805 };
321             }
322              
323             # Function: methodsInheritance
324             # Inheritance of methods.
325             #
326             # Parameters:
327             # $this - Str - caller package.
328             sub methodsInheritance {
329 51     51 0 40 my $this = shift;
330            
331 51         35 for my $base ( @{$this.'::ISA'} ) {
  51         147  
332             # All Pony-like classes.
333 30 50       82 if ($base->can('META')) {
334 30         31 my $methods = $base->META->{methods};
335            
336 30         76 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         8 my $abstracts = $base->META->{abstracts};
344 7         5 push @{ $this->META->{abstracts} }, @$abstracts;
  7         7  
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 72 my $this = shift;
358            
359 80 100       110 return if $this->META->{checked};
360 42         64 $this->META->{checked} = 1;
361            
362             # Check: does all abstract methods implemented.
363 42         37 for my $base (@{$this.'::ISA'}) {
  42         112  
364 29 100 33     171 if ( $base->can('META') && $base->META->{isAbstract} ) {
365 7         10 my $methods = $base->META->{abstracts};
366 7         4 my @bad;
367            
368             # Find Abstract methods,
369             # which was not implements.
370 7         11 for my $method (@$methods) {
371             # Get Abstract methods.
372             push @bad, $method
373 11         35 if grep { $_ eq 'Abstract' }
374 11 50       7 @{ $base->META->{methods}->{$method}->{attributes} };
  11         14  
375            
376             # Get abstract methods,
377             # which doesn't implement.
378 11         10 @bad = grep { !exists $this->META->{methods}->{$_} } @bad;
  11         17  
379             }
380            
381 7 50       18 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 19 my ($this, $attr, $value) = @_;
401            
402             # Properties
403 17 100       28 if (ref $value ne 'CODE') {
404 9 50       17 if ($attr =~ /^__/) {
    50          
405 0         0 return addPrivate(@_);
406             } elsif ($attr =~ /^_/) {
407 0         0 return addProtected(@_);
408             } else {
409 9         10 return addPublic(@_);
410             }
411             }
412            
413             # Methods
414             else {
415 8         8 *{$this."::$attr"} = $value;
  8         24  
416 8         9 my $sym = findsym($this, $value);
417 8         15 my @attrs = qw/Public/;
418            
419 8 100       18 if ($attr =~ /^__/) {
    100          
420 2         2 @attrs = qw/Private/;
421 2         4 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         1 push @{ $call->META->{statics} }, $name;
  2         3  
451 2         4 addPropertyToMeta('static', $call, @_);
452 2         5 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 47 my $access = shift;
465 69         48 my $call = shift;
466 69         50 my ($name, $value) = @_;
467            
468 69         85 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     76 $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     457 ( $props->{$name}->{package} && $props->{$name}->{package} ne $call );
      66        
479            
480 69         84 push @{$props->{$name}->{access}}, $access;
  69         97  
481 69         103 $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 14 my $call = shift;
494 15         14 my ($name, $value) = @_;
495 15         27 addPropertyToMeta('public', $call, @_);
496            
497             # Save pair (property name => default value)
498 15         11 %{ $call.'::ALL' } = ( %{ $call.'::ALL' }, $name => $value );
  15         33  
  15         29  
499 15     102   30 *{$call."::$name"} = sub : lvalue { my $call = shift; $call->{$name} };
  15         41  
  102         4656  
  102         323  
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 46 my $pkg = shift;
514 44         39 my ($name, $value) = @_;
515 44         54 addPropertyToMeta('protected', $pkg, @_);
516            
517             # Save pair (property name => default value)
518 44         42 %{$pkg.'::ALL'} = (%{$pkg.'::ALL'}, $name => $value);
  44         113  
  44         79  
519            
520 44         118 *{$pkg."::$name"} = sub : lvalue {
521 281     281   1176 my $this = shift;
522 281         204 my $call = caller;
523 281 100 100     1471 confess "Protected ${pkg}::$name called"
      66        
524             unless ($call->isa($pkg) || $pkg->isa($call)) and $this->isa($pkg);
525 279         1026 $this->{$name};
526 44         126 };
527 44         68 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 6 my $pkg = shift;
541 8         8 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         26  
  8         16  
546            
547 8         19 *{$pkg."::$name"} = sub : lvalue {
548 7     7   728 my $this = shift;
549 7         11 my $call = caller;
550 7 100 66     227 confess "Private ${pkg}::$name called"
551             unless $pkg->isa($call) && $this->isa($pkg);
552 6         21 $this->{$name};
553 8         23 };
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 4 my ($pkg, $symbol, $ref) = @_;
568 4         3 my $method = *{$symbol}{NAME};
  4         5  
569            
570 8     8   36 no warnings 'redefine';
  8         17  
  8         1332  
571            
572 4         10 *{$symbol} = sub {
573 19     19   1281 my $this = $_[0];
574 19         20 my $call = caller;
575 19 100 66     463 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         10 }
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 5 my ($pkg, $symbol, $ref) = @_;
592 5         4 my $method = *{$symbol}{NAME};
  5         7  
593            
594 8     8   35 no warnings 'redefine';
  8         11  
  8         1330  
595            
596 5         12 *{$symbol} = sub {
597 9     9   1856 my $this = $_[0];
598 9         13 my $call = caller;
599 9 100 66     392 confess "Private ${pkg}::$method() called"
600             unless $pkg->isa($call) && $this->isa($pkg);
601 6         15 goto &$ref;
602             }
603 5         17 }
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         3 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         7  
640            
641 8     8   31 no warnings 'redefine';
  8         7  
  8         4354  
642            
643             # Can't call abstract method.
644 6     0   20 *{$symbol} = sub { confess "Abstract ${pkg}::$method() called" };
  6         13  
  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 36 my $this = shift;
656 51         47 my %classes;
657 51         32 my @classes = @{ $this.'::ISA' };
  51         102  
658 51         29 my @base;
659             my %props;
660            
661             # Get all parent's properties
662 51         93 while (@classes) {
663 42         33 my $c = pop @classes;
664 42 100       69 next if exists $classes{$c};
665 36         66 %classes = (%classes, $c => 1);
666 36         34 push @base, $c;
667 36         19 push @classes, @{$c.'::ISA'};
  36         80  
668             }
669            
670 51         78 for my $base (reverse @base) {
671 36 50       99 if ($base->can('ALL')) {
672             # Default values
673 36         57 my $all = $base->ALL();
674 36         65 for my $k (keys %$all) {
675 77 100       37 unless (exists ${$this.'::ALL'}{$k}) {
  77         150  
676 56         34 %{$this.'::ALL'} = (%{$this.'::ALL'}, $k => $all->{$k});
  56         163  
  56         108  
677             }
678             }
679             # Statics
680 36         46 $all = $base->META->{properties};
681 36         55 for my $k (keys %$all) {
682 77 100       71 unless (exists $this->META->{properties}->{$k}) {
683 56         50 %{$this->META->{properties}} = (%{$this->META->{properties}},
  56         52  
684 56         31 $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 53 my ($pkg, $ref) = @_;
702 65         90 my $symcache = $pkg->META->{symcache};
703            
704 65 50       208 return $symcache->{$pkg, $ref} if $symcache->{$pkg, $ref};
705            
706 65         55 my $type = 'CODE';
707            
708 65         41 for my $sym (values %{$pkg."::"}) {
  65         176  
709 599 50       743 next unless ref ( \$sym ) eq 'GLOB';
710            
711             return $symcache->{$pkg, $ref} = \$sym
712 599 100 100     366 if *{$sym}{$type} && *{$sym}{$type} == $ref;
  599         1387  
  533         1748  
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   6 my $class = shift;
736 7         6 my ($storage, $name, $val) = @_;
737 7 100       14 $storage->{$name} = $val unless exists $storage->{$name};
738              
739 7         25 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   22 my $self = shift;
749 42         70 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__