File Coverage

blib/lib/OrePAN2/Repository/Cache.pm
Criterion Covered Total %
statement 71 73 97.2
branch 10 20 50.0
condition 4 9 44.4
subroutine 18 18 100.0
pod 0 4 0.0
total 103 124 83.0


line stmt bran cond sub pod time code
1             package OrePAN2::Repository::Cache;
2              
3 6     6   44 use utf8;
  6         11  
  6         62  
4              
5 6     6   296 use Moo;
  6         9  
  6         40  
6              
7 6     6   2195 use Carp ();
  6         11  
  6         82  
8 6     6   23 use Digest::MD5 ();
  6         9  
  6         73  
9 6     6   21 use File::Path ();
  6         8  
  6         60  
10 6     6   18 use File::Spec ();
  6         12  
  6         130  
11 6     6   2494 use File::stat qw( stat );
  6         37137  
  6         483  
12 6     6   3088 use IO::File::AtomicChange ();
  6         9879  
  6         176  
13 6     6   40 use JSON::PP ();
  6         8  
  6         175  
14 6     6   28 use Types::Standard qw( Bool HashRef Str );
  6         10  
  6         64  
15 6     6   18296 use Types::Path::Tiny qw( Path );
  6         14  
  6         49  
16              
17 6     6   3234 use namespace::clean;
  6         15  
  6         56  
18              
19             has directory => ( is => 'ro', isa => Path, coerce => 1, required => 1 );
20             has data => ( is => 'lazy', isa => HashRef, builder => 1 );
21             has filename => ( is => 'lazy', isa => Str, builder => 1 );
22             has is_dirty => ( is => 'rw', isa => Bool, default => !!0 );
23              
24             sub _build_data {
25 5     5   75 my $self = shift;
26 5         11 return do {
27 5 100       134 if ( open my $fh, '<', $self->filename ) {
28             JSON::PP->new->utf8->decode(
29 1         145 do { local $/; <$fh> }
  1         57  
  1         60  
30             );
31             }
32             else {
33 4         574 +{};
34             }
35             };
36             }
37              
38             sub _build_filename {
39 5     5   53 my $self = shift;
40 5         152 return File::Spec->catfile( $self->directory, 'orepan2-cache.json' );
41             }
42              
43             sub is_hit {
44 2     2 0 7728 my ( $self, $stuff ) = @_;
45              
46 2         58 my $entry = $self->data->{$stuff};
47              
48 2 50 66     2055 return 0 unless $entry && $entry->{filename} && $entry->{md5};
      33        
49              
50             my $fullpath
51 1         23 = File::Spec->catfile( $self->directory, $entry->{filename} );
52 1 50       47 return 0 unless -f $fullpath;
53              
54 1 50 33     9 if ( my $stat = stat($stuff) && defined( $entry->{mtime} ) ) {
55 0 0       0 return 0 if $stat->mtime ne $entry->{mtime};
56             }
57              
58 1         313 my $md5 = $self->calc_md5($fullpath);
59 1 50       7 return 0 unless $md5;
60 1 50       5 return 0 if $md5 ne $entry->{md5};
61 1         11 return 1;
62             }
63              
64             sub calc_md5 {
65 5     5 0 51 my ( $self, $filename ) = @_;
66              
67             open my $fh, '<', $filename
68 5 50       332 or do {
69 0         0 return;
70             };
71              
72 5         68 my $md5 = Digest::MD5->new();
73 5         343 $md5->addfile($fh);
74 5         141 return $md5->hexdigest;
75             }
76              
77             sub set {
78 4     4 0 8006 my ( $self, $stuff, $filename ) = @_;
79              
80 4 50       63 my $md5
81             = $self->calc_md5(
82             File::Spec->catfile( $self->directory, $filename ) )
83             or Carp::croak("Cannot calculate MD5 for '$filename'");
84 4 50       246 $self->data->{$stuff} = +{
85             filename => $filename,
86             md5 => $md5,
87             ( -f $filename ? ( mtime => stat($filename)->mtime ) : () ),
88             };
89 4         187 $self->is_dirty(1);
90             }
91              
92             sub save {
93 2     2 0 82 my ($self) = @_;
94              
95 2         37 my $filename = $self->filename;
96 2         35 my $json
97             = JSON::PP->new->pretty(1)->canonical(1)->encode( $self->data );
98              
99 2         973 File::Path::mkpath( File::Basename::dirname($filename) );
100              
101 2         23 my $fh = IO::File::AtomicChange->new( $filename, 'w' );
102 2         1312 $fh->print($json);
103 2         48 $fh->close(); # MUST CALL close EXPLICITLY
104             }
105              
106             1;
107