File Coverage

blib/lib/APR/HTTP/Headers/Compat/MagicHash.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package APR::HTTP::Headers::Compat::MagicHash;
2              
3 2     2   9 use strict;
  2         4  
  2         59  
4 2     2   10 use warnings;
  2         3  
  2         215  
5              
6 2     2   1177 use APR::HTTP::Headers::Compat::MagicArray;
  2         6  
  2         52  
7 2     2   2587 use APR::Table;
  0            
  0            
8             use Carp qw( confess );
9             use HTTP::Headers;
10             use Storable qw( dclone );
11              
12             =head1 NAME
13              
14             APR::HTTP::Headers::Compat::MagicHash - Tie a hash to an APR::Table
15              
16             =cut
17              
18             sub TIEHASH {
19             my ( $class, $table, %args ) = @_;
20              
21             my $self = bless { table => $table }, $class;
22              
23             while ( my ( $k, $v ) = each %args ) {
24             $self->STORE( $k, $v );
25             }
26              
27             return $self;
28             }
29              
30             =head2 C<< table >>
31              
32             Get the table object.
33              
34             =cut
35              
36             sub table { shift->{table} }
37              
38             sub _nicename {
39             my ( $self, @names ) = @_;
40              
41             my $hdr = HTTP::Headers->new( map { $_ => 1 } @names );
42             my @nice = $hdr->header_field_names;
43             my %lookup = map { lc $_ => $_ } @nice;
44             my @r = map { $lookup{$_} or confess "No mapping for $_" } @names;
45             return wantarray ? @r : $r[0];
46             }
47              
48             sub _nicefor {
49             my ( $self, $name ) = @_;
50             return $1 if $name =~ /^:(.+)/;
51             return $self->{namemap}{$name} ||= $self->_nicename( $name );
52             }
53              
54             sub FETCH {
55             my ( $self, $key ) = @_;
56             my $nkey = $self->_nicefor( $key );
57             my @vals = $self->table->get( $nkey );
58             return $vals[0] if @vals < 2;
59             tie my @r, 'APR::HTTP::Headers::Compat::MagicArray', $nkey, $self,
60             @vals;
61             return \@r;
62             # return $self->{hash}{$nkey};
63             }
64              
65             sub STORE {
66             my ( $self, $key, $value ) = @_;
67             my $nkey = $self->_nicefor( $key );
68             $self->{rmap}{$nkey} = $key;
69              
70             my $table = $self->table;
71             my @vals = 'ARRAY' eq ref $value ? @$value : $value;
72             $table->set( $nkey, shift @vals );
73             $table->add( $nkey, $_ ) for @vals;
74             $self->_changed;
75             }
76              
77             sub DELETE {
78             my ( $self, $key ) = @_;
79             my $nkey = $self->_nicefor( $key );
80             my $rv = $self->FETCH( $key );
81             $self->table->unset( $nkey );
82             $self->_changed;
83             return $rv;
84             }
85              
86             sub CLEAR {
87             my ( $self ) = @_;
88             $self->table->clear;
89             $self->_changed;
90             }
91              
92             sub EXISTS {
93             my ( $self, $key ) = @_;
94             my %fld = map { $_ => 1 } $self->_keys;
95             return exists $fld{$key};
96             }
97              
98             sub _mkkeys {
99             my $self = shift;
100             my @k = ();
101             my $rm = $self->{rmap};
102             my %seen = ();
103             $self->table->do(
104             sub {
105             my ( $k, $v ) = @_;
106             my $kk = defined $rm->{$k} ? $rm->{$k} : lc $k;
107             push @k, $kk unless $seen{$kk}++;
108             } );
109             return \@k;
110             }
111              
112             sub _keys {
113             my $self = shift;
114             return @{ $self->{keys} ||= $self->_mkkeys };
115             }
116              
117             sub _changed {
118             my $self = shift;
119             delete $self->{keys};
120             }
121              
122             sub FIRSTKEY {
123             my ( $self ) = @_;
124             $self->{pos} = 0;
125             return ( $self->_keys )[0];
126             }
127              
128             sub NEXTKEY {
129             my ( $self, $lastkey ) = @_;
130             my @keys = $self->_keys;
131             unless ( $keys[ $self->{pos} ] eq $lastkey ) {
132             my $nk = scalar @{ $self->{keys} };
133             for my $i ( 0 .. $nk ) {
134             if ( $keys[$i] eq $lastkey ) {
135             $self->{pos} = $i;
136             last;
137             }
138             }
139             }
140             return $keys[ ++$self->{pos} ];
141             }
142              
143             sub SCALAR {
144             my ( $self ) = @_;
145             return scalar $self->_keys;
146             }
147              
148             sub DESTROY {
149             my ( $self ) = @_;
150             # use Data::Dumper;
151             # print STDERR "# ", Dumper($self);
152             # print STDERR "# <<<\n";
153             # $self->table->do(
154             # sub {
155             # my ( $k, $v ) = @_;
156             # print STDERR "# $k => $v\n";
157             # } );
158             # print STDERR "# >>>\n";
159             }
160              
161             sub UNTIE { }
162              
163             1;
164              
165             # vim:ts=2:sw=2:sts=2:et:ft=perl