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   21300 use Evo 'Carp croak; Scalar::Util reftype; -Internal::Util; Module::Load ()';
  41         98  
  41         224  
3 41     41   264 use Evo '/::Attrs *; /::Syntax *';
  41         90  
  41         163  
4              
5             our @CARP_NOT = qw(Evo::Class);
6              
7 203 50   203 1 1267 sub register ($me, $package) {
  203 50       583  
  203         426  
  203         374  
  203         351  
8 41     41   322 no strict 'refs'; ## no critic
  41         98  
  41         1413  
9 41     41   237 no warnings 'once';
  41         90  
  41         6185  
10              
11 203   66     345 ${"${package}::EVO_CLASS_ATTRS"} ||= Evo::Class::Attrs->new;
  203         2437  
12              
13 203   100     379 ${"${package}::EVO_CLASS_META"}
  203         2355  
14             ||= bless {package => $package, private => {}, methods => {}, reqs => {}, overridden => {}},
15             $me;
16             }
17              
18 516 50   516 0 1512 sub find_or_croak ($self, $package) {
  516 50       1376  
  516         893  
  516         834  
  516         807  
19 41     41   333 no strict 'refs'; ## no critic
  41         98  
  41         9060  
20 516 100       773 ${"${package}::EVO_CLASS_META"}
  516         3426  
21             or croak qq#$package isn't Evo::Class; "use parent '$package';" for external classes#;
22             }
23              
24 4344 50   4344 0 9228 sub package($self) { $self->{package} }
  4344 50       8826  
  4344         6234  
  4344         6052  
  4344         8173  
25              
26 1862 50   1862 1 6097 sub attrs($self) {
  1862 50       3924  
  1862         2728  
  1862         2590  
27 41     41   279 no strict 'refs'; ## no critic
  41         105  
  41         19409  
28 1862         3143 my $package = $self->{package};
29 1862         2636 ${"${package}::EVO_CLASS_ATTRS"};
  1862         13857  
30             }
31              
32 2789 50   2789 0 5731 sub methods($self) { $self->{methods} }
  2789 50       5642  
  2789         4100  
  2789         3801  
  2789         6843  
33 89 50   89 0 276 sub reqs($self) { $self->{reqs} }
  89 50       264  
  89         168  
  89         165  
  89         455  
34              
35 331 50   331 1 743 sub overridden($self) { $self->{overridden} }
  331 50       741  
  331         522  
  331         460  
  331         934  
36 1862 50   1862 1 3870 sub private($self) { $self->{private} }
  1862 50       3656  
  1862         2667  
  1862         2499  
  1862         6003  
37              
38 16 50   16 0 1386 sub mark_as_overridden ($self, $name) {
  16 50       44  
  16         31  
  16         30  
  16         24  
39 16         45 $self->overridden->{$name} = 1;
40 16         34 $self;
41             }
42              
43 314 50   314 0 746 sub is_overridden ($self, $name) {
  314 50       674  
  314         479  
  314         495  
  314         429  
44 314         690 $self->overridden->{$name};
45             }
46              
47 15 50   15 1 716 sub mark_as_private ($self, $name) {
  15 50       41  
  15         25  
  15         30  
  15         23  
48 15         51 $self->private->{$name} = 1;
49             }
50              
51 1846 50   1846 0 3953 sub is_private ($self, $name) {
  1846 50       3687  
  1846         2987  
  1846         2661  
  1846         2515  
52 1846         3428 $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 6618 sub is_method ($self, $name) {
  2573 50       5332  
  2573         3719  
  2573         3658  
  2573         3710  
59 2573 100       5230 return 1 if $self->methods->{$name};
60 2547         4861 my $pkg = $self->package;
61              
62             {
63 41     41   280 no strict 'refs'; ## no critic
  41         88  
  41         1204  
  2547         3907  
64 41     41   221 no warnings 'once';
  41         86  
  41         52529  
65 2547         3528 my $meta = ${"${pkg}::EVO_EXPORT_META"};
  2547         6639  
66 2547 100 100     5738 return if $meta && $meta->symbols->{$name};
67             }
68              
69 2545 100       6166 my $code = Evo::Internal::Util::names2code($pkg, $name) or return;
70 1491         3585 my ($realpkg, $realname, $xsub) = Evo::Internal::Util::code2names($code);
71 1491   100     8639 return !$xsub && $realpkg eq $pkg;
72             }
73              
74 1234 50   1234 0 4361 sub is_attr ($self, $name) {
  1234 50       2635  
  1234         1885  
  1234         1820  
  1234         1769  
75 1234         2497 $self->attrs->exists($name);
76             }
77              
78 722 50   722   1697 sub _check_valid_name ($self, $name) {
  722 50       1641  
  722         1108  
  722         1165  
  722         1035  
79 722 100       2643 croak(qq{"$name" is invalid name}) unless Evo::Internal::Util::check_subname($name);
80             }
81              
82 920 50   920   2167 sub _check_exists ($self, $name) {
  920 50       2041  
  920         1417  
  920         1453  
  920         1304  
83 920         2018 my $pkg = $self->package;
84 920 100       2152 croak qq{$pkg already has attribute "$name"} if $self->is_attr($name);
85 915 100       2417 croak qq{$pkg already has method "$name"} if $self->is_method($name);
86             }
87              
88 714 50   714   1691 sub _check_exists_valid_name ($self, $name) {
  714 50       1699  
  714         1130  
  714         1102  
  714         1010  
89 714         1822 _check_valid_name($self, $name);
90 712         1879 _check_exists($self, $name);
91             }
92              
93 493 50   493   1356 sub _reg_parsed_attr ($self, %opts) {
  493 50       1263  
  493         851  
  493         1994  
  493         982  
94 493         943 my $name = $opts{name};
95 493         1304 _check_exists_valid_name($self, $name);
96 487         1231 my $pkg = $self->package;
97 487 100       1266 croak qq{$pkg already has subroutine "$name"} if Evo::Internal::Util::names2code($pkg, $name);
98              
99 484         1131 my $sub = $self->attrs->gen_attr(%opts); # register
100 484 100       2463 Evo::Internal::Util::monkey_patch $pkg, $name, $sub if $opts{method};
101             }
102              
103 8 50   8   35 sub _reg_parsed_attr_over ($self, %opts) {
  8 50       26  
  8         20  
  8         42  
  8         17  
104 8         14 my $name = $opts{name};
105 8         24 _check_valid_name($self, $name);
106 8         32 $self->mark_as_overridden($name);
107 8         18 my $sub = $self->attrs->gen_attr(%opts); # register
108 8         43 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 1508 sub reg_attr ($self, $name, @attr) {
  403         693  
  403         663  
  403         967  
  403         688  
113 403         1111 my %opts = $self->parse_attr($name, @attr);
114 403         1681 $self->_reg_parsed_attr(%opts);
115             }
116              
117 8 50   8 0 27 sub reg_attr_over ($self, $name, @attr) {
  8         14  
  8         16  
  8         23  
  8         13  
118 8         23 my %opts = $self->parse_attr($name, @attr);
119 8         43 $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 736 sub reg_method ($self, $name) {
  221 50       507  
  221         343  
  221         368  
  221         305  
125 221         536 _check_exists_valid_name($self, $name);
126 217         557 my $pkg = $self->package;
127 217 100       529 my $code = Evo::Internal::Util::names2code($pkg, $name) or croak "$pkg::$name doesn't exist";
128 215         529 $self->methods->{$name}++;
129             }
130              
131 57 50   57   184 sub _public_attrs_slots($self) {
  57 50       175  
  57         110  
  57         123  
132 57         153 grep { !$self->is_private($_->{name}) } $self->attrs->slots;
  188         475  
133             }
134              
135             # not marked as private
136             # was compiled in the same package, not constant, not exported lib
137 61 50   61   208 sub _public_methods_map($self) {
  61 50       181  
  61         119  
  61         125  
138 61         161 my $pkg = $self->package;
139 435         931 map { ($_, Evo::Internal::Util::names2code($pkg, $_)) }
140 61 100       251 grep { !$self->is_private($_) && $self->is_method($_) }
  1656         3584  
141             Evo::Internal::Util::list_symbols($pkg);
142             }
143              
144 26 50   26 0 902 sub public_attrs($self) {
  26 50       103  
  26         62  
  26         56  
145 26         90 map { $_->{name} } $self->_public_attrs_slots;
  92         249  
146             }
147              
148 27 50   27 0 135 sub public_methods($self) {
  27 50       100  
  27         61  
  27         75  
149 27         88 my %map = $self->_public_methods_map;
150 27         497 keys %map;
151             }
152              
153              
154 32 50   32 0 161 sub extend_with ($self, $source_p) {
  32 50       104  
  32         87  
  32         71  
  32         59  
155 32         100 $source_p = Evo::Internal::Util::resolve_package($self->package, $source_p);
156 32         175 Module::Load::load($source_p);
157 32         696 my $source = $self->find_or_croak($source_p);
158 31         97 my $dest_p = $self->package;
159 31         96 my %reqs = $source->reqs()->%*;
160 31         133 my %methods = $source->_public_methods_map();
161              
162 31         100 my @new_attrs;
163 31         117 foreach my $name (keys %reqs) { $self->reg_requirement($name); }
  14         78  
164              
165 31         119 foreach my $slot ($source->_public_attrs_slots) {
166 93 100       287 next if $self->is_overridden($slot->{name});
167 90         386 $self->_reg_parsed_attr(%$slot);
168 87         288 push @new_attrs, $slot->{name};
169             }
170              
171 28         221 foreach my $name (keys %methods) {
172 215 100       502 next if $self->is_overridden($name);
173 211 100       549 croak qq/$dest_p already has a subroutine with name "$name"/
174             if Evo::Internal::Util::names2code($dest_p, $name);
175 208         597 _check_exists($self, $name); # prevent patching before check
176 208         862 Evo::Internal::Util::monkey_patch $dest_p, $name, $methods{$name};
177 208         545 $self->reg_method($name);
178             }
179              
180 41     41   344 no strict 'refs'; ## no critic
  41         88  
  41         29623  
181 25         74 push @{"${dest_p}::ISA"}, $source_p;
  25         452  
182 25         178 @new_attrs;
183             }
184              
185              
186 33 50   33 0 536 sub reg_requirement ($self, $name) {
  33 50       125  
  33         77  
  33         69  
  33         67  
187 33         126 $self->reqs->{$name}++;
188             }
189              
190 23 50   23 0 219 sub requirements($self) {
  23 50       105  
  23         72  
  23         172  
191 23         146 (keys($self->reqs->%*), $self->public_attrs, $self->public_methods);
192             }
193              
194 20 50   20 1 130 sub check_implementation ($self, $inter_class) {
  20 50       81  
  20         60  
  20         47  
  20         44  
195 20         63 $inter_class = Evo::Internal::Util::resolve_package($self->package, $inter_class);
196 20         135 Module::Load::load($inter_class);
197 20         1755 my $class = $self->package;
198 20         79 my $inter = $self->find_or_croak($inter_class);
199 19         100 my @reqs = sort $inter->requirements;
200              
201 19   100     80 my @not_exists = grep { !($self->is_attr($_) || $class->can($_)); } @reqs;
  305         685  
202 19 100       182 return $self if !@not_exists;
203              
204 3         424 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 1786 sub parse_attr ($me, $name, @attr) {
  449         742  
  449         721  
  449         983  
  449         720  
216 449         839 my @scalars = grep { $_ ne SYNTAX_STATE } @attr;
  339         1384  
217 449 100       1360 croak "expected 1 scalar, got: " . join ',', @scalars if @scalars > 1;
218 448         1352 my %state = syntax_reset;
219              
220             croak qq#"optional" flag makes no sense with default("$scalars[0]")#
221 448 100 100     1920 if $state{optional} && @scalars;
222             croak qq#"lazy" requires code reference#
223 447 100 100     1972 if $state{lazy} && (reftype($scalars[0]) // '') ne 'CODE';
      100        
224 444 100 100     1960 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         751 my $type;
229 443 50       1481 if ($state{optional}) { $type = ECA_OPTIONAL if $state{optional}; }
  141 100       420  
    100          
    100          
230 14 50       43 elsif ($state{lazy}) { $type = ECA_LAZY if $state{lazy}; }
231 66 100       260 elsif (@scalars) { $type = ref($scalars[0]) ? ECA_DEFAULT_CODE : ECA_DEFAULT; }
232 222         397 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         3159 );
243             }
244              
245 1 50   1 0 12 sub info($self) {
  1 50       6  
  1         3  
  1         3  
246 1         6 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         18 \%info;
256             }
257              
258              
259             1;
260              
261             __END__