File Coverage

blib/lib/File/HashCache.pm
Criterion Covered Total %
statement 68 68 100.0
branch 15 24 62.5
condition 12 20 60.0
subroutine 11 11 100.0
pod 2 4 50.0
total 108 127 85.0


line stmt bran cond sub pod time code
1             # Copyright © 2009-2013 David Caldwell and Jim Radford.
2             #
3             # This library is free software; you can redistribute it and/or modify
4             # it under the same terms as Perl itself, either Perl version 5.12.4 or,
5             # at your option, any later version of Perl 5 you may have available.
6              
7 3     3   86736 package File::HashCache; use warnings; use strict;
  3     3   9  
  3         90  
  3         17  
  3         6  
  3         141  
8              
9             our $VERSION = '1.0.2';
10              
11 3     3   16 use List::Util qw(max);
  3         10  
  3         403  
12 3     3   16 use Digest::MD5 qw(md5_hex);
  3         7  
  3         163  
13 3     3   15 use File::Basename;
  3         5  
  3         307  
14 3     3   3520 use File::Slurp qw(read_file write_file);
  3         94063  
  3         290  
15 3     3   3959 use JSON qw(to_json from_json);
  3         99925  
  3         20  
16              
17 23 50   23 0 43 sub max_timestamp(@) { max map { (stat $_)[9] || 0 } @_ } # Obviously 9 is mtime
  32         1025  
18              
19             # DEPRECATED. This has been subsumed by the concatenation code--It's easier
20             # to deal with and doesn't make you use a weird dialect of Javscript in your
21             # code. **This is only here for backwards compatibility. Do Not Use.**
22             sub pound_include($;$);
23             sub pound_include($;$) {
24 12     12 0 27 my ($text, $referrer) = @_;
25 12         25 my ($line, @deps) = (0);
26 12         45 return (join('', map { $line++;
  41         47  
27 41         52 $_ .= "\n";
28 41 100       99 if (/^#include\s+"([^"]+)"/) {
29 4 0       21 my $included = read_file(my $name=$1) or die "include '$1' not found".($referrer?" at $referrer\n":"\n");
    50          
30 3         268 ($_, my @new_deps) = pound_include($included, $name);
31 3         11 push @deps, $name, @new_deps;
32             }
33 40         103 $_;
34             } split(/\n/, $text)),
35             @deps);
36             }
37              
38             sub hash {
39 20     20 1 2018751 my ($config, @name) = @_;
40              
41 20         34 my ($dir,@base,$ext);
42 20         53 for my $name (@name) {
43 25         998 my ($_base, $_dir, $_ext) = fileparse $name, qr/\.[^.]+/;
44 25         135 $_ext =~ s/^\.//;
45 25 100 66     176 $_ext eq ($ext //= $_ext) or die "extentions should be the same when concatenating";
46 24   66     176 $dir //= $_dir; # Not quite right but works in most cases.
47 24         71 push @base, $_base;
48             }
49 19         53 my $base = join '-', @base;
50 19         43 my $name = "$dir$base.$ext"; # canonical version of the name in the single case, and a merged version in the multiple case
51              
52 19         42 my $script;
53 19 50 100     277 if ( !($script = $config->{cache}->{$name})
  5   66     32  
54             || ! -f $script->{path}
55             || max_timestamp(@{$script->{deps}}) > $script->{timestamp}) {
56              
57 19         52 my @deps = @name;
58 19         40 my $processed = join("\n", map { scalar read_file($_) } @name);
  23         509  
59 19         2000 my $process_ext = $config->{"process_$ext"};
60 19 100       30 for my $process (@{ref($process_ext) eq 'CODE' ? [$process_ext] : $process_ext}) {
  19         84  
61 24         512 ($processed, my @new_deps) = $process->($processed);
62 23         74 push @deps, @new_deps;
63             }
64              
65 18         105 my $hash = md5_hex($processed);
66 18         192 $config->{cache}->{$name} = $script = { deps => \@deps,
67             name => "$base-$hash.$ext",
68             path => "$config->{cache_dir}/$base-$hash.$ext",
69             hash => $hash,
70             timestamp => max_timestamp(@deps) };
71 18 100       306 if (! -f $script->{path}) {
72 2         17 mkdir $config->{cache_dir};
73 2 50       15 write_file($script->{path}, { atomic => 1 }, $processed) or die "couldn't cache $script->{path}";
74 2 50       703 write_file($config->{cache_file}, { atomic => 1 }, to_json($config->{cache}, {pretty => 1})) or warn "Couldn't save cache control file";
75             }
76             }
77 18         861 $script->{name};
78             }
79              
80             sub new {
81 5     5 1 1002976 my $class = shift;
82 5         85 my $config = bless { cache_dir => '.hashcache',
83             @_,
84             }, $class;
85 5   33     75 $config->{cache_file} ||= "$config->{cache_dir}/cache.json";
86 5 50       172 $config->{cache} = from_json( read_file($config->{cache_file}) ) if -f $config->{cache_file};
87 5         9502 my $cache_file_version = 1;
88             # On mismatched versions, just clear out the cache:
89 5 50 50     72 $config->{cache} = { VERSION => $cache_file_version } unless $config->{cache} && ($config->{cache}->{VERSION} || 0) == $cache_file_version;
      33        
90 5         30 $config;
91             }
92              
93             1;
94              
95             __END__