File Coverage

blib/lib/fields.pm
Criterion Covered Total %
statement 47 94 50.0
branch 15 42 35.7
condition 10 13 76.9
subroutine 8 10 80.0
pod 1 2 50.0
total 81 161 50.3


line stmt bran cond sub pod time code
1 6     6   87542 use 5.008;
  6         20  
2             package fields;
3              
4             require 5.005;
5 6     6   36 use strict;
  6         8  
  6         158  
6 6     6   36 no strict 'refs';
  6         11  
  6         497  
7             unless( eval q{require warnings::register; warnings::register->import; 1} ) {
8             *warnings::warnif = sub {
9             require Carp;
10             Carp::carp(@_);
11             }
12             }
13 6     6   30 use vars qw(%attr $VERSION);
  6         9  
  6         7354  
14              
15             $VERSION = '2.23';
16             $VERSION =~ tr/_//d;
17              
18             # constant.pm is slow
19             sub PUBLIC () { 2**0 }
20             sub PRIVATE () { 2**1 }
21             sub INHERITED () { 2**2 }
22             sub PROTECTED () { 2**3 }
23              
24              
25             # The %attr hash holds the attributes of the currently assigned fields
26             # per class. The hash is indexed by class names and the hash value is
27             # an array reference. The first element in the array is the lowest field
28             # number not belonging to a base class. The remaining elements' indices
29             # are the field numbers. The values are integer bit masks, or undef
30             # in the case of base class private fields (which occupy a slot but are
31             # otherwise irrelevant to the class).
32              
33             sub import {
34 56     56   21436 my $class = shift;
35 56 100       164 return unless @_;
36 54         86 my $package = caller(0);
37             # avoid possible typo warnings
38 54 100       57 %{"$package\::FIELDS"} = () unless %{"$package\::FIELDS"};
  25         116  
  54         225  
39 54         70 my $fields = \%{"$package\::FIELDS"};
  54         126  
40 54   100     212 my $fattr = ($attr{$package} ||= [1]);
41 54         74 my $next = @$fattr;
42              
43             # Quiet pseudo-hash deprecation warning for uses of fields::new.
44 54         56 bless \%{"$package\::FIELDS"}, 'pseudohash';
  54         146  
45              
46 54 100 100     241 if ($next > $fattr->[0]
      100        
47             and ($fields->{$_[0]} || 0) >= $fattr->[0])
48             {
49             # There are already fields not belonging to base classes.
50             # Looks like a possible module reload...
51 6         9 $next = $fattr->[0];
52             }
53 54         98 foreach my $f (@_) {
54 132         192 my $fno = $fields->{$f};
55              
56             # Allow the module to be reloaded so long as field positions
57             # have not changed.
58 132 100 100     318 if ($fno and $fno != $next) {
59 3         17 require Carp;
60 3 50       10 if ($fno < $fattr->[0]) {
61 3 50       10 if ($] < 5.006001) {
62 0 0       0 warn("Hides field '$f' in base class") if $^W;
63             } else {
64 3         975 warnings::warnif("Hides field '$f' in base class") ;
65             }
66             } else {
67 0         0 Carp::croak("Field name '$f' already in use");
68             }
69             }
70 132         251 $fields->{$f} = $next;
71 132 100       365 $fattr->[$next] = ($f =~ /^_/) ? PRIVATE : PUBLIC;
72 132         220 $next += 1;
73             }
74 54 50       8205 if (@$fattr > $next) {
75             # Well, we gave them the benefit of the doubt by guessing the
76             # module was reloaded, but they appear to be declaring fields
77             # in more than one place. We can't be sure (without some extra
78             # bookkeeping) that the rest of the fields will be declared or
79             # have the same positions, so punt.
80 0         0 require Carp;
81 0         0 Carp::croak ("Reloaded module must declare all fields at once");
82             }
83             }
84              
85             sub inherit {
86 0     0 0 0 require base;
87 0         0 goto &base::inherit_fields;
88             }
89              
90             sub _dump # sometimes useful for debugging
91             {
92 0     0   0 for my $pkg (sort keys %attr) {
93 0         0 print "\n$pkg";
94 0 0       0 if (@{"$pkg\::ISA"}) {
  0         0  
95 0         0 print " (", join(", ", @{"$pkg\::ISA"}), ")";
  0         0  
96             }
97 0         0 print "\n";
98 0         0 my $fields = \%{"$pkg\::FIELDS"};
  0         0  
99 0         0 for my $f (sort {$fields->{$a} <=> $fields->{$b}} keys %$fields) {
  0         0  
100 0         0 my $no = $fields->{$f};
101 0         0 print " $no: $f";
102 0         0 my $fattr = $attr{$pkg}[$no];
103 0 0       0 if (defined $fattr) {
104 0         0 my @a;
105 0 0       0 push(@a, "public") if $fattr & PUBLIC;
106 0 0       0 push(@a, "private") if $fattr & PRIVATE;
107 0 0       0 push(@a, "inherited") if $fattr & INHERITED;
108 0         0 print "\t(", join(", ", @a), ")";
109             }
110 0         0 print "\n";
111             }
112             }
113             }
114              
115             if ($] < 5.009) {
116             *new = sub {
117             my $class = shift;
118             $class = ref $class if ref $class;
119             return bless [\%{$class . "::FIELDS"}], $class;
120             }
121             } else {
122             *new = sub {
123 7     7   21480 my $class = shift;
124 7 50       27 $class = ref $class if ref $class;
125 7         4012 require Hash::Util;
126 7         13399 my $self = bless {}, $class;
127              
128             # The lock_keys() prototype won't work since we require Hash::Util :(
129 7         63 &Hash::Util::lock_keys(\%$self, _accessible_keys($class));
130 7         337 return $self;
131             }
132             }
133              
134             sub _accessible_keys {
135 10     10   25 my ($class) = @_;
136             return (
137 10         53 keys %{$class.'::FIELDS'},
138 10         17 map(_accessible_keys($_), @{$class.'::ISA'}),
  10         80  
139             );
140             }
141              
142             sub phash {
143 1 50   1 1 2600 die "Pseudo-hashes have been removed from Perl" if $] >= 5.009;
144 0           my $h;
145             my $v;
146 0 0         if (@_) {
147 0 0         if (ref $_[0] eq 'ARRAY') {
148 0           my $a = shift;
149 0           @$h{@$a} = 1 .. @$a;
150 0 0         if (@_) {
151 0           $v = shift;
152 0 0 0       unless (! @_ and ref $v eq 'ARRAY') {
153 0           require Carp;
154 0           Carp::croak ("Expected at most two array refs\n");
155             }
156             }
157             }
158             else {
159 0 0         if (@_ % 2) {
160 0           require Carp;
161 0           Carp::croak ("Odd number of elements initializing pseudo-hash\n");
162             }
163 0           my $i = 0;
164 0           @$h{grep ++$i % 2, @_} = 1 .. @_ / 2;
165 0           $i = 0;
166 0           $v = [grep $i++ % 2, @_];
167             }
168             }
169             else {
170 0           $h = {};
171 0           $v = [];
172             }
173 0           [ $h, @$v ];
174              
175             }
176              
177             1;
178              
179             __END__