r11536@llin: dpavlin | 2005-12-05 15:29:47 +0100
[webpac2] / lib / WebPAC / Store.pm
1 package WebPAC::Store;
2
3 use warnings;
4 use strict;
5
6 use base 'WebPAC::Common';
7 use Storable;
8 use File::Path;
9 use Data::Dumper;
10
11 =head1 NAME
12
13 WebPAC::Store - Store normalized data on disk
14
15 =head1 VERSION
16
17 Version 0.05
18
19 =cut
20
21 our $VERSION = '0.05';
22
23 =head1 SYNOPSIS
24
25 This module provides disk storage for normalised data.
26
27 It is newest component of WebPAC, so it will change quite often or be in
28 flux. However, I will try to keep backward compatiblity by providing
29 multiple back-ends.
30
31 This has additional advantage. I can create single place to plugin other
32 file formats which provide better performance for particular type of data.
33
34 For now, this is a prototype version.
35
36     use WebPAC::Store;
37
38     my $foo = WebPAC::Store->new();
39     ...
40
41 =head1 FUNCTIONS
42
43 =head2 new
44
45 Create new normalised database object
46
47   my $db = new WebPAC::Store(
48         path => '/path/to/cache/ds/',
49         database => 'name',
50         read_only => 1,
51   );
52
53 Optional parameter C<path> defines path to directory
54 in which cache file for C<data_structure> call will be created.
55
56 If called with C<read_only> it will not disable caching if
57 called without write permission (but will die on C<save_ds>).
58
59 =cut
60
61 sub new {
62         my $class = shift;
63         my $self = {@_};
64         bless($self, $class);
65
66         $self->path( $self->{'path'} );
67
68         $self ? return $self : return undef;
69 }
70
71 =head2 path
72
73 Check if specified cache directory exist, and if not, disable caching.
74
75  $db->path('./cache/ds/');
76
77 If you pass false or zero value to this function, it will disable
78 cacheing.
79
80 You can also example C<< $db->{path} >> to get current cache path.
81
82 =cut
83
84 sub path {
85         my $self = shift;
86
87         my $dir = shift;
88
89         my $log = $self->_get_logger();
90
91         if ($dir) {
92                 my $msg;
93                 if (! -e $dir) {
94                         if ($self->{'read_only'}) {
95                                 $msg = "doesn't exist";
96                         } else {
97                                 $log->info("creating $dir");
98                                 mkpath $dir;
99                         }
100                 } elsif (! -d $dir) {
101                         $msg = "is not directory";
102                 } elsif (! -w $dir) {
103                         $msg = "not writable" unless ($self->{'read_only'});
104                 }
105
106                 if ($msg) {
107                         $log->warn("cache path $dir $msg, disabling...");
108                         undef $self->{'path'};
109                 } else {
110                         $log->debug("using cache dir $dir");
111                         $self->{'path'} = $dir;
112                 }
113         } else {
114                 $log->debug("disabling cache");
115                 undef $self->{'path'};
116         }
117 }
118
119 =head2 load_ds
120
121 Retrive from disk one data_structure records usually using field 000 as key
122
123   my $ds = $db->load_ds( id => 42, database => 'name' );
124
125 This function will also perform basic sanity checking on returned
126 data and disable caching if data is corrupted (or changed since last
127 update).
128
129 Returns hash or undef if cacheing is disabled or unavailable.
130
131 =cut
132
133 sub load_ds {
134         my $self = shift;
135
136         my $log = $self->_get_logger;
137
138         my $cache_path = $self->{'path'};
139
140         if (! $cache_path) {
141                 $log->warn("path not set, ignoring load_ds");
142                 return;
143         }
144
145         $log->logconfess("arguments for load_ds must be HASH") unless (ref(\@_) eq 'ARRAY' && ($#_ % 2 == 1));
146
147         my $args = {@_};
148         my $id = $args->{id};
149
150         $log->logconfess("got hash, but without id") unless (defined($id));
151         $log->logconfess("got hash, but id isn't number") unless ($id =~ /^\d+$/);
152
153         my $database = $args->{database} || $self->{database};
154
155         $log->logconfess("can't find database name") unless ($database);
156
157         my $cache_file = "$cache_path/$database#$id";
158         $cache_file =~ s#//#/#g;
159
160         $log->debug("using cache_file $cache_file");
161
162         if (-r $cache_file) {
163                 my $ds_ref = retrieve($cache_file);
164                 if ($ds_ref) {
165                         $log->debug("cache hit: $cache_file");
166                         if ($ds_ref->{'ds'}) {
167                                 return $ds_ref->{'ds'};
168                         } else {
169                                 $log->warn("cache entry $cache_file corrupt. Use rm $cache_path/* to re-create it on next run!");
170                                 undef $self->{'path'};
171                         }
172                 }
173         } else {
174                 #$log->warn("cache entry $cache_file doesn't exist");
175                 return undef;
176         }
177
178         return undef;
179 }
180
181 =head2 save_ds
182
183 Store data_structure on disk.
184
185   $db->save_ds(
186         id => $ds->{000}->[0],
187         database => 'name',
188         ds => $ds,
189   );
190
191 B<Totally broken, but fast.>
192
193 Depends on filename generated by C<load_ds>.
194
195 =cut
196
197 sub save_ds {
198         my $self = shift;
199
200         die "can't write to database in read_only mode!" if ($self->{'read_only'});
201
202         return unless($self->{'path'});
203
204         my $arg = {@_};
205
206         my $log = $self->_get_logger;
207
208         foreach my $f (qw/id ds/) {
209                 $log->logconfess("need $f") unless ($arg->{$f});
210         }
211
212         my $database = $arg->{database} ||  $self->{database};
213         $log->logconfess("can't find database name") unless ($database);
214
215         my $cache_file = $self->{path} . '/' . $database . '#' . $arg->{id};
216
217         $log->debug("creating storable cache file $cache_file");
218
219         return store {
220                 ds => $arg->{ds},
221                 id => $arg->{id},
222         }, $cache_file;
223
224 }
225
226 =head1 AUTHOR
227
228 Dobrica Pavlinusic, C<< <dpavlin@rot13.org> >>
229
230 =head1 COPYRIGHT & LICENSE
231
232 Copyright 2005 Dobrica Pavlinusic, All Rights Reserved.
233
234 This program is free software; you can redistribute it and/or modify it
235 under the same terms as Perl itself.
236
237 =cut
238
239 1; # End of WebPAC::Store