File Coverage

blib/lib/YATT/Lite/MFields.pm
Criterion Covered Total %
statement 102 116 87.9
branch 37 52 71.1
condition 6 9 66.6
subroutine 22 25 88.0
pod 0 12 0.0
total 167 214 78.0


line stmt bran cond sub pod time code
1             package YATT::Lite::MFields; sub MY () {__PACKAGE__}
2 26     26   957 use strict;
  26         111  
  26         793  
3 26     26   230 use warnings qw(FATAL all NONFATAL misc);
  26         60  
  26         887  
4 26     26   522 use 5.009; # For real hash only. (not works for pseudo-hash)
  26         101  
5              
6 26     26   359 use parent qw/YATT::Lite::Object/;
  26         4568  
  26         155  
7              
8             sub Decl () {'YATT::Lite::MFields::Decl'}
9             BEGIN {
10             package YATT::Lite::MFields::Decl;
11 26     26   2255 use parent qw/YATT::Lite::Object/;
  26         64  
  26         107  
12 26     26   1838 our %FIELDS = map {$_ => 1}
  286         1846  
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             cf_only_if_missing
19             /;
20             }
21              
22             BEGIN {
23 26     26   100 our %FIELDS = map {$_ => Decl->new(name => $_)}
  78         338  
24             qw/fields cf_package known_parent/;
25             }
26              
27 26         1599 use YATT::Lite::Util qw/globref look_for_globref list_isa fields_hash
28             lexpand
29 26     26   201 /;
  26         58  
30 26     26   158 use Carp;
  26         61  
  26         29359  
31              
32             sub import {
33 492     492   1568 my $pack = shift;
34 492         1148 my $callpack = caller;
35 492         1693 $pack->define_fields($callpack, @_);
36             }
37              
38             sub configure_package {
39 798     798 0 1805 (my MY $self, my $pack) = @_;
40 798         1841 $self->{cf_package} = $pack;
41 798         2145 my $sym = globref($pack, 'FIELDS');
42 798 100       1546 *$sym = {} unless *{$sym}{HASH};
  798         2774  
43 798         1422 $self->{fields} = *{$sym}{HASH};
  798         3120  
44             }
45              
46             {
47             my %meta;
48             # XXX: This might harm if we need to care about package removal.
49             # $PACKAGE::FIELDS might be good alternative place.
50              
51             sub get_meta {
52 1123     1123 0 3736 my ($pack, $callpack) = @_;
53 1123   66     6171 $meta{$callpack} //= $pack->new(package => $callpack);
54             }
55             }
56              
57             sub has_fields {
58 296     296 0 753 my ($pack, $callpack) = @_;
59 296         869 fields_hash($callpack);
60             }
61              
62             sub define_fields {
63 936     936 0 3077 my ($pack, $callpack) = splice @_, 0, 2;
64              
65 936         2519 my MY $meta = $pack->get_meta($callpack);
66              
67 936         3176 $meta->import_fields_from(list_isa($callpack));
68              
69 936 100 100     3405 if (@_ == 1 and ref $_[0] eq 'CODE') {
70 1         25 $_[0]->($meta);
71             } else {
72 935         1877 foreach my $item (@_) {
73 4505 100       11941 $meta->has(ref $item ? @$item : $item);
74             }
75             }
76              
77 936         37805 $meta;
78             }
79              
80             sub import_fields_from {
81 1042     1042 0 2099 (my MY $self) = shift;
82 1042         2278 foreach my $item (@_) {
83 1369         2333 my ($class, $fields);
84 1369 100       2900 if (ref $item) {
85 106 50       394 unless (UNIVERSAL::isa($item, MY)) {
86 0         0 croak "Invalid item for MFields::Meta->import_fields_from: $item";
87             }
88 106         205 my MY $super = $item;
89 106         221 $class = $super->{cf_package};
90 106 50       17410 next if $self->{known_parent}{$class}++;
91 0         0 $fields = $super->{fields};
92             } else {
93 1263         2065 $class = $item;
94 1263 100       4425 next if $self->{known_parent}{$class}++;
95 891 100       2426 my $sym = look_for_globref($class, 'FIELDS')
96             or next;
97 823         2498 $fields = *{$sym}{HASH}
98 823 50       1502 or next;
99             }
100              
101 823         4209 foreach my $name (keys %$fields) {
102 12473         19127 my Decl $importing = $fields->{$name};
103 12473 50       37120 unless (UNIVERSAL::isa($importing, $self->Decl)) {
104 0         0 croak "Importing raw field $class.$name is prohibited!";
105             }
106              
107 12473 50 33     24748 unless (my Decl $existing = $self->{fields}->{$name}) {
    50          
    100          
108 12078         23962 $self->{fields}->{$name} = $importing;
109 0         0 } elsif (not UNIVERSAL::isa($existing, $self->Decl)) {
110 0         0 croak "Importing $class.$name onto raw field"
111             . " (defined in $self->{cf_package}) is prohibited";
112 0         0 } elsif ($importing->{cf_only_if_missing}) {
113             ; # import $importing only if it is missing in target package.
114             } elsif ($importing != $existing) {
115             croak "Conflicting import $class.$name"
116             . " (defined in $importing->{cf_package}) "
117             . "onto $existing->{cf_package}";
118             }
119             }
120             }
121             }
122              
123             sub fields {
124 2     2 0 6 (my MY $self) = @_;
125 2         6 my $f = $self->{fields};
126 2 50       61 wantarray ? map([$_ => $f->{$_}], keys %$f) : $f;
127             }
128              
129             sub has {
130 4509     4509 0 9146 (my MY $self, my $nameSpec, my @atts) = @_;
131 4509         9934 (my $attName, @atts) = ($self->parse_field_spec($nameSpec), @atts);
132 4509 50       13257 if (my $old = $self->{fields}->{$attName}) {
133 0         0 carp "Redefinition of field $self->{cf_package}.$attName is prohibited!";
134             }
135 4509 50       11324 unless (@atts % 2 == 0) {
136 0         0 croak "Invalid number of field spec for $self->{cf_package}.$attName";
137             }
138             my Decl $field = $self->Decl->new(
139             name => $attName, @atts, package => $self->{cf_package}
140 4509         18097 );
141 4509 100       11547 if ($field->{cf_getter}) {
142 33         142 my ($name, $code) = lexpand($field->{cf_getter});
143 33 100       158 if (not defined $code) {
    50          
    0          
144 19     0   120 $code = sub {$_[0]->{$attName}};
  0         0  
145             } elsif (not ref $code) {
146 14         69 $code = $self->make_accessor_type($code, $attName);
147             } elsif (ref $code ne 'CODE') {
148 0         0 croak "field getter code must be CODE ref! for field $attName";
149             }
150 33         77 *{globref($field->{cf_package}, $name)} = $code;
  33         119  
151             }
152 4509         14615 $self->{fields}->{$attName} = $field;
153             }
154              
155             sub make_accessor_type {
156 14     14 0 53 (my MY $self, my ($type, $name)) = @_;
157 14 50       101 my $builder = $self->can("make_accessor_type_$type")
158             or croak "Unknown auto accessor type: $type";
159 14         55 $builder->($self, $name);
160             }
161              
162             sub make_accessor_type_hash {
163 0     0 0 0 (my MY $self, my $name) = @_;
164 0     0   0 sub { $_[0]->{$name} }
165 0         0 }
166              
167             sub make_accessor_type_glob {
168 14     14 0 47 (my MY $self, my $name) = @_;
169 10     10   19 sub { (*{$_[0]}{HASH})->{$name} }
  10         45  
170 14         81 }
171              
172             sub parse_field_spec {
173 4509     4509 0 7179 my $pack = shift;
174 4509 100       19587 if ($_[0] =~ m{^(\w*)\^(\w+)$}) {
    100          
175 19 50       216 ($1.$2, getter => $2, ($1 ? (public_name => $2) : ()));
176             } elsif ($_[0] =~ m{^cf_(\w+)$}) {
177 3310         13608 ($_[0], public_name => $1);
178             } else {
179 1180         2631 $_[0];
180             }
181             }
182              
183             sub add_isa_to {
184 817     817 0 2341 my ($pack, $target, @base) = @_;
185 817         2602 my $sym = globref($target, 'ISA');
186 817         1690 my $isa;
187 817 50       1380 unless ($isa = *{$sym}{ARRAY}) {
  817         2660  
188 0         0 *$sym = $isa = [];
189             }
190              
191 817         1956 foreach my $base (@base) {
192 906 100       2606 next if grep {$_ eq $base} @$isa;
  465         1198  
193             # if (my $err = do {local $@; eval {
194 905         14675 push @$isa, $base
195             # }; $@}) {
196             # if ($err =~ /^Inconsistent hierarchy during C3 merge of class/) {
197             # print "[inserting $base to $target] $err";
198             # next;
199             # }
200             # }
201             }
202              
203 817         3103 $pack;
204             }
205              
206             1;
207              
208             __END__