File Coverage

blib/lib/Apache/FakeTable.pm
Criterion Covered Total %
statement 81 82 98.7
branch 21 26 80.7
condition 13 21 61.9
subroutine 22 22 100.0
pod 8 8 100.0
total 145 159 91.1


line stmt bran cond sub pod time code
1             package Apache::FakeTable;
2 1     1   24407 use strict;
  1         2  
  1         34  
3 1     1   6 use vars qw($VERSION);
  1         1  
  1         579  
4             $VERSION = '0.06';
5              
6             =head1 Name
7              
8             Apache::FakeTable - Pure Perl implementation of the Apache::Table interface
9              
10             =head1 Synopsis
11              
12             use Apache::FakeTable;
13              
14             my $table = Apache::FakeTable->new($r);
15              
16             $table->set(From => 'david@example.com');
17              
18             $table->add(Cookie => 'One Cookie');
19             $table->add(Cookie => 'Another Cookie');
20              
21             while(my($key, $val) = each %$table) {
22             print "$key: $val\n";
23             }
24              
25             =head1 Description
26              
27             This class emulates the behavior of the L class, and is
28             designed to behave exactly like Apache::Table. This means that all keys are
29             case-insensitive and may have multiple values. As a drop-in substitute for
30             Apache::Table, you should be able to use it exactly like Apache::Table.
31              
32             You can treat an Apache::FakeTable object much like any other hash. However,
33             like Apache Table, those keys that contain multiple values will trigger
34             slightly different behavior than a traditional hash. The variations in
35             behavior are as follows:
36              
37             =over
38              
39             =item keys
40              
41             Will return the same key multiple times, once for each value stored for that
42             key.
43              
44             =item values
45              
46             Will return the first value multiple times, once for each value stored for a
47             given key. It'd be nice if it returned all the values for a given key, instead
48             of the first value C<*> the number of values, but that's not the way
49             Apache::Table works, and I'm not sure I'd know how to implement it even if it
50             did!
51              
52             =item each
53              
54             Will return the same key multiple times, pairing it with each of its values
55             in turn.
56              
57             =back
58              
59             Otherwise, things should be quite hash-like, particularly when a key has only
60             a single value.
61              
62             =head1 Interface
63              
64             =head3 new()
65              
66             my $table = Apache::FakeTable->new($r);
67             $table = Apache::FakeTable->new($r, $initial_size);
68              
69             Returns a new C object. An L object is required as
70             the first argument. An optional second argument sets the initial size of the
71             table for storing values.
72              
73             =cut
74              
75             sub new {
76             # We actually ignore the optional initial size argument.
77 2     2 1 786 my ($class, $r) = @_;
78 2 100       12 unless (UNIVERSAL::isa($r, 'Apache')) {
79 1         11 require Carp;
80 1         168 Carp::croak("Usage: " . __PACKAGE__ . "::new(pclass, r, nalloc=10)");
81             }
82 1         3 my $self = {};
83 1         2 tie %{$self}, 'Apache::FakeTableHash';
  1         8  
84 1   33     10 return bless $self, ref $class || $class;
85             }
86              
87             =head3 get()
88              
89             my $value = $table->get($key);
90             my @values = $table->get($key);
91             my $value = $table->{$key};
92              
93             Gets the value stored for a given key in the table. If a key has multiple
94             values, all will be returned when C is called in an array context, and
95             only the first value when it is called in a scalar context.
96              
97             =cut
98              
99             sub get {
100 7     7 1 12 tied(%{shift()})->_get(@_);
  7         21  
101             }
102              
103             =head3 set()
104              
105             $table->set($key, $value);
106             $table->{$key} = $value;
107              
108             Takes key and value arguments and sets the value for that key. Previous values
109             for that key will be discarded. The value must be a string, or C will
110             turn it into one. A value of C will be converted to the null string
111             ('') a warning will be issued if warnings are enabled.
112              
113             =cut
114              
115             sub set {
116 2     2 1 561 my ($self, $header, $value) = @_;
117             # Issue a warning if the value is undefined.
118 2 50 66     12 if (! defined $value and $^W) {
119 1         5 require Carp;
120 1         129 Carp::carp('Use of uninitialized value in null operation');
121 1         399 $value = '';
122             }
123 2         8 $self->{$header} = $value;
124             }
125              
126             =head3 unset()
127              
128             $table->unset($key);
129             delete $table->{$key};
130              
131             Takes a single key argument and deletes that key from the table, so that none
132             of its values will be in the table any longer.
133              
134             =cut
135              
136             sub unset {
137 1     1 1 2 my $self = shift;
138 1         5 delete $self->{shift()};
139             }
140              
141             =head3 clear()
142              
143             $table->clear;
144             %$table = ();
145              
146             Clears the table of all values.
147              
148             =cut
149              
150             sub clear {
151 1     1 1 1 %{shift()} = ();
  1         4  
152             }
153              
154             =head3 add()
155              
156             $table->add($key, $value);
157              
158             Adds a new value to the table. This method is the sole interface for adding
159             mutiple values for a single key.
160              
161             =cut
162              
163             sub add {
164             # Issue a warning if the value is undefined.
165 5 100 66 5 1 23 if (! defined $_[2] and $^W) {
166 1         6 require Carp;
167 1         103 Carp::carp('Use of uninitialized value in null operation');
168 1         444 splice @_, 2, 1, '';
169             }
170 5         8 tied(%{shift()})->_add(@_);
  5         18  
171             }
172              
173             =head3 merge()
174              
175             $table->merge($key, $value);
176              
177             Merges a new value with an existing value by appending the new value to the
178             existing. The result is a string with the old value separated from the new by
179             a comma and a space. If C<$key> contains multiple values, then only the first
180             value will be used before appending the new value, and the remaining values
181             will be discarded.
182              
183             =cut
184              
185             sub merge {
186 2     2 1 5 my ($self, $key, $value) = @_;
187 2 50       8 if (exists $self->{$key}) {
188 2         11 $self->{$key} .= ', ' . $value;
189             } else {
190 0         0 $self->{$key} = $value;
191             }
192             }
193              
194             =head3 do()
195              
196             $table->do($coderef);
197              
198             Pass a code reference to this method to have it iterate over all of the
199             key/value pairs in the table. Keys with multiple values will trigger the
200             execution of the code reference multiple times, once for each value. The code
201             reference should expect two arguments: a key and a value. Iteration terminates
202             when the code reference returns false, so be sure to have it return a true
203             value if you want it to iterate over every value in the table.
204              
205             =cut
206              
207             sub do {
208 2     2 1 17 my ($self, $code) = @_;
209 2         5 while (my ($k, $val) = each %$self) {
210 3 50       9 for my $v (ref $val ? @$val : $val) {
211 3 100       8 return unless $code->($k => $v);
212             }
213             }
214             }
215              
216             1;
217              
218             ##############################################################################
219             # This is the implementation of the case-insensitive hash that each table
220             # object is based on.
221             package
222             Apache::FakeTableHash;
223 1     1   7 use strict;
  1         5  
  1         694  
224             my %curr_keys;
225              
226             sub TIEHASH {
227 1     1   2 my $class = shift;
228 1   33     12 return bless {}, ref $class || $class;
229             }
230              
231             # Values are always stored as strings in an array.
232             sub STORE {
233 8     8   229 my ($self, $key, $value) = @_;
234             # Issue a warning if the value is undefined.
235 8 50 66     24 if (! defined $value and $^W) {
236 1         4 require Carp;
237 1         86 Carp::carp('Use of uninitialized value in null operation');
238 1         405 $value = '';
239             }
240 8         62 $self->{lc $key} = [ $key => ["$value"] ];
241             }
242              
243             sub _add {
244 5     5   10 my ($self, $key, $value) = @_;
245 5         10 my $ckey = lc $key;
246 5 100       13 if (exists $self->{$ckey}) {
247             # Add it to the array,
248 4         6 push @{$self->{$ckey}[1]}, "$value";
  4         30  
249             } else {
250             # It's a simple assignment.
251 1         9 $self->{$ckey} = [ $key => ["$value"] ];
252             }
253             }
254              
255             sub DELETE {
256 2     2   5 my ($self, $key) = @_;
257 2         5 my $ret = delete $self->{lc $key};
258 2         14 return $ret->[1][0];
259             }
260              
261             sub FETCH {
262 26     26   234 my $self = shift;
263 26         35 my $key = lc shift;
264             # Grab the values first so that we don't autovivicate the key.
265 26 100       67 my $val = $self->{$key} or return;
266             # If the key is the current key, return the value that's next. Otherwise,
267             # return the first value.
268 24 100 100     204 return $curr_keys{$self} && $curr_keys{$self}->[0] eq $key
269             ? $val->[1][$curr_keys{$self}->[1]]
270             : $val->[1][0];
271             }
272              
273             sub _get {
274 7     7   13 my ($self, $key) = @_;
275 7         10 my $ckey = lc $key;
276             # Prevent autovivication.
277 7 50       21 return unless exists $self->{$ckey};
278             # Return the array in an array context and just the first value in a
279             # scalar context.
280 7 100       585 return wantarray ? @{$self->{$ckey}[1]} : $self->{$ckey}[1][0];
  1         13  
281             }
282              
283             sub CLEAR {
284 1     1   2 %{shift()} = ();
  1         6  
285             }
286              
287             sub EXISTS {
288 4     4   7 my ($self, $key)= @_;
289 4         16 return exists $self->{lc $key};
290             }
291              
292             my $keyer = sub {
293             my $self = shift;
294             # Get the next key via perl's iterator.
295             my $key = each %$self;
296             # If there's no key, clear out our tracking of the current key and return.
297             delete $curr_keys{$self}, return unless defined $key;
298             # Cache the key and array index 0 for NEXTKEY and FETCH to use.
299             $curr_keys{$self} = [ $key => 0 ];
300             return $self->{$key}[0];
301             };
302              
303             sub FIRSTKEY {
304 5     5   6 my $self = shift;
305             # Reset perl's iterator and then get the key.
306 5         6 keys %$self;
307 5         13 $self->$keyer();
308             }
309              
310             sub NEXTKEY {
311 8     8   2650 my ($self, $last_key) = @_;
312             # Return the last key if there are more values to be fetched for it.
313 8         9 my $ckey = lc $last_key;
314 8         57 return $last_key
315             if $curr_keys{$self}->[0] eq $ckey
316 8 100 66     34 && ++$curr_keys{$self}->[1] <= $#{$self->{$ckey}[1]};
317              
318             # Otherwise, just get the next key.
319 4         7 $self->$keyer();
320             }
321              
322             # Just be sure to clear out the current key.
323 1     1   405 sub DESTROY { delete $curr_keys{shift()}; }
324              
325             1;
326             __END__