File Coverage

blib/lib/MLDBM.pm
Criterion Covered Total %
statement 67 102 65.6
branch 35 60 58.3
condition 9 20 45.0
subroutine 15 29 51.7
pod 5 6 83.3
total 131 217 60.3


line stmt bran cond sub pod time code
1             #
2             # MLDBM.pm
3             #
4             # store multi-level hash structure in single level tied hash (read DBM)
5             #
6             # Documentation at the __END__
7             #
8             # Gurusamy Sarathy
9             # Raphael Manfredi
10             #
11            
12             require 5.005;
13 5     5   11737 use strict;
  5         12  
  5         374  
14            
15             ####################################################################
16             package MLDBM::Serializer; ## deferred
17            
18             $MLDBM::Serializer::VERSION = $MLDBM::Serializer::VERSION = '2.05';
19 5     5   28 use Carp;
  5         9  
  5         2872  
20            
21             #
22             # The serialization interface comprises of just three methods:
23             # new(), serialize() and deserialize(). Only the last two are
24             # _required_ to be implemented by any MLDBM serialization wrapper.
25             #
26            
27 3     3   16 sub new { bless {}, shift };
28            
29 0     0   0 sub serialize { confess "deferred" };
30            
31 0     0   0 sub deserialize { confess "deferred" };
32            
33            
34             #
35             # Attributes:
36             #
37             # dumpmeth:
38             # the preferred dumping method.
39             #
40             # removetaint:
41             # untainting flag; when true, data will be untainted after
42             # extraction from the database.
43             #
44             # key:
45             # the magic string used to recognize non-natively stored data.
46             #
47             # Attribute access methods:
48             #
49             # These defaults allow readonly access. Sub-class may override
50             # them to allow write access if any of these attributes
51             # makes sense for it.
52             #
53            
54             sub DumpMeth {
55 0     0   0 my $s = shift;
56 0 0       0 confess "can't set dumpmeth with " . ref($s) if @_;
57 0         0 $s->_attrib('dumpmeth');
58             }
59            
60             sub RemoveTaint {
61 0     0   0 my $s = shift;
62 0 0       0 confess "can't set untaint with " . ref($s) if @_;
63 0         0 $s->_attrib('removetaint');
64             }
65            
66             sub Key {
67 0     0   0 my $s = shift;
68 0 0       0 confess "can't set key with " . ref($s) if @_;
69 0         0 $s->_attrib('key');
70             }
71            
72             sub _attrib {
73 6     6   12 my ($s, $a, $v) = @_;
74 6 50 33     39 if (ref $s and @_ > 2) {
75 6         15 $s->{$a} = $v;
76 6         32 return $s;
77             }
78 0         0 $s->{$a};
79             }
80            
81             ####################################################################
82             package MLDBM;
83            
84             $MLDBM::VERSION = $MLDBM::VERSION = '2.05';
85            
86             require Tie::Hash;
87             @MLDBM::ISA = 'Tie::Hash';
88            
89 5     5   31 use Carp;
  5         13  
  5         6857  
90            
91             #
92             # the DB package to use (we default to SDBM since it comes with perl)
93             # you might want to change this default to something more efficient
94             # like DB_File (you can always override it in the use list)
95             #
96             $MLDBM::UseDB = "SDBM_File" unless $MLDBM::UseDB;
97             $MLDBM::Serializer = 'Data::Dumper' unless $MLDBM::Serializer;
98             $MLDBM::Key = '$MlDbM' unless $MLDBM::Key;
99             $MLDBM::DumpMeth = "" unless $MLDBM::DumpMeth;
100             $MLDBM::RemoveTaint = 0 unless $MLDBM::RemoveTaint;
101            
102             #
103             # A private way to load packages at runtime.
104             my $loadpack = sub {
105             my $pack = shift;
106             $pack =~ s|::|/|g;
107             $pack .= ".pm";
108             eval { require $pack };
109             if ($@) {
110             carp "MLDBM error: " .
111             "Please make sure $pack is a properly installed package.\n" .
112             "\tPerl says: \"$@\"";
113             return undef;
114             }
115             1;
116             };
117            
118            
119             #
120             # TIEHASH interface methods
121             #
122             sub TIEHASH {
123 3     3   5989 my $c = shift;
124 3         10 my $s = bless {}, $c;
125            
126             #
127             # Create the right serializer object.
128 3         7 my $szr = $MLDBM::Serializer;
129 3 50       13 unless (ref $szr) {
130 3 50       18 $szr = "MLDBM::Serializer::$szr" # allow convenient short names
131             unless $szr =~ /^MLDBM::Serializer::/;
132 3 50       13 $loadpack->($szr) or return undef;
133 3         24 $szr = $szr->new($MLDBM::DumpMeth,
134             $MLDBM::RemoveTaint,
135             $MLDBM::Key);
136             }
137 3         12 $s->Serializer($szr);
138            
139             #
140             # Create the right TIEHASH object.
141 3         5 my $db = $MLDBM::UseDB;
142 3 50       12 unless (ref $db) {
143 3 50       6 $loadpack->($db) or return undef;
144 3 50 0     163 $db = $db->TIEHASH(@_)
145             or carp "MLDBM error: Second level tie failed, \"$!\""
146             and return undef;
147             }
148 3         15 $s->UseDB($db);
149            
150 3         11 return $s;
151             }
152            
153             sub FETCH {
154 19     19   2223 my ($s, $k) = @_;
155 19         76 my $ret = $s->{DB}->FETCH($k);
156 19         68 $s->{SR}->deserialize($ret);
157             }
158            
159             sub STORE {
160 19     19   497 my ($s, $k, $v) = @_;
161 19         83 $v = $s->{SR}->serialize($v);
162 19         140495 $s->{DB}->STORE($k, $v);
163             }
164            
165 0     0   0 sub DELETE { my $s = shift; $s->{DB}->DELETE(@_); }
  0         0  
166 0     0   0 sub FIRSTKEY { my $s = shift; $s->{DB}->FIRSTKEY(@_); }
  0         0  
167 0     0   0 sub NEXTKEY { my $s = shift; $s->{DB}->NEXTKEY(@_); }
  0         0  
168 0     0   0 sub EXISTS { my $s = shift; $s->{DB}->EXISTS(@_); }
  0         0  
169 0     0   0 sub CLEAR { my $s = shift; $s->{DB}->CLEAR(@_); }
  0         0  
170            
171 0     0 0 0 sub new { &TIEHASH }
172            
173             #
174             # delegate messages to the underlying DBM
175             #
176             sub AUTOLOAD {
177 0 0   0   0 return if $MLDBM::AUTOLOAD =~ /::DESTROY$/;
178 0         0 my $s = shift;
179 0 0       0 if (ref $s) { # twas a method call
180 0         0 my $dbname = ref($s->{DB});
181             # permit inheritance
182 0         0 $MLDBM::AUTOLOAD =~ s/^.*::([^:]+)$/$dbname\:\:$1/;
183 0         0 $s->{DB}->$MLDBM::AUTOLOAD(@_);
184             }
185             }
186            
187             #
188             # delegate messages to the underlying Serializer
189             #
190 1     1 1 5 sub DumpMeth { my $s = shift; $s->{SR}->DumpMeth(@_); }
  1         5  
191 0     0 1 0 sub RemoveTaint { my $s = shift; $s->{SR}->RemoveTaint(@_); }
  0         0  
192 0     0 1 0 sub Key { my $s = shift; $s->{SR}->Key(@_); }
  0         0  
193            
194             #
195             # get/set the DB object
196             #
197 3 50   3 1 5 sub UseDB { my $s = shift; @_ ? ($s->{DB} = shift) : $s->{DB}; }
  3         14  
198            
199             #
200             # get/set the Serializer object
201             #
202 3 50   3 1 5 sub Serializer { my $s = shift; @_ ? ($s->{SR} = shift) : $s->{SR}; }
  3         23  
203            
204             #
205             # stuff to do at 'use' time
206             #
207             sub import {
208 6     6   12184 my ($pack, $dbpack, $szr, $dumpmeth, $removetaint, $key) = @_;
209 6 100 66     57 $MLDBM::UseDB = $dbpack if defined $dbpack and $dbpack;
210 6 100 66     36 $MLDBM::Serializer = $szr if defined $szr and $szr;
211             # undocumented, may change!
212 6 50       61 $MLDBM::DumpMeth = $dumpmeth if defined $dumpmeth;
213 6 50       39 $MLDBM::RemoveTaint = $removetaint if defined $removetaint;
214 6 50 33     3328 $MLDBM::Key = $key if defined $key and $key;
215             }
216            
217             # helper subroutine for tests to compare to arbitrary data structures
218             # for equivalency
219             sub _compare {
220 5     5   40 use vars qw(%compared);
  5         10  
  5         2601  
221 9     9   127 local %compared;
222 9         30 return _cmp(@_);
223             }
224            
225             sub _cmp {
226 110     110   151 my($a, $b) = @_;
227            
228             # catch circular loops
229 110 100       1065 return(1) if $compared{$a.'&*&*&*&*&*'.$b}++;
230             # print "$a $b\n";
231             # print &Data::Dumper::Dumper($a, $b);
232            
233 73 100 66     372 if(ref($a) and ref($a) eq ref($b)) {
    50 33        
234 51 100       91 if(eval { @$a }) {
  51 100       203  
    50          
235             # print "HERE ".@$a." ".@$b."\n";
236 24 50       55 @$a == @$b or return 0;
237             # print @$a, ' ', @$b, "\n";
238             # print "HERE2\n";
239            
240 24         52 for(0..@$a-1) {
241 48 100       109 &_cmp($a->[$_], $b->[$_]) or return 0;
242             }
243 27         132 } elsif(eval { %$a }) {
244 14 100       49 keys %$a == keys %$b or return 0;
245 13         94 for (keys %$a) {
246 40 100       93 &_cmp($a->{$_}, $b->{$_}) or return 0;
247             }
248 13         32 } elsif(eval { $$a }) {
249 13 100       33 &_cmp($$a, $$b) or return 0;
250             } else {
251 0         0 die("data $a $b not handled");
252             }
253 44         158 return 1;
254             } elsif(! ref($a) and ! ref($b)) {
255 22         95 return ($a eq $b);
256             } else {
257 0           return 0;
258             }
259            
260             }
261            
262             1;
263            
264             __END__