File Coverage

blib/lib/Evo/Class/Meta.pm
Criterion Covered Total %
statement 289 289 100.0
branch 111 174 63.7
condition 27 28 96.4
subroutine 41 41 100.0
pod 7 25 28.0
total 475 557 85.2


line stmt bran cond sub pod time code
1             package Evo::Class::Meta;
2 41     41   22479 use Evo 'Carp croak; Scalar::Util reftype; -Internal::Util; Module::Load ()';
  41         101  
  41         246  
3 41     41   286 use Evo '/::Attrs *; /::Syntax *';
  41         92  
  41         174  
4              
5             our @CARP_NOT = qw(Evo::Class);
6              
7 203 50   203 1 1271 sub register ($me, $package) {
  203 50       606  
  203         450  
  203         358  
  203         348  
8 41     41   320 no strict 'refs'; ## no critic
  41         92  
  41         1426  
9 41     41   209 no warnings 'once';
  41         90  
  41         5952  
10              
11 203   66     349 ${"${package}::EVO_CLASS_ATTRS"} ||= Evo::Class::Attrs->new;
  203         2562  
12              
13 203   100     374 ${"${package}::EVO_CLASS_META"}
  203         2495  
14             ||= bless {package => $package, private => {}, methods => {}, reqs => {}, overridden => {}},
15             $me;
16             }
17              
18 516 50   516 0 1428 sub find_or_croak ($self, $package) {
  516 50       1341  
  516         957  
  516         878  
  516         770  
19 41     41   305 no strict 'refs'; ## no critic
  41         91  
  41         6516  
20 516 100       776 ${"${package}::EVO_CLASS_META"}
  516         3418  
21             or croak qq#$package isn't Evo::Class; "use parent '$package';" for external classes#;
22             }
23              
24 4344 50   4344 0 9312 sub package($self) { $self->{package} }
  4344 50       8827  
  4344         6306  
  4344         6213  
  4344         8258  
25              
26 1862 50   1862 1 6482 sub attrs($self) {
  1862 50       3984  
  1862         2870  
  1862         2560  
27 41     41   253 no strict 'refs'; ## no critic
  41         146  
  41         18765  
28 1862         3116 my $package = $self->{package};
29 1862         2569 ${"${package}::EVO_CLASS_ATTRS"};
  1862         13925  
30             }
31              
32 2789 50   2789 0 5951 sub methods($self) { $self->{methods} }
  2789 50       5620  
  2789         4045  
  2789         3913  
  2789         6965  
33 89 50   89 0 251 sub reqs($self) { $self->{reqs} }
  89 50       237  
  89         157  
  89         153  
  89         389  
34              
35 331 50   331 1 824 sub overridden($self) { $self->{overridden} }
  331 50       733  
  331         542  
  331         521  
  331         1007  
36 1862 50   1862 1 3942 sub private($self) { $self->{private} }
  1862 50       3689  
  1862         2681  
  1862         2480  
  1862         6044  
37              
38 16 50   16 0 1245 sub mark_as_overridden ($self, $name) {
  16 50       39  
  16         27  
  16         24  
  16         27  
39 16         36 $self->overridden->{$name} = 1;
40 16         33 $self;
41             }
42              
43 314 50   314 0 860 sub is_overridden ($self, $name) {
  314 50       729  
  314         523  
  314         529  
  314         466  
44 314         700 $self->overridden->{$name};
45             }
46              
47 15 50   15 1 796 sub mark_as_private ($self, $name) {
  15 50       37  
  15         25  
  15         29  
  15         27  
48 15         45 $self->private->{$name} = 1;
49             }
50              
51 1846 50   1846 0 3975 sub is_private ($self, $name) {
  1846 50       3716  
  1846         2682  
  1846         2705  
  1846         2495  
52 1846         3371 $self->private->{$name};
53             }
54              
55             # first check methods (marked as method or inherited), if doesn't exists, try to determine if there is a sub in package
56             # if a sub is compiled in the same package, it's a public, if not(imported or xsub), and not exported function - it's private
57              
58 2573 50   2573 0 6660 sub is_method ($self, $name) {
  2573 50       5349  
  2573         3796  
  2573         3806  
  2573         3499  
59 2573 100       4997 return 1 if $self->methods->{$name};
60 2547         4931 my $pkg = $self->package;
61              
62             {
63 41     41   276 no strict 'refs'; ## no critic
  41         98  
  41         1452  
  2547         3967  
64 41     41   200 no warnings 'once';
  41         89  
  41         54625  
65 2547         3527 my $meta = ${"${pkg}::EVO_EXPORT_META"};
  2547         6638  
66 2547 100 100     5929 return if $meta && $meta->symbols->{$name};
67             }
68              
69 2545 100       6247 my $code = Evo::Internal::Util::names2code($pkg, $name) or return;
70 1491         3682 my ($realpkg, $realname, $xsub) = Evo::Internal::Util::code2names($code);
71 1491   100     8937 return !$xsub && $realpkg eq $pkg;
72             }
73              
74 1234 50   1234 0 4506 sub is_attr ($self, $name) {
  1234 50       2749  
  1234         1853  
  1234         1870  
  1234         1714  
75 1234         2494 $self->attrs->exists($name);
76             }
77              
78 722 50   722   1688 sub _check_valid_name ($self, $name) {
  722 50       1610  
  722         1153  
  722         1117  
  722         1036  
79 722 100       1990 croak(qq{"$name" is invalid name}) unless Evo::Internal::Util::check_subname($name);
80             }
81              
82 920 50   920   2208 sub _check_exists ($self, $name) {
  920 50       2074  
  920         1462  
  920         1429  
  920         1367  
83 920         2095 my $pkg = $self->package;
84 920 100       2163 croak qq{$pkg already has attribute "$name"} if $self->is_attr($name);
85 915 100       2429 croak qq{$pkg already has method "$name"} if $self->is_method($name);
86             }
87              
88 714 50   714   1782 sub _check_exists_valid_name ($self, $name) {
  714 50       1640  
  714         1155  
  714         1178  
  714         1048  
89 714         1807 _check_valid_name($self, $name);
90 712         1841 _check_exists($self, $name);
91             }
92              
93 493 50   493   1299 sub _reg_parsed_attr ($self, %opts) {
  493 50       1253  
  493         827  
  493         2187  
  493         990  
94 493         881 my $name = $opts{name};
95 493         1325 _check_exists_valid_name($self, $name);
96 487         1144 my $pkg = $self->package;
97 487 100       1274 croak qq{$pkg already has subroutine "$name"} if Evo::Internal::Util::names2code($pkg, $name);
98              
99 484         1146 my $sub = $self->attrs->gen_attr(%opts); # register
100 484 100       2393 Evo::Internal::Util::monkey_patch $pkg, $name, $sub if $opts{method};
101             }
102              
103 8 50   8   26 sub _reg_parsed_attr_over ($self, %opts) {
  8 50       28  
  8         15  
  8         36  
  8         16  
104 8         19 my $name = $opts{name};
105 8         20 _check_valid_name($self, $name);
106 8         27 $self->mark_as_overridden($name);
107 8         18 my $sub = $self->attrs->gen_attr(%opts); # register
108 8         41 my $pkg = $self->package;
109 8 100       42 Evo::Internal::Util::monkey_patch_silent $pkg, $name, $sub if $opts{method};
110             }
111              
112 403 50   403 0 1332 sub reg_attr ($self, $name, @attr) {
  403         674  
  403         652  
  403         1018  
  403         702  
113 403         1369 my %opts = $self->parse_attr($name, @attr);
114 403         1631 $self->_reg_parsed_attr(%opts);
115             }
116              
117 8 50   8 0 23 sub reg_attr_over ($self, $name, @attr) {
  8         16  
  8         13  
  8         21  
  8         15  
118 8         21 my %opts = $self->parse_attr($name, @attr);
119 8         38 $self->_reg_parsed_attr_over(%opts);
120             }
121              
122             # means register external sub as method. Because every sub in the current package
123             # is public by default
124 221 50   221 1 757 sub reg_method ($self, $name) {
  221 50       536  
  221         481  
  221         369  
  221         347  
125 221         555 _check_exists_valid_name($self, $name);
126 217         574 my $pkg = $self->package;
127 217 100       568 my $code = Evo::Internal::Util::names2code($pkg, $name) or croak "$pkg::$name doesn't exist";
128 215         536 $self->methods->{$name}++;
129             }
130              
131 57 50   57   205 sub _public_attrs_slots($self) {
  57 50       157  
  57         105  
  57         101  
132 57         180 grep { !$self->is_private($_->{name}) } $self->attrs->slots;
  188         484  
133             }
134              
135             # not marked as private
136             # was compiled in the same package, not constant, not exported lib
137 61 50   61   192 sub _public_methods_map($self) {
  61 50       166  
  61         112  
  61         119  
138 61         145 my $pkg = $self->package;
139 435         1009 map { ($_, Evo::Internal::Util::names2code($pkg, $_)) }
140 61 100       230 grep { !$self->is_private($_) && $self->is_method($_) }
  1656         3568  
141             Evo::Internal::Util::list_symbols($pkg);
142             }
143              
144 26 50   26 0 1153 sub public_attrs($self) {
  26 50       77  
  26         52  
  26         47  
145 26         80 map { $_->{name} } $self->_public_attrs_slots;
  92         261  
146             }
147              
148 27 50   27 0 98 sub public_methods($self) {
  27 50       91  
  27         50  
  27         47  
149 27         90 my %map = $self->_public_methods_map;
150 27         459 keys %map;
151             }
152              
153              
154 32 50   32 0 143 sub extend_with ($self, $source_p) {
  32 50       98  
  32         57  
  32         62  
  32         55  
155 32         95 $source_p = Evo::Internal::Util::resolve_package($self->package, $source_p);
156 32         163 Module::Load::load($source_p);
157 32         566 my $source = $self->find_or_croak($source_p);
158 31         90 my $dest_p = $self->package;
159 31         89 my %reqs = $source->reqs()->%*;
160 31         109 my %methods = $source->_public_methods_map();
161              
162 31         109 my @new_attrs;
163 31         113 foreach my $name (keys %reqs) { $self->reg_requirement($name); }
  14         85  
164              
165 31         120 foreach my $slot ($source->_public_attrs_slots) {
166 93 100       299 next if $self->is_overridden($slot->{name});
167 90         439 $self->_reg_parsed_attr(%$slot);
168 87         317 push @new_attrs, $slot->{name};
169             }
170              
171 28         212 foreach my $name (keys %methods) {
172 215 100       532 next if $self->is_overridden($name);
173 211 100       598 croak qq/$dest_p already has a subroutine with name "$name"/
174             if Evo::Internal::Util::names2code($dest_p, $name);
175 208         604 _check_exists($self, $name); # prevent patching before check
176 208         725 Evo::Internal::Util::monkey_patch $dest_p, $name, $methods{$name};
177 208         673 $self->reg_method($name);
178             }
179              
180 41     41   339 no strict 'refs'; ## no critic
  41         91  
  41         30119  
181 25         72 push @{"${dest_p}::ISA"}, $source_p;
  25         426  
182 25         182 @new_attrs;
183             }
184              
185              
186 33 50   33 0 345 sub reg_requirement ($self, $name) {
  33 50       116  
  33         71  
  33         71  
  33         65  
187 33         124 $self->reqs->{$name}++;
188             }
189              
190 23 50   23 0 87 sub requirements($self) {
  23 50       88  
  23         55  
  23         124  
191 23         121 (keys($self->reqs->%*), $self->public_attrs, $self->public_methods);
192             }
193              
194 20 50   20 1 111 sub check_implementation ($self, $inter_class) {
  20 50       84  
  20         48  
  20         51  
  20         43  
195 20         74 $inter_class = Evo::Internal::Util::resolve_package($self->package, $inter_class);
196 20         126 Module::Load::load($inter_class);
197 20         1802 my $class = $self->package;
198 20         186 my $inter = $self->find_or_croak($inter_class);
199 19         90 my @reqs = sort $inter->requirements;
200              
201 19   100     81 my @not_exists = grep { !($self->is_attr($_) || $class->can($_)); } @reqs;
  305         656  
202 19 100       189 return $self if !@not_exists;
203              
204 3         306 croak qq/Bad implementation of "$inter_class", missing in "$class": /, join ';', @not_exists;
205             }
206              
207             # -- class methods for usage from other modules too
208              
209              
210             # rtype: default, default_code, required, lazy, relaxed
211             # rvalue is used as meta for required(di), default and lazy
212             # check?
213             # is_ro?
214              
215 449 50   449 0 1820 sub parse_attr ($me, $name, @attr) {
  449         738  
  449         703  
  449         978  
  449         740  
216 449         823 my @scalars = grep { $_ ne SYNTAX_STATE } @attr;
  339         1371  
217 449 100       1552 croak "expected 1 scalar, got: " . join ',', @scalars if @scalars > 1;
218 448         1316 my %state = syntax_reset;
219              
220             croak qq#"optional" flag makes no sense with default("$scalars[0]")#
221 448 100 100     1864 if $state{optional} && @scalars;
222             croak qq#"lazy" requires code reference#
223 447 100 100     1651 if $state{lazy} && (reftype($scalars[0]) // '') ne 'CODE';
      100        
224 444 100 100     1951 croak qq#default("$scalars[0]") should be either a scalar or a code reference#
      100        
225             if @scalars && ref($scalars[0]) && reftype($scalars[0]) ne 'CODE';
226              
227              
228 443         719 my $type;
229 443 50       1426 if ($state{optional}) { $type = ECA_OPTIONAL if $state{optional}; }
  141 100       408  
    100          
    100          
230 14 50       40 elsif ($state{lazy}) { $type = ECA_LAZY if $state{lazy}; }
231 66 100       251 elsif (@scalars) { $type = ref($scalars[0]) ? ECA_DEFAULT_CODE : ECA_DEFAULT; }
232 222         374 else { $type = ECA_REQUIRED; }
233              
234             return (
235             name => $name,
236             type => $type,
237             value => $scalars[0],
238             check => $state{check},
239             ro => !!$state{ro},
240             inject => $state{inject},
241             method => !$state{no_method},
242 443         3094 );
243             }
244              
245 1 50   1 0 10 sub info($self) {
  1 50       4  
  1         3  
  1         2  
246 1         4 my %info = (
247             public => {
248             methods => [sort $self->public_methods],
249             attrs => [sort $self->public_attrs],
250             reqs => [sort keys($self->reqs->%*)],
251             },
252             overridden => [sort keys($self->overridden->%*)],
253             private => [sort keys($self->private->%*)],
254             );
255 1         16 \%info;
256             }
257              
258              
259             1;
260              
261             __END__