File Coverage

blib/lib/YATT/Lite/MFields.pm
Criterion Covered Total %
statement 99 115 86.0
branch 36 52 69.2
condition 5 6 83.3
subroutine 21 25 84.0
pod 0 12 0.0
total 161 210 76.6


line stmt bran cond sub pod time code
1             package YATT::Lite::MFields; sub MY () {__PACKAGE__}
2 18     18   1292 use strict;
  18         34  
  18         621  
3 18     18   94 use warnings qw(FATAL all NONFATAL misc);
  18         31  
  18         640  
4 18     18   387 use 5.009; # For real hash only. (not works for pseudo-hash)
  18         68  
5              
6 18     18   159 use parent qw/YATT::Lite::Object/;
  18         1896  
  18         111  
7              
8             sub Decl () {'YATT::Lite::MFields::Decl'}
9             BEGIN {
10             package YATT::Lite::MFields::Decl;
11 18     18   1598 use parent qw/YATT::Lite::Object/;
  18         33  
  18         223  
12 18     18   1429 our %FIELDS = map {$_ => 1}
  180         1452  
13             qw/cf_is cf_isa cf_required
14             cf_name cf_public_name cf_getter
15             cf_package
16             cf_default
17             cf_doc cf_label
18             /;
19             }
20              
21             BEGIN {
22 18     18   41 our %FIELDS = map {$_ => Decl->new(name => $_)}
  54         254  
23             qw/fields cf_package known_parent/;
24             }
25              
26 18         1254 use YATT::Lite::Util qw/globref look_for_globref list_isa fields_hash
27             lexpand
28 18     18   106 /;
  18         43  
29 18     18   89 use Carp;
  18         33  
  18         27852  
30              
31             sub import {
32 289     289   874 my $pack = shift;
33 289         602 my $callpack = caller;
34 289         863 $pack->define_fields($callpack, @_);
35             }
36              
37             sub configure_package {
38 432     432 0 746 (my MY $self, my $pack) = @_;
39 432         912 $self->{cf_package} = $pack;
40 432         1142 my $sym = globref($pack, 'FIELDS');
41 432 100       639 *$sym = {} unless *{$sym}{HASH};
  432         1452  
42 432         579 $self->{fields} = *{$sym}{HASH};
  432         2156  
43             }
44              
45             {
46             my %meta;
47             # XXX: This might harm if we need to care about package removal.
48             # $PACKAGE::FIELDS might be good alternative place.
49              
50             sub get_meta {
51 651     651 0 1027 my ($pack, $callpack) = @_;
52 651   66     3555 $meta{$callpack} //= $pack->new(package => $callpack);
53             }
54             }
55              
56             sub has_fields {
57 129     129 0 234 my ($pack, $callpack) = @_;
58 129         390 fields_hash($callpack);
59             }
60              
61             sub define_fields {
62 524     524 0 1258 my ($pack, $callpack) = splice @_, 0, 2;
63              
64 524         1283 my MY $meta = $pack->get_meta($callpack);
65              
66 524         1626 $meta->import_fields_from(list_isa($callpack));
67              
68 524 100 100     1911 if (@_ == 1 and ref $_[0] eq 'CODE') {
69 1         15 $_[0]->($meta);
70             } else {
71 523         946 foreach my $item (@_) {
72 2520 100       7025 $meta->has(ref $item ? @$item : $item);
73             }
74             }
75              
76 524         28806 $meta;
77             }
78              
79             sub import_fields_from {
80 595     595 0 1005 (my MY $self) = shift;
81 595         1263 foreach my $item (@_) {
82 784         931 my ($class, $fields);
83 784 100       1503 if (ref $item) {
84 71 50       298 unless (UNIVERSAL::isa($item, MY)) {
85 0         0 croak "Invalid item for MFields::Meta->import_fields_from: $item";
86             }
87 71         122 my MY $super = $item;
88 71         123 $class = $super->{cf_package};
89 71 50       12466 next if $self->{known_parent}{$class}++;
90 0         0 $fields = $super->{fields};
91             } else {
92 713         992 $class = $item;
93 713 100       2606 next if $self->{known_parent}{$class}++;
94 465 100       1336 my $sym = look_for_globref($class, 'FIELDS')
95             or next;
96 426         1288 $fields = *{$sym}{HASH}
97 426 50       626 or next;
98             }
99              
100 426         2139 foreach my $name (keys %$fields) {
101 5753         8515 my Decl $importing = $fields->{$name};
102 5753 50       20503 unless (UNIVERSAL::isa($importing, $self->Decl)) {
103 0         0 croak "Importing raw field $class.$name is prohibited!";
104             }
105              
106 5753 100       13806 unless (my Decl $existing = $self->{fields}->{$name}) {
    50          
    50          
107 5714         13399 $self->{fields}->{$name} = $importing;
108             } elsif (not UNIVERSAL::isa($existing, $self->Decl)) {
109 0         0 croak "Importing $class.$name onto raw field"
110             . " (defined in $self->{cf_package}) is prohibited";
111             } elsif ($importing != $existing) {
112 0         0 croak "Conflicting import $class.$name"
113             . " (defined in $importing->{cf_package}) "
114             . "onto $existing->{cf_package}";
115             }
116             }
117             }
118             }
119              
120             sub fields {
121 0     0 0 0 (my MY $self) = @_;
122 0         0 my $f = $self->{fields};
123 0 0       0 wantarray ? map([$_ => $f->{$_}], keys %$f) : $f;
124             }
125              
126             sub has {
127 2524     2524 0 4482 (my MY $self, my $nameSpec, my @atts) = @_;
128 2524         4930 (my $attName, @atts) = ($self->parse_field_spec($nameSpec), @atts);
129 2524 50       7917 if (my $old = $self->{fields}->{$attName}) {
130 0         0 carp "Redefinition of field $self->{cf_package}.$attName is prohibited!";
131             }
132 2524 50       6083 unless (@atts % 2 == 0) {
133 0         0 croak "Invalid number of field spec for $self->{cf_package}.$attName";
134             }
135             my Decl $field = $self->Decl->new(
136             name => $attName, @atts, package => $self->{cf_package}
137 2524         11971 );
138 2524 100       6670 if ($field->{cf_getter}) {
139 22         93 my ($name, $code) = lexpand($field->{cf_getter});
140 22 100       118 if (not defined $code) {
    50          
    0          
141 13     0   70 $code = sub {$_[0]->{$attName}};
  0         0  
142             } elsif (not ref $code) {
143 9         42 $code = $self->make_accessor_type($code, $attName);
144             } elsif (ref $code ne 'CODE') {
145 0         0 croak "field getter code must be CODE ref! for field $attName";
146             }
147 22         45 *{globref($field->{cf_package}, $name)} = $code;
  22         91  
148             }
149 2524         9972 $self->{fields}->{$attName} = $field;
150             }
151              
152             sub make_accessor_type {
153 9     9 0 28 (my MY $self, my ($type, $name)) = @_;
154 9 50       67 my $builder = $self->can("make_accessor_type_$type")
155             or croak "Unknown auto accessor type: $type";
156 9         33 $builder->($self, $name);
157             }
158              
159             sub make_accessor_type_hash {
160 0     0 0 0 (my MY $self, my $name) = @_;
161 0     0   0 sub { $_[0]->{$name} }
162 0         0 }
163              
164             sub make_accessor_type_glob {
165 9     9 0 23 (my MY $self, my $name) = @_;
166 2     2   5 sub { (*{$_[0]}{HASH})->{$name} }
  2         9  
167 9         50 }
168              
169             sub parse_field_spec {
170 2524     2524 0 3148 my $pack = shift;
171 2524 100       10465 if ($_[0] =~ m{^(\w*)\^(\w+)$}) {
    100          
172 13 50       154 ($1.$2, getter => $2, ($1 ? (public_name => $2) : ()));
173             } elsif ($_[0] =~ m{^cf_(\w+)$}) {
174 1861         8338 ($_[0], public_name => $1);
175             } else {
176 650         1639 $_[0];
177             }
178             }
179              
180             sub add_isa_to {
181 485     485 0 1164 my ($pack, $target, @base) = @_;
182 485         1348 my $sym = globref($target, 'ISA');
183 485         748 my $isa;
184 485 50       601 unless ($isa = *{$sym}{ARRAY}) {
  485         1626  
185 0         0 *$sym = $isa = [];
186             }
187              
188 485         952 foreach my $base (@base) {
189 570 100       1461 next if grep {$_ eq $base} @$isa;
  329         961  
190             # if (my $err = do {local $@; eval {
191 506         6592 push @$isa, $base
192             # }; $@}) {
193             # if ($err =~ /^Inconsistent hierarchy during C3 merge of class/) {
194             # print "[inserting $base to $target] $err";
195             # next;
196             # }
197             # }
198             }
199              
200 485         1736 $pack;
201             }
202              
203             1;
204              
205             __END__