File Coverage

blib/lib/Class/Fields/Fuxor.pm
Criterion Covered Total %
statement 64 72 88.8
branch 8 14 57.1
condition 10 25 40.0
subroutine 15 16 93.7
pod 6 6 100.0
total 103 133 77.4


line stmt bran cond sub pod time code
1             package Class::Fields::Fuxor;
2              
3 6     6   28151 use strict;
  6         209  
  6         243  
4 6     6   31 no strict 'refs';
  6         12  
  6         228  
5 6     6   31 use vars qw(@ISA @EXPORT $VERSION);
  6         27  
  6         413  
6              
7 6     6   6273 use Carp::Assert;
  6         8172  
  6         41  
8              
9             $VERSION = '0.06';
10              
11             require Exporter;
12             @ISA = qw(Exporter);
13              
14             @EXPORT = qw(add_fields
15             add_field_set
16             has_fields
17             get_fields
18             get_attr
19             has_attr
20             );
21              
22              
23 6     6   1144 use constant TRUE => (1==1);
  6         10  
  6         551  
24 6     6   34 use constant FALSE => !TRUE;
  6         10  
  6         290  
25 6     6   29 use constant SUCCESS => TRUE;
  6         11  
  6         392  
26 6     6   29 use constant FAILURE => !SUCCESS;
  6         10  
  6         250  
27              
28 6     6   3909 use Class::Fields::Attribs;
  6         17  
  6         477  
29              
30             =pod
31              
32             =head1 NAME
33              
34             Class::Fields::Fuxor - Low level manipuation of object data members
35              
36             =head1 SYNOPSIS
37              
38             # As functions.
39             use Class::Fields::Fuxor;
40             add_fields($class, $attrib, @fields);
41             add_field_set($class, \@fields, \@attribs);
42             has_fields($class);
43             $fields = get_fields($class);
44             $fattr = get_attr($class);
45              
46              
47             # As methods.
48             package Foo;
49             use base qw( Class::Fields::Fuxor );
50              
51             Foo->add_fields($attrib, @fields);
52             Foo->has_fields;
53             $fields = Foo->get_fields;
54             $fattr = Foo->get_attr;
55            
56              
57             =head1 DESCRIPTION
58              
59             This is a module for low level manipuation of the %FIELDS hash and its
60             accompying %fields::attr hash without actually touching them. Modules
61             like fields.pm, base.pm and public.pm make use of this module.
62              
63             %FIELDS and %fields::attr are currently used to store information
64             about the data members of classes. Since the current data inheritance
65             system, built around pseudo-hashes, is considered a bit twitchy, it is
66             wise to encapsulate and rope it off in the expectation that it will be
67             replaced with something better.
68              
69             Typically one does not want to mess with this stuff and instead uses
70             fields.pm and friends or perhaps Class::Fields.
71              
72             =cut
73              
74              
75             # The %attr hash holds the attributes of the currently assigned fields
76             # per class. The hash is indexed by class names and the hash value is
77             # an array reference. The array is indexed with the field numbers
78             # (minus one) and the values are integer bit masks (or undef). The
79             # size of the array also indicates the next field index to assign for
80             # additional fields in this class.
81             #
82             # BTW %attr is part of fields for legacy reasons. We alias it here to make
83             # life easier.
84 6     6   30 use vars qw(%attr);
  6         9  
  6         4365  
85             *attr = \%fields::attr;
86              
87             =pod
88              
89             =over 4
90              
91             =item B
92              
93             add_fields($class, $attrib, @fields);
94              
95             Adds a bunch of @fields to the given $class using the given $attrib.
96             For example:
97              
98             # Add the public fields 'this' and 'that' to the class Foo.
99             use Class::Fields::Attribs;
100             add_fields('Foo', PUBLIC, qw(this that));
101              
102             $attrib is built from the constants in Class::Fields::Attribs
103              
104             =cut
105              
106             sub add_fields {
107 22     22 1 10562 my($proto, $attrib, @fields) = @_;
108 22         330 add_field_set($proto, \@fields, [($attrib) x @fields]);
109             }
110              
111             =pod
112              
113             =item B
114              
115             add_field_set($class, \@fields, \@attribs);
116              
117             Functionally similar to add_fields(), excepting that it can add a
118             group of fields with different attributes all at once. This is
119             necessary for the proper functioning of fields.pm.
120              
121             Each element in @fields matches up with one in @attribs. Obviously,
122             the two arrays must be the same size.
123              
124             =cut
125              
126             sub add_field_set {
127             # Read the first two parameters. The rest are field names.
128 22     22 1 42 my($proto, $new_fields, $new_attribs) = @_;
129              
130 22         85 assert(@$new_fields == @$new_attribs) if DEBUG;
131              
132             # Quick bail out if nothing is to be added.
133 22 100       137 return SUCCESS unless @$new_fields;
134              
135 20   33     115 my($class) = ref $proto || $proto;
136            
137 20         53 my $fields = get_fields($class);
138 20         62 my $fattr = get_attr($class);
139 20         33 my $next_fno = @$fattr;
140              
141              
142             # Check for existing fields not belonging to base classes.
143             # Indicates a possible module reload.
144 20 50 50     160 if ($next_fno > $fattr->[0]
      66        
145             and ($fields->{$new_fields->[0]} || 0) >= $fattr->[0])
146             {
147             # Reset the next pointer to let the reload work.
148 0         0 $next_fno = $fattr->[0];
149             }
150              
151             # Go through the fields and attach attributes.
152 20         25 foreach my $idx (0..$#{$new_fields}) {
  20         58  
153 48         69 my $f = $new_fields->[$idx];
154 48         60 my $attrib = $new_attribs->[$idx];
155 48         61 my $fno = $fields->{$f};
156              
157             # Allow the module to be reloaded so long as field positions
158             # have not changed.
159 48 50 33     113 if ($fno and $fno != $next_fno) {
160 0         0 require Carp;
161 0 0       0 if ($fno < $fattr->[0]) {
162 0 0       0 Carp::carp("Hides field '$f' in base class") if $^W;
163             } else {
164 0         0 Carp::croak("Field name '$f' already in use");
165             }
166             }
167 48         100 $fields->{$f} = $next_fno;
168 48         80 $fattr->[$next_fno] = $attrib;
169 48         2906 $next_fno++;
170             }
171             }
172              
173              
174             =item B
175              
176             has_fields($class);
177              
178             A simple check to see if the given $class has a %FIELDS hash defined.
179             A simple test like (defined %{"$class\::FIELDS"}) will sometimes
180             produce typo warnings because it would create the hash if it was not
181             present before.
182              
183             =cut
184              
185             sub has_fields {
186 15     15 1 26 my($proto) = shift;
187 15   33     73 my($class) = ref $proto || $proto;
188 15         66 my $fglob;
189 15 100 100     17 return ($fglob = ${"$class\::"}{"FIELDS"} and *$fglob{HASH}) ? TRUE
190             : FALSE;
191             }
192              
193             =item B
194              
195             has_attr($class);
196              
197             A simple check to see if the given $class has attributes.
198              
199             =cut
200              
201             sub has_attr {
202 0     0 1 0 my($proto) = shift;
203 0   0     0 my($class) = ref $proto || $proto;
204 0         0 return exists $attr{$class};
205             }
206              
207             =item B
208              
209             $fattr = get_attr($class);
210              
211             Get's the field attribute array for the given $class. This is roughly
212             equivalent to $fields::attr{$class} but we put a nice wrapper around
213             it for compatibility and readability.
214              
215             $fattr is an array reference containing the attributes of the fields
216             in the given $class. Each entry in $fattr corresponds to the position
217             indicated by the $class's %FIELDS has. For example:
218              
219             package Foo;
220             use fields qw(this _that);
221              
222             $fattr = get_attr('Foo');
223              
224             # Get the attributes for '_that' in the class 'Foo'.
225             $that_attribs = print $fattr->[$Foo::FIELDS->{_that}];
226              
227             When possible, one should avoid using this function since it exposes
228             more implementation detail than I'd like. Class::Fields
229             should provide most of the functionality you'll need.
230              
231             =cut
232              
233             sub get_attr {
234 45     45 1 65 my($proto) = shift;
235 45   33     292 my($class) = ref $proto || $proto;
236 45 100       125 unless ( defined $attr{$class} ) {
237 9         27 $attr{$class} = [1];
238             }
239 45         109 return $attr{$class};
240             }
241              
242             =pod
243              
244             =item B
245              
246             $fields = get_fields($class);
247              
248             Gets a reference to the %FIELDS hash for the given $class. It will
249             autogenerate a %FIELDS hash if one doesn't already exist. If you
250             don't want this behavior, be sure to check beforehand with
251             has_fields().
252              
253             When possible, one should avoid using this function since it exposes
254             more implementation detail than I'd like. Class::Fields
255             should provide most of the functionality you'll need.
256              
257             =cut
258              
259             sub get_fields {
260 48     48 1 76 my($proto) = shift;
261 48   33     185 my($class) = ref $proto || $proto;
262              
263             # Shut up a possible typo warning.
264 48         64 () = \%{$class.'::FIELDS'};
  48         233  
265              
266 48         68 return \%{$class.'::FIELDS'};
  48         171  
267             }
268              
269             =pod
270              
271             =back
272              
273             =head1 AUTHOR
274              
275             Michael G Schwern based heavily on code liberated
276             from the original fields.pm and base.pm.
277              
278              
279             =head1 SEE ALSO
280              
281             L, L, L, L, L,
282             L, L
283              
284             =cut
285              
286             return 'Maybe we should have stopped with Smalltalk.';