File Coverage

lib/Pony/Object.pm
Criterion Covered Total %
statement 376 388 96.9
branch 103 118 87.2
condition 43 55 78.1
subroutine 54 56 96.4
pod 0 18 0.0
total 576 635 90.7


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   416384 use feature ':5.10';
  8         68  
  8         808  
5 8     8   4592 use Storable qw/dclone/;
  8         22162  
  8         399  
6 8     8   3594 use Module::Load;
  8         7633  
  8         41  
7 8     8   363 use Carp qw(confess);
  8         13  
  8         345  
8 8     8   38 use Scalar::Util qw(refaddr);
  8         13  
  8         317  
9              
10 8     8   42 use constant DEBUG => 0;
  8         10  
  8         1059  
11              
12             BEGIN {
13 8     8   15091 if (DEBUG) {
14             say STDERR "\n[!] Pony::Object DEBUGing mode is turning on!\n";
15              
16             *{dumper} = sub {
17 8     8   4216 use Data::Dumper;
  8         45145  
  8         664  
18             $Data::Dumper::Indent = 1;
19             say Dumper(@_);
20             say '=' x 79;
21             }
22             }
23             }
24              
25             our $VERSION = "1.04";
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   14323 my $this = shift;
47 53         96 my $call = caller;
48              
49             # Modify caller just once.
50             # We suppose, that only we can create function ALL.
51 53 100       56 return if defined *{$call.'::ALL'};
  53         298  
52              
53             # Parse parameters.
54 52         2584 my $default = dclone $DEFAULT;
55 52         103 my $profile;
56              
57             # Get predefined params.
58 52         163 for my $prefix (sort {length $b <=> length $a} keys %$DEFAULT) {
  3         10  
59 55 100       336 if ($call =~ /^$prefix/) {
60             my @doesnt_exist = grep {
61 105         279 not exists $profile->{$_}
62 53         63 } keys %{ $default->{$prefix} };
  53         138  
63              
64 53         162 $profile->{$_} = $default->{$prefix}->{$_} for @doesnt_exist;
65 53         107 next;
66             }
67              
68 2 50       3 last if keys %{$default->{''}} == keys %{$default->{$call}};
  2         5  
  2         7  
69             }
70              
71 52         78 $profile->{isAbstract} = 0; # don't do default object abstract.
72 52         63 $profile->{isSingleton} = 0; # don't do default object singleton.
73 52         101 $profile = parseParams($call, $profile, @_);
74              
75             # Keywords, base methods, attributes.
76 52         95 predefine($call, $profile);
77              
78             # Pony objects must be strict and modern.
79 52         292 strict ->import;
80 52         424 warnings->import;
81 52         2452 feature ->import(':5.10');
82 52 50       424 feature ->import('signatures') if $] >= 5.020;
83              
84 52 100       1550 unless ($profile->{noObject}) {
85             # Base classes and params.
86 51         138 prepareClass($call, "${call}::ISA", $profile);
87              
88 51         105 methodsInheritance($call);
89 51         100 propertiesInheritance($call);
90              
91 51     52   148 *{$call.'::new'} = sub { importNew($call, @_) };
  51         3409  
  52         5263  
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 83 my $call = shift;
105              
106 52 100       123 if ($call->META->{isAbstract}) {
107 2         259 confess "Trying to use an abstract class $call";
108             } else {
109 50         139 $call->AFTER_LOAD_CHECK;
110             }
111              
112             # For singletons.
113 50 100       64 return ${$call.'::instance'} if defined ${$call.'::instance'};
  1         3  
  50         241  
114              
115 49         73 my $this = shift;
116 49         52 my $obj = dclone { %{${this}.'::ALL'} };
  49         1636  
117              
118 49         149 while (my ($k, $p) = each %{$this->META->{properties}}) {
  182         271  
119 133 100       139 if (grep {$_ eq 'static'} @{$p->{access}}) {
  140         363  
  133         218  
120             tie $obj->{$k}, 'Pony::Object::TieStatic',
121 7   66     13 $call->META->{static}, $k, $call->META->{static}->{$k} || $obj->{$k};
122             }
123             }
124              
125 49         94 $this = bless $obj, $this;
126              
127 49 100       77 ${$call.'::instance'} = $this if $call->META->{isSingleton};
  1         4  
128              
129             # 'After hook' for user.
130 49 100       348 $this->init(@_) if $call->can('init');
131 49         137 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 102 my ($call, $profile, @params) = @_;
146              
147 52         69 for my $param (@params) {
148              
149             # Define singleton class.
150 41 100 100     275 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         8 $profile->{isAbstract} = 1;
158 5         6 next;
159             }
160              
161             # Features:
162              
163             # Use exceptions featureset.
164             elsif ($param =~ /^:exceptions?$/ || $param =~ /^:try$/) {
165 3         4 $profile->{withExceptions} = 1;
166 3         5 next;
167             }
168              
169             # Don't use exceptions featureset.
170             elsif ($param =~ /^:noexceptions?$/ || $param =~ /^:notry$/) {
171 2         4 $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         2 next;
180             }
181              
182             # Base classes:
183              
184             # Save class' base classes.
185             else {
186 29         31 push @{$profile->{baseClass}}, $param;
  29         65  
187             }
188             }
189              
190 52         79 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 88 my ($call, $isaRef, $profile) = @_;
202              
203 51   50     203 $call->META->{isSingleton} = $profile->{isSingleton} // 0;
204 51   50     122 $call->META->{isAbstract} = $profile->{isAbstract} // 0;
205              
206 51         59 for my $base (@{ $profile->{baseClass} }) {
  51         93  
207 31 100       52 next if $call eq $base;
208 30         88 load $base;
209 30 50       1908 $base->AFTER_LOAD_CHECK if $base->can('AFTER_LOAD_CHECK');
210 30         242 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 76 my ($call, $profile) = @_;
222              
223             # Only for objects.
224 52 100       103 unless ($profile->{noObject}) {
225             # Predefine ALL and META.
226 51         51 %{$call.'::ALL' } = ();
  51         217  
227 51         48 %{$call.'::META'} = ();
  51         171  
228 51         88 ${$call.'::META'}{isSingleton}= 0;
  51         108  
229 51         49 ${$call.'::META'}{isAbstract} = 0;
  51         82  
230 51         63 ${$call.'::META'}{abstracts} = [];
  51         82  
231 51         66 ${$call.'::META'}{methods} = {};
  51         83  
232 51         51 ${$call.'::META'}{properties} = {};
  51         77  
233 51         59 ${$call.'::META'}{symcache} = {};
  51         85  
234 51         56 ${$call.'::META'}{checked} = 0;
  51         88  
235 51         68 ${$call.'::META'}{static} = {};
  51         123  
236              
237             # Access for properties.
238 51     17   180 *{$call.'::has'} = sub { addProperty ($call, @_) };
  51         177  
  17         227  
239 51     2   111 *{$call.'::static'} = sub { addStatic ($call, @_) };
  51         182  
  2         105  
240 51     6   113 *{$call.'::public'} = sub { addPublic ($call, @_) };
  51         126  
  6         104  
241 51     8   92 *{$call.'::private'} = sub { addPrivate ($call, @_) };
  51         221  
  8         142  
242 51     44   88 *{$call.'::protected'}= sub { addProtected($call, @_) };
  51         160  
  44         937  
243              
244             # Convert object's data into hash.
245             # Uses ALL() to get properties' list.
246 51         159 *{$call.'::toHash'} = *{$call.'::to_h'} = sub {
  51         141  
247 2     2   449 my $this = shift;
248 2         3 my %hash = map { $_, $this->{$_} } keys %{ $this->ALL() };
  4         11  
  2         5  
249 2         5 return \%hash;
250 51         120 };
251              
252 51     80   98 *{$call.'::AFTER_LOAD_CHECK'} = sub { checkImplementations($call) };
  51         138  
  80         151  
253              
254             # Save method's attributes.
255 51         172 *{$call.'::MODIFY_CODE_ATTRIBUTES'} = sub {
256 57     57   7179 my ($pkg, $ref, @attrs) = @_;
257 57         102 my $sym = findsym($pkg, $ref);
258              
259 57         109 $call->META->{methods}->{ *{$sym}{NAME} } = {
260 57         175 attributes => \@attrs,
261             package => $pkg
262             };
263              
264 57         87 for my $attr (@attrs) {
265 57 100       111 if ($attr eq 'Public' ) { makePublic ($pkg, $sym, $ref) }
  45 100       68  
    100          
    50          
266 3         6 elsif ($attr eq 'Protected') { makeProtected($pkg, $sym, $ref) }
267 3         5 elsif ($attr eq 'Private' ) { makePrivate ($pkg, $sym, $ref) }
268 6         11 elsif ($attr eq 'Abstract' ) { makeAbstract ($pkg, $sym, $ref) }
269             }
270 57         127 return;
271 51         121 };
272              
273             # Getters for REFs to special variables %ALL and %META.
274 51     41   105 *{$call.'::ALL'} = sub { \%{ $call.'::ALL' } };
  51         98  
  41         742  
  41         93  
275 51     1317   94 *{$call.'::META'} = sub { \%{ $call.'::META'} };
  51         95  
  1317         1110  
  1317         3640  
276             }
277              
278             # Try, Catch, Finally.
279             # Define them if user wants.
280 52 100       110 if ($profile->{withExceptions}) {
281 6         19 *{$call.'::try'} = sub (&;@) {
282 22     22   966 my($try, $catch, $finally) = @_;
283 22         29 local $@;
284              
285             # If some one wanna to get some
286             # values from try/catch/finally blocks.
287 22 100       49 if (defined wantarray) {
288 12 100       66 if (wantarray == 0) {
    50          
289 8         14 my $ret = eval{ $try->() };
  8         20  
290 8 100 100     81 $ret = $catch->($@) if $@ && defined $catch;
291 8 100       42 $ret = $finally->() if defined $finally;
292 8         31 return $ret;
293             }
294             elsif (wantarray == 1) {
295 4         5 my @ret = eval{ $try->() };
  4         5  
296 4 100 100     32 @ret = $catch->($@) if $@ && defined $catch;
297 4 100       12 @ret = $finally->() if defined $finally;
298 4         12 return @ret;
299             }
300             }
301             else {
302 10         17 eval{ $try->() };
  10         27  
303 10 100 100     104 $catch->($@) if $@ && defined $catch;
304 9 100       1619 $finally->() if defined $finally;
305             }
306 6         19 };
307 6     17   12 *{$call.'::catch'} = sub (&;@) { @_ };
  6         70  
  17         2515  
308 6     5   32 *{$call.'::finally'} = sub (&) { @_ };
  6         19  
  5         557  
309             }
310              
311             # This method provides deep copy
312             # for Pony::Objects
313 52     2   108 *{$call.'::clone'} = sub { dclone shift };
  52         132  
  2         74  
314              
315             # Simple Data::Dumper wrapper.
316 52         231 *{$call.'::dump'} = sub {
317 8     8   62 use Data::Dumper;
  8         12  
  8         10990  
318 1     1   740 $Data::Dumper::Indent = 1;
319 1         6 Dumper(@_);
320 52         100 };
321             }
322              
323             # Function: methodsInheritance
324             # Inheritance of methods.
325             #
326             # Parameters:
327             # $this - Str - caller package.
328             sub methodsInheritance {
329 51     51 0 55 my $this = shift;
330              
331 51         51 for my $base ( @{$this.'::ISA'} ) {
  51         167  
332             # All Pony-like classes.
333 30 50       87 if ($base->can('META')) {
334 30         47 my $methods = $base->META->{methods};
335              
336 30         93 while (my($k, $v) = each %$methods) {
337             $this->META->{methods}->{$k} = $v
338 51 100       68 unless exists $this->META->{methods}->{$k};
339             }
340              
341             # Abstract classes.
342 30 100       44 if ($base->META->{isAbstract}) {
343 7         8 my $abstracts = $base->META->{abstracts};
344 7         6 push @{ $this->META->{abstracts} }, @$abstracts;
  7         9  
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 102 my $this = shift;
358              
359 80 100       112 return if $this->META->{checked};
360 42         78 $this->META->{checked} = 1;
361              
362             # Check: does all abstract methods implemented.
363 42         54 for my $base (@{$this.'::ISA'}) {
  42         133  
364 29 100 66     175 if ( $base->can('META') && $base->META->{isAbstract} ) {
365 7         15 my $methods = $base->META->{abstracts};
366 7         8 my @bad;
367              
368             # Find Abstract methods,
369             # which was not implements.
370 7         8 for my $method (@$methods) {
371             # Get Abstract methods.
372             push @bad, $method
373 11         33 if grep { $_ eq 'Abstract' }
374 11 50       14 @{ $base->META->{methods}->{$method}->{attributes} };
  11         14  
375              
376             # Get abstract methods,
377             # which doesn't implement.
378 11         14 @bad = grep { !exists $this->META->{methods}->{$_} } @bad;
  11         15  
379             }
380              
381 7 50       19 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 28 my ($this, $attr, $value) = @_;
401              
402             # Properties
403 17 100       33 if (ref $value ne 'CODE') {
404 9 50       22 if ($attr =~ /^__/) {
    50          
405 0         0 return addPrivate(@_);
406             } elsif ($attr =~ /^_/) {
407 0         0 return addProtected(@_);
408             } else {
409 9         13 return addPublic(@_);
410             }
411             }
412              
413             # Methods
414             else {
415 8         6 *{$this."::$attr"} = $value;
  8         31  
416 8         12 my $sym = findsym($this, $value);
417 8         14 my @attrs = qw/Public/;
418              
419 8 100       23 if ($attr =~ /^__/) {
    100          
420 2         3 @attrs = qw/Private/;
421 2         5 return makePrivate($this, $sym, $value);
422             } elsif ($attr =~ /^_/) {
423 1         2 @attrs = qw/Protected/;
424 1         2 return makeProtected($this, $sym, $value);
425             } else {
426 5         9 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 3 my $call = shift;
449 2         2 my ($name, $value) = @_;
450 2         3 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 71 my $access = shift;
465 69         60 my $call = shift;
466 69         76 my ($name, $value) = @_;
467              
468 69         101 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     92 $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     429 ( $props->{$name}->{package} && $props->{$name}->{package} ne $call );
      66        
479              
480 69         99 push @{$props->{$name}->{access}}, $access;
  69         117  
481 69         126 $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 33 my $call = shift;
494 15         18 my ($name, $value) = @_;
495 15         23 addPropertyToMeta('public', $call, @_);
496              
497             # Save pair (property name => default value)
498 15         12 %{ $call.'::ALL' } = ( %{ $call.'::ALL' }, $name => $value );
  15         37  
  15         33  
499 15     102   40 *{$call."::$name"} = sub : lvalue { my $call = shift; $call->{$name} };
  15         49  
  102         3957  
  102         349  
500 15         31 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 59 my $pkg = shift;
514 44         61 my ($name, $value) = @_;
515 44         68 addPropertyToMeta('protected', $pkg, @_);
516              
517             # Save pair (property name => default value)
518 44         39 %{$pkg.'::ALL'} = (%{$pkg.'::ALL'}, $name => $value);
  44         111  
  44         94  
519              
520 44         137 *{$pkg."::$name"} = sub : lvalue {
521 281     281   1267 my $this = shift;
522 281         303 my $call = caller;
523 281 100 100     1355 confess "Protected ${pkg}::$name called"
      66        
524             unless ($call->isa($pkg) || $pkg->isa($call)) and $this->isa($pkg);
525 279         890 $this->{$name};
526 44         131 };
527 44         74 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         10 my ($name, $value) = @_;
542 8         12 addPropertyToMeta('private', $pkg, @_);
543              
544             # Save pair (property name => default value)
545 8         8 %{ $pkg.'::ALL' } = ( %{ $pkg.'::ALL' }, $name => $value );
  8         22  
  8         19  
546              
547 8         26 *{$pkg."::$name"} = sub : lvalue {
548 7     7   614 my $this = shift;
549 7         10 my $call = caller;
550 7 100 66     117 confess "Private ${pkg}::$name called"
551             unless $pkg->isa($call) && $this->isa($pkg);
552 6         20 $this->{$name};
553 8         27 };
554 8         11 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 7 my ($pkg, $symbol, $ref) = @_;
568 4         4 my $method = *{$symbol}{NAME};
  4         6  
569              
570 8     8   55 no warnings 'redefine';
  8         13  
  8         1487  
571              
572 4         13 *{$symbol} = sub {
573 19     19   1168 my $this = $_[0];
574 19         25 my $call = caller;
575 19 100 66     456 confess "Protected ${pkg}::$method() called"
      66        
576             unless ($call->isa($pkg) || $pkg->isa($call)) and $this->isa($pkg);
577 15         37 goto &$ref;
578             }
579 4         26 }
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 8 my ($pkg, $symbol, $ref) = @_;
592 5         13 my $method = *{$symbol}{NAME};
  5         8  
593              
594 8     8   51 no warnings 'redefine';
  8         20  
  8         1633  
595              
596 5         17 *{$symbol} = sub {
597 9     9   1850 my $this = $_[0];
598 9         17 my $call = caller;
599 9 100 66     457 confess "Private ${pkg}::$method() called"
600             unless $pkg->isa($call) && $this->isa($pkg);
601 6         24 goto &$ref;
602             }
603 5         28 }
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 7 my ($pkg, $symbol, $ref) = @_;
630 6         6 my $method = *{$symbol}{NAME};
  6         7  
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       10 unless $pkg->META->{isAbstract};
636              
637             # Push abstract method
638             # into object meta.
639 6         6 push @{ $pkg->META->{abstracts} }, $method;
  6         9  
640              
641 8     8   51 no warnings 'redefine';
  8         13  
  8         5055  
642              
643             # Can't call abstract method.
644 6     0   20 *{$symbol} = sub { confess "Abstract ${pkg}::$method() called" };
  6         14  
  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 54 my $this = shift;
656 51         49 my %classes;
657 51         43 my @classes = @{ $this.'::ISA' };
  51         121  
658 51         62 my @base;
659             my %props;
660              
661             # Get all parent's properties
662 51         91 while (@classes) {
663 42         52 my $c = pop @classes;
664 42 100       89 next if exists $classes{$c};
665 36         73 %classes = (%classes, $c => 1);
666 36         45 push @base, $c;
667 36         33 push @classes, @{$c.'::ISA'};
  36         89  
668             }
669              
670 51         88 for my $base (reverse @base) {
671 36 50       105 if ($base->can('ALL')) {
672             # Default values
673 36         58 my $all = $base->ALL();
674 36         66 for my $k (keys %$all) {
675 77 100       65 unless (exists ${$this.'::ALL'}{$k}) {
  77         155  
676 56         80 %{$this.'::ALL'} = (%{$this.'::ALL'}, $k => $all->{$k});
  56         154  
  56         110  
677             }
678             }
679             # Statics
680 36         62 $all = $base->META->{properties};
681 36         60 for my $k (keys %$all) {
682 77 100       95 unless (exists $this->META->{properties}->{$k}) {
683 56         64 %{$this->META->{properties}} = (%{$this->META->{properties}},
  56         59  
684 56         54 $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 89 my ($pkg, $ref) = @_;
702 65         100 my $symcache = $pkg->META->{symcache};
703              
704 65 50       223 return $symcache->{$pkg, $ref} if $symcache->{$pkg, $ref};
705              
706 65         76 my $type = 'CODE';
707              
708 65         64 for my $sym (values %{$pkg."::"}) {
  65         200  
709 649 50       856 next unless ref ( \$sym ) eq 'GLOB';
710              
711             return $symcache->{$pkg, $ref} = \$sym
712 649 100 100     535 if *{$sym}{$type} && *{$sym}{$type} == $ref;
  649         1257  
  571         1854  
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   9 my $class = shift;
736 7         10 my ($storage, $name, $val) = @_;
737 7 100       14 $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   38 my $self = shift;
749 42         77 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__