File Coverage

blib/lib/Persistent/Hash.pm
Criterion Covered Total %
statement 120 299 40.1
branch 22 184 11.9
condition 0 12 0.0
subroutine 27 45 60.0
pod 0 13 0.0
total 169 553 30.5


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2             package Persistent::Hash;
3              
4 9     9   39330 use strict;
  9         21  
  9         328  
5 9     9   44 use Carp qw(croak);
  9         17  
  9         407  
6 9     9   8788 use Data::Dumper;
  9         134879  
  9         749  
7              
8 9     9   83 use vars qw($VERSION);
  9         19  
  9         551  
9             $VERSION = '0.5';
10              
11 9     9   8304 use Persistent::Hash::Dumper;
  9         25  
  9         447  
12              
13 9     9   56 use constant DEBUG_LEVEL => 0;
  9         13  
  9         637  
14              
15 9     9   50 use constant PROJECT => 'persistent-hash';
  9         16  
  9         758  
16              
17 9     9   44 use constant INFO_TABLE => 'object_info';
  9         15  
  9         395  
18              
19 9     9   42 use constant INDEX_TABLE => 'object_index';
  9         16  
  9         585  
20 9     9   55 use constant INDEX_FIELDS => [];
  9         15  
  9         458  
21              
22 9     9   43 use constant DATA_TABLE => 'object_data';
  9         16  
  9         330  
23 9     9   40 use constant DATA_FIELDS => [];
  9         21  
  9         377  
24              
25 9     9   49 use constant STRICT_FIELDS => 1;
  9         13  
  9         372  
26 9     9   43 use constant STORAGE_MODULE => 'Persistent::Hash::Storage::MySQL';
  9         18  
  9         332  
27              
28 9     9   43 use constant STORABLE => 0;
  9         14  
  9         399  
29 9     9   60 use constant LOAD_ON_DEMAND => 1;
  9         36  
  9         417  
30 9     9   45 use constant SAVE_ONLY_IF_DIRTY => 0;
  9         12  
  9         56681  
31              
32             sub new
33             {
34 3     3 0 3614 my $classname = shift;
35 3         30 return $classname->_instanciate(@_);
36             }
37              
38             sub _instanciate
39             {
40 3     3   6 my($classname, $args) = @_;
41              
42 3         6 my $self;
43             #Create the untied hash
44              
45             #Tie the hash
46 3         24 tie %$self, $classname;
47 3         8 bless $self, $classname;
48              
49 3         6 my $untied_self = tied %$self;
50 3         5 $untied_self->{_data_dirty} = 1;
51 3         5 $untied_self->{_index_dirty} = 1;
52            
53 3         13 return $self;
54             }
55              
56             sub load
57             {
58 0     0 0 0 my ($classname, $id) = @_;
59              
60 0 0       0 croak "Attempt to call load() using a function call" if not defined $classname;
61 0 0       0 croak "No id passed to load()" if not defined $id;
62 0 0       0 croak "Argument to load() is not numeric" if not $id =~ /^[0-9]+$/;
63            
64 0         0 my $self = $classname->_instanciate();
65 0 0       0 my $untied_self = tied %$self if tied %$self;
66 0 0       0 die "Constructor error" if not defined $untied_self;
67              
68 0         0 my $storage_module = $untied_self->_PreloadStorageModule();
69              
70 0         0 my $object_info = $storage_module->LoadObjectInfo($classname, $id);
71 0 0       0 if(not defined $object_info)
72             {
73 0         0 print STDERR "Could not load object id $id";
74 0         0 return undef;
75             }
76            
77 0         0 $untied_self->{_object_id} = $id;
78 0         0 $untied_self->{_object_type} = $object_info->{type};
79 0         0 $untied_self->{_time_created} = $object_info->{time_created};
80 0         0 $untied_self->{_time_modified} = $object_info->{time_modified};
81              
82 0 0       0 if(not $self->LOAD_ON_DEMAND())
83             {
84 0         0 $untied_self->_Initialize();
85             }
86              
87 0         0 return $self;
88             }
89              
90             sub Id
91             {
92 0     0 0 0 my $self = shift;
93 0 0       0 croak "No self reference!" if not defined $self;
94              
95 0 0       0 $self = tied %$self if tied %$self;
96 0 0       0 croak "Wrong object side!" if not defined $self;
97              
98 0         0 return $self->{_object_id};
99             }
100              
101             sub Type
102             {
103 0     0 0 0 my $self = shift;
104 0 0       0 croak "No self reference!" if not defined $self;
105              
106 0         0 return $self->PackageToType();
107             }
108              
109             sub TimeCreated
110             {
111 0     0 0 0 my $self = shift;
112 0 0       0 croak "No self reference!" if not defined $self;
113              
114 0 0       0 my $untied_self = tied %$self if tied %$self;
115 0 0       0 croak "Wrong object side!" if not defined $untied_self;
116              
117 0         0 return $untied_self->{_time_created};
118             }
119              
120             sub TimeModified
121             {
122 0     0 0 0 my $self = shift;
123 0 0       0 croak "No self reference!" if not defined $self;
124              
125 0 0       0 my $untied_self = tied %$self if tied %$self;
126 0 0       0 croak "Wrong object side!" if not defined $untied_self;
127              
128 0         0 return $untied_self->{_time_modified};
129             }
130             sub PackageToType
131             {
132 3     3 0 6 my $self = shift;
133 3 50       11 croak "No self reference!" if not defined $self;
134              
135 3 50       12 my $type = ref($self) ? ref($self) : $self;
136 3         19 $type =~ s/::/_/g;
137              
138 3         23 $type = $self->PROJECT()."/".$type;
139              
140 3         9 return $type;
141             }
142              
143             sub Save
144             {
145 0     0 0 0 my $self = shift;
146 0 0       0 croak "No self reference!" if not defined $self;
147              
148 0 0       0 my $untied_self = tied %$self if tied %$self;
149 0 0       0 croak "Wrong object side!" if not defined $untied_self;
150              
151 0 0       0 return undef if !$untied_self->STORABLE();
152 0 0 0     0 return undef if !$untied_self->_IsDirty() && $untied_self->SAVE_ONLY_IF_DIRTY();
153              
154 0         0 my $storage_module = $untied_self->_PreloadStorageModule();
155              
156 0 0       0 if(!defined $untied_self->{_object_id})
157             {
158 0         0 my $object_id = $storage_module->InsertObject($self);
159 0 0       0 croak "Object insertion failed!" if not defined $object_id;
160              
161 0         0 $untied_self->{_object_id} = $object_id;
162 0         0 $untied_self->{_data_dirty} = 0;
163 0         0 $untied_self->{_index_dirty} = 0;
164              
165 0         0 return $object_id;
166             }
167             else
168             {
169 0         0 my $object_id = $storage_module->UpdateObject($self);
170 0 0       0 croak "Object update failed!" if not defined $object_id;
171              
172 0         0 $untied_self->{_data_dirty} = 0;
173 0         0 $untied_self->{_index_dirty} = 0;
174              
175 0         0 return $object_id;
176             }
177             }
178              
179             sub Delete
180             {
181 0     0 0 0 my $self = shift;
182 0 0       0 croak "No self reference!" if not defined $self;
183              
184 0 0       0 my $untied_self = tied %$self if tied %$self;
185 0 0       0 croak "Wrong object side!" if not defined $untied_self;
186              
187 0 0       0 return undef if not $untied_self->STORABLE();
188 0 0       0 return undef if not $untied_self->{_object_id};
189            
190 0         0 my $storage_module = $untied_self->_PreloadStorageModule();
191              
192 0         0 my $delete_status = $storage_module->DeleteObject($self);
193 0 0       0 if($delete_status)
194             {
195 0         0 $untied_self = undef;
196 0         0 untie %$self;
197 0         0 return 1;
198             }
199             else
200             {
201 0         0 die "Object deletion error !";
202             }
203              
204             }
205              
206             sub DatabaseHandle
207             {
208 0     0 0 0 my $self = shift;
209              
210 0         0 croak "\nNo DatabaseHandle() function defined in ".ref($self)."\n";
211             }
212              
213              
214             sub InternalData
215             {
216 0     0 0 0 my $self = shift;
217 0 0       0 croak "No self reference!" if not defined $self;
218            
219 0 0       0 my $untied_self = tied %$self if tied %$self;
220 0 0       0 croak "Wrong object side!" if not defined $untied_self;
221              
222 0         0 return $untied_self;
223             }
224              
225              
226             sub Freezer
227             {
228 0     0 0 0 my $self = shift;
229 0 0       0 croak "No self reference!" if not defined $self;
230            
231 0 0       0 my $untied_self = tied %$self if tied %$self;
232 0 0       0 croak "Wrong object side!" if not defined $untied_self;
233              
234 0         0 my $id = $self->Id();
235              
236 0 0       0 if(not defined $id)
237             {
238 0         0 my $type = $self->Type();
239 0         0 warn "Attempted to freeze an unsaved object instance of type $type";
240             }
241              
242 0         0 my $package = ref($self);
243 0         0 my $str = "do { use $package; scalar(load $package('$id')) }";
244 0         0 return $str;
245             }
246              
247             sub Dump
248             {
249 0     0 0 0 my $self = shift;
250 0 0       0 croak "No self reference!" if not defined $self;
251            
252 0 0       0 my $untied_self = tied %$self if tied %$self;
253 0 0       0 croak "Wrong object side!" if not defined $untied_self;
254              
255 0         0 local $Data::Dumper::Indent=0;
256 0         0 local $Data::Dumper::Useqq=1;
257 0         0 local $Data::Dumper::Terse=1;
258 0         0 local $Data::Dumper::Freezer = 'Freezer';
259              
260 0         0 my $d = 'Persistent::Hash::Dumper'->new( [$untied_self->{_data}] );
261 0         0 my $str = $d->Dump();
262 0         0 return $str;
263             }
264              
265            
266             #--#------------------------------------------#
267             # Internal API
268             #--#------------------------------------------#
269              
270             sub _IsInitialized
271             {
272 9     9   12 my $untied_self = shift;
273 9 50       21 croak "Attempt to call _IsInitialized() as a function call" if not defined $untied_self;
274 9 50       22 croak "Wrong object side!" if tied %$untied_self;
275              
276 9 50       31 return 1 if(not $untied_self->LOAD_ON_DEMAND());
277 9 50       40 return 1 if(not defined $untied_self->{_object_id});
278 0 0 0     0 return 1 if( defined $untied_self->{_object_id} && defined $untied_self->{_initialized});
279 0         0 return undef;
280             }
281              
282             sub _Initialize
283             {
284 0     0   0 my $untied_self = shift;
285 0 0       0 croak "Attempt to call _Initialize() as a function call" if not defined $untied_self;
286 0 0       0 croak "Wrong object side!" if tied %$untied_self;
287              
288 0 0       0 return undef if not defined $untied_self->{_object_id};
289              
290 0         0 my $storage_module = $untied_self->_PreloadStorageModule();
291              
292 0         0 $untied_self->{_data} = $storage_module->LoadObjectData($untied_self);
293 0         0 $untied_self->{_index_data} = $storage_module->LoadObjectIndex($untied_self);
294              
295              
296 0         0 foreach my $key (keys %{$untied_self->{_data}})
  0         0  
297             {
298 0 0 0     0 delete $untied_self->{_data}->{$key} if !$untied_self->{_data_fields}->{$key} && $untied_self->STRICT_FIELDS();
299 0 0       0 delete $untied_self->{_data}->{$key} if !defined $untied_self->{_data}->{$key};
300             }
301              
302 0         0 foreach my $key (keys %{$untied_self->{_index_data}})
  0         0  
303             {
304 0 0 0     0 delete $untied_self->{_index_data}->{$key} if !$untied_self->{_index_fields}->{$key} && $untied_self->STRICT_FIELDS();
305 0 0       0 delete $untied_self->{_index_data}->{$key} if !defined $untied_self->{_index_data}->{$key};
306             }
307              
308 0         0 $untied_self->{_initialized} = 1;
309              
310 0         0 return 1;
311             }
312            
313             sub _IsDirty
314             {
315 0     0   0 my $untied_self = shift;
316 0 0       0 croak "Attempt to call _IsDirty() as a function call" if not defined $untied_self;
317 0 0       0 croak "Wrong object side!" if tied %$untied_self;
318              
319             #If any key in the hash is a ref to something, we are automatically
320             #dirty because we can't track another ref's modifications.
321 0         0 foreach my $key (keys %{$untied_self->{'_data'}})
  0         0  
322             {
323 0 0       0 $untied_self->{'_data_dirty'} = 1 if ref( $untied_self->{'_data'}->{$key} );
324             }
325              
326 0 0       0 return 1 if $untied_self->{_index_dirty} == 1;
327 0 0       0 return 1 if $untied_self->{_data_dirty} == 1;
328 0         0 return undef;
329             }
330              
331             sub _FlattenData
332             {
333 0     0   0 my $untied_self = shift;
334 0 0       0 croak "No self reference!" if not defined $untied_self;
335 0 0       0 croak "Wrong object side!" if tied %$untied_self;
336              
337 0         0 local $Data::Dumper::Terse = 1;
338 0         0 local $Data::Dumper::Indent = 0;
339 0         0 local $Data::Dumper::Useqq = 1;
340              
341 0         0 my $dump = Dumper($untied_self->{_data});
342 0         0 $dump =~ s/\$VAR1 =//g;
343              
344 0         0 return $dump;
345             }
346              
347             sub _PreloadStorageModule
348             {
349 0     0   0 my $untied_self = shift;
350 0 0       0 croak "Attempt to call _IsDirty() as a function call" if not defined $untied_self;
351 0 0       0 croak "Wrong object side!" if tied %$untied_self;
352              
353 0 0       0 return $untied_self->STORAGE_MODULE() if $untied_self->{_storage_module_preloaded};
354              
355 0         0 my $storage_module = $untied_self->STORAGE_MODULE();
356 0         0 eval "use $storage_module";
357 0 0       0 if($@)
358             {
359 0         0 die "Could not compile $storage_module";
360             }
361 0         0 $untied_self->{_storage_module_preloaded} = 1;
362 0         0 return $untied_self->STORAGE_MODULE();
363             }
364              
365             #--#------------------------------------------#
366             # Tie Hash implementation
367             #--#------------------------------------------#
368              
369             sub TIEHASH
370             {
371 3     3   8 my ($classname, $args) = @_;
372 3 50       26 print STDERR "TIEHASH called.\n" if $classname->DEBUG_LEVEL();
373              
374 3         19 my $type = $classname->PackageToType();
375              
376 3         33 my $self =
377             {
378             _object_id => undef,
379             _object_type => $type,
380             _data => {},
381             _data_fields => {},
382             _data_dirty => 0,
383             _index_data => {},
384             _index_fields => {},
385             _index_dirty => 0,
386             _storage_module_preloaded => 0,
387             };
388 3         11 bless $self, $classname;
389              
390 3         17 return $self->_TieFields();
391             }
392              
393             sub _TieFields
394             {
395 3     3   5 my $self = shift;
396 3         20 $self->{_index_fields} = {};
397 3         9 $self->{_data_fields} = {};
398              
399 3         6 foreach my $field (@{$self->INDEX_FIELDS()})
  3         13  
400             {
401 9         28 $self->{_index_fields}->{$field} = 1;
402             }
403              
404 3         5 foreach my $field (@{$self->DATA_FIELDS()})
  3         18  
405             {
406 21         49 $self->{_data_fields}->{$field} = 1;
407             }
408 3         12 return $self;
409             }
410            
411             sub DELETE
412             {
413 0     0   0 my $untied_self = shift;
414 0 0       0 print STDERR "DELETE called.\n" if $untied_self->DEBUG_LEVEL();
415 0         0 my $key = shift;
416              
417 0 0       0 $untied_self->_Initialize() if not $untied_self->_IsInitialized();
418              
419 0 0       0 if($untied_self->{_index_fields}->{$key})
    0          
420             {
421 0         0 $untied_self->{_index_dirty} = 1;
422 0         0 return delete $untied_self->{_index_data}->{$key};
423             }
424             elsif($untied_self->{_data_fields}->{$key})
425             {
426 0         0 $untied_self->{_data_dirty} = 1;
427 0         0 return delete $untied_self->{_data}->{$key};
428             }
429             else
430             {
431 0         0 $untied_self->{_data_dirty} = 1;
432 0         0 return delete $untied_self->{_data}->{$key};
433             }
434             }
435              
436             sub CLEAR
437             {
438 0     0   0 my $untied_self = shift;
439 0 0       0 print STDERR "CLEAR called.\n" if $untied_self->DEBUG_LEVEL();
440              
441 0 0       0 $untied_self->_Initialize() if not $untied_self->_IsInitialized();
442              
443 0         0 $untied_self->{_index_dirty} = 1;
444 0         0 $untied_self->{_index_data} = {};
445 0         0 $untied_self->{_data_dirty} = 1;
446 0         0 return $untied_self->{_data} = {};
447             }
448              
449            
450             sub STORE
451             {
452 4     4   810 my $untied_self = shift;
453 4         6 my $key = shift;
454 4         5 my $value = shift;
455 4 50       18 print STDERR "STORE called for $key: $value\n" if $untied_self->DEBUG_LEVEL();
456              
457 4 50       15 $untied_self->_Initialize() if not $untied_self->_IsInitialized();
458              
459 4 100       18 if($untied_self->{_index_fields}->{$key})
    50          
    0          
460             {
461 2         3 $untied_self->{_index_dirty} = 1;
462 2         10 return $untied_self->{_index_data}->{$key} = $value;
463             }
464             elsif($untied_self->{_data_fields}->{$key})
465             {
466 2         4 $untied_self->{_data_dirty} = 1;
467 2         10 return $untied_self->{_data}->{$key} = $value;
468             }
469             elsif($untied_self->STRICT_FIELDS())
470             {
471 0         0 print STDERR "\nKey $key not allowed in a ".ref($untied_self)."\n";
472 0         0 return undef;
473             }
474             else
475             {
476 0         0 $untied_self->{_data_dirty} = 1;
477 0         0 return ($untied_self->{_data}->{$key} = $value);
478             }
479             }
480              
481             sub FETCH
482             {
483 2     2   17 my $untied_self = shift;
484 2         3 my $key = shift;
485 2 50       8 print STDERR "FETCH called for $key\n" if $untied_self->DEBUG_LEVEL();
486              
487 2 50       4 $untied_self->_Initialize() if not $untied_self->_IsInitialized();
488              
489 2 50       6 if($untied_self->STRICT_FIELDS())
490             {
491 2 100       7 if($untied_self->{_index_fields}->{$key})
492             {
493 1         4 return $untied_self->{_index_data}->{$key};
494             }
495 1 50       4 if($untied_self->{_data_fields}->{$key})
496             {
497 1         4 return $untied_self->{_data}->{$key};
498             }
499 0         0 return undef;
500             }
501             else
502             {
503 0 0       0 if($untied_self->{_index_fields}->{$key})
504             {
505 0         0 return $untied_self->{_index_data}->{$key};
506             }
507 0         0 return $untied_self->{_data}->{$key};
508             }
509            
510             }
511              
512             sub EXISTS
513             {
514 0     0   0 my $untied_self = shift;
515 0 0       0 print STDERR "EXISTS called.\n" if $untied_self->DEBUG_LEVEL();
516 0         0 my $key = shift;
517              
518 0 0       0 $untied_self->_Initialize() if not $untied_self->_IsInitialized();
519              
520 0 0       0 if($untied_self->{_index_fields}->{$key})
    0          
521             {
522 0         0 return exists $untied_self->{_index_data}->{$key};
523             }
524             elsif($untied_self->{_data_fields}->{$key})
525             {
526 0         0 return exists $untied_self->{_data}->{$key};
527             }
528             else
529             {
530 0         0 return undef;
531             }
532             }
533              
534             sub NEXTKEY
535             {
536 2     2   3 my $untied_self = shift;
537 2 50       9 print STDERR "NEXTKEY called.\n" if $untied_self->DEBUG_LEVEL();
538              
539 2 50       6 $untied_self->_Initialize() if not $untied_self->_IsInitialized();
540 2         4 return pop @{$untied_self->{_keyslist}};
  2         10  
541             }
542              
543             sub FIRSTKEY
544             {
545 1     1   2093 my $untied_self = shift;
546 1 50       9 print STDERR "FIRSTKEY called.\n" if $untied_self->DEBUG_LEVEL();
547              
548 1 50       5 $untied_self->_Initialize() if not $untied_self->_IsInitialized();
549 1         21 $untied_self->{_keyslist} =
550             [
551 1         6 (keys %{$untied_self->{_data}}),
552 1         2 (keys %{$untied_self->{_index_data}})
553             ];
554              
555 1         3 return pop @{$untied_self->{_keyslist}};
  1         11  
556             }
557              
558             666;