File Coverage

lib/Petal/Cache/Disk.pm
Criterion Covered Total %
statement 55 82 67.0
branch 6 18 33.3
condition 5 17 29.4
subroutine 11 15 73.3
pod 0 9 0.0
total 77 141 54.6


line stmt bran cond sub pod time code
1             # ------------------------------------------------------------------
2             # Petal::Cache::Disk - Caches generated code on disk.
3             # ------------------------------------------------------------------
4             # Author: Jean-Michel Hiver
5             # Description: A simple cache module to avoid re-generating the Perl
6             # code from the template file every time
7             # ------------------------------------------------------------------
8             package Petal::Cache::Disk;
9 77     77   493 use strict;
  77         126  
  77         2229  
10 77     77   366 use warnings;
  77         118  
  77         1805  
11 77     77   362 use File::Spec;
  77         126  
  77         1837  
12 77     77   56762 use File::Temp qw/tempfile/;
  77         1660299  
  77         5017  
13 77     77   618 use Digest::MD5 qw /md5_hex/;
  77         136  
  77         3942  
14 77     77   513 use Carp;
  77         154  
  77         71312  
15              
16              
17             # kill silly warnings
18             sub sillyness
19             {
20 0 0   0 0 0 + $Petal::INPUT &&
21             + $Petal::OUTPUT;
22             }
23              
24              
25             # local $Petal::Cache::Disk::TMP_DIR =
26             # defaults to File::Spec->tmpdir;
27             our $TMP_DIR = File::Spec->tmpdir;
28              
29              
30             # local $Petal::Cache::Disk::PREFIX =
31             # defaults to 'petal_cache_'
32             our $PREFIX = 'petal_cache';
33              
34              
35             # $class->get ($file, $lang);
36             # --------------------
37             # Returns the cached data if its last modification time is more
38             # recent than the last modification time of the template
39             # Returns the code for template file $file, undef otherwise
40             sub get
41             {
42 4     4 0 38 my $class = shift;
43 4         10 my $file = shift;
44 4   50     10 my $lang = shift || '';
45 4         10 my $key = $class->compute_key ($file, $lang);
46 4 50       9 return $class->cached ($key) if ($class->is_ok ($file, $lang));
47 4         17 return;
48             }
49              
50              
51             # $class->set ($file, $code, $lang);
52             # ---------------------------
53             # Sets the cached code for $file + $lang
54             sub set
55             {
56 4     4 0 8 my $class = shift;
57 4         5 my $file = shift;
58 4         19 my $code = shift;
59 4   50     8 my $lang = shift || '';
60 4         11 my $key = $class->compute_key ($file, $lang);
61 4         9 my $tmp = $class->tmp;
62 4         11 my $final_file_path = "$tmp/$key";
63              
64             #we write the cached templated to a temp file first and move it to the final
65             #destination afterwards. this prevents a rare race condition if a
66             #request attempts to use a cached template that is not yet fully written
67             #by turning it into a atomic operation
68              
69 4         16 my ($fh, $temp_file_path) = tempfile( $PREFIX . "_XXXXXX", dir => $tmp);
70 4         1537 binmode( $fh, ":utf8" );
71 4         60 print $fh $code;
72 4         150 close($fh);
73              
74 4 50 0     153 rename($temp_file_path, $final_file_path)
75             or ( Carp::cluck "Cannot write-open $final_file_path ($!)" and return );
76             }
77              
78              
79             # $class->is_ok ($file, $lang);
80             # ----------------------
81             # Returns TRUE if the cache is still fresh, FALSE otherwise.
82             sub is_ok
83             {
84 4     4 0 5 my $class = shift;
85 4         6 my $file = shift;
86 4   50     8 my $lang = shift || '';
87              
88 4         12 my $key = $class->compute_key ($file, $lang);
89 4         12 my $tmp = $class->tmp;
90 4         12 my $tmp_file = "$tmp/$key";
91 4 50       219 return unless (-e $tmp_file);
92              
93 0         0 my $cached_mtime = $class->cached_mtime ($file, $lang);
94 0         0 my $current_mtime = $class->current_mtime ($file);
95 0         0 return $cached_mtime >= $current_mtime;
96             }
97              
98              
99             # $class->compute_key ($file, $lang);
100             # ----------------------------
101             # Computes a cache 'key' for $file+$lang, which should be unique.
102             # (Well, currently an MD5 checksum is used, which is not
103             # *exactly* unique but which should be good enough)
104             sub compute_key
105             {
106 12     12 0 15 my $class = shift;
107 12         12 my $file = shift;
108 12   50     22 my $lang = shift || '';
109              
110 12         58 my $key = md5_hex ($file . ";$lang" . ";INPUT=" . $Petal::INPUT . ";OUTPUT=" . $Petal::OUTPUT);
111 12 50       36 $key = $PREFIX . "_" . $Petal::VERSION . "_" . $key if (defined $PREFIX);
112 12         27 return $key;
113             }
114              
115              
116             # $class->cached_mtime ($file, $lang);
117             # -----------------------------
118             # Returns the last modification date of the cached data
119             # for $file + $lang
120             sub cached_mtime
121             {
122 0     0 0 0 my $class = shift;
123 0         0 my $file = shift;
124 0   0     0 my $lang = shift || '';
125 0         0 my $key = $class->compute_key ($file, $lang);
126 0         0 my $tmp = $class->tmp;
127              
128 0         0 my $tmp_file = "$tmp/$key";
129 0         0 my $mtime = (stat($tmp_file))[9];
130 0         0 return $mtime;
131             }
132              
133              
134             # $class->current_mtime ($file);
135             # ------------------------------
136             # Returns the last modification date for $file
137             sub current_mtime
138             {
139 0     0 0 0 my $class = shift;
140 0         0 my $file = shift;
141 0         0 $file =~ s/#.*$//;
142 0         0 my $mtime = (stat($file))[9];
143 0         0 return $mtime;
144             }
145              
146              
147             # $class->cached ($key);
148             # ----------------------
149             # Returns the cached data for $key
150             sub cached
151             {
152 0     0 0 0 my $class = shift;
153 0         0 my $key = shift;
154 0         0 my $tmp = $class->tmp;
155 0         0 my $cached_filepath = $tmp . '/' . $key;
156              
157 0 0       0 (-e $cached_filepath) or return;
158              
159 0         0 my $res = undef;
160              
161 0 0 0     0 open FP, "<:utf8", "$tmp/$key"
162             or ( Carp::cluck "Cannot read-open $tmp/$key ($!)" and return );
163              
164 0         0 $res = join '', ;
165 0         0 close FP;
166              
167 0         0 return $res;
168             }
169              
170              
171             # $class->tmp;
172             # ------------
173             # Returns the temp directory in which the cached data is kept.
174             sub tmp
175             {
176 8     8 0 12 my $class = shift;
177 8   33     16 $TMP_DIR ||= File::Spec->tmpdir;
178              
179 8 50       121 (-e $TMP_DIR) or confess "\$TMP_DIR '$TMP_DIR' does not exist";
180 8 50       89 (-d $TMP_DIR) or confess "\$TMP_DIR '$TMP_DIR' is not a directory";
181 8         43 $TMP_DIR =~ s/\/+$//;
182 8         26 return $TMP_DIR;
183             }
184              
185              
186             1;