File Coverage

blib/lib/Template/Overlay.pm
Criterion Covered Total %
statement 80 82 97.5
branch 20 24 83.3
condition 9 12 75.0
subroutine 18 18 100.0
pod 2 2 100.0
total 129 138 93.4


line stmt bran cond sub pod time code
1 1     1   29375 use strict;
  1         8  
  1         22  
2 1     1   4 use warnings;
  1         1  
  1         34  
3              
4             package Template::Overlay;
5             $Template::Overlay::VERSION = '1.16';
6             # ABSTRACT: A powerful, and simple, library for resolving placeholders in templated files
7             # PODNAME: Template::Overlay
8              
9 1     1   5 use Carp;
  1         1  
  1         52  
10 1     1   411 use File::Copy qw(copy);
  1         1885  
  1         44  
11 1     1   6 use File::Find;
  1         1  
  1         39  
12 1     1   5 use File::Path qw(make_path);
  1         2  
  1         39  
13 1     1   4 use File::Spec;
  1         2  
  1         13  
14 1     1   382 use File::stat;
  1         5315  
  1         3  
15 1     1   46 use Fcntl;
  1         2  
  1         162  
16 1     1   5 use Log::Any;
  1         2  
  1         4  
17              
18             my $logger = Log::Any->get_logger();
19              
20             sub new {
21 8     8 1 23 return bless( {}, shift )->_init(@_);
22             }
23              
24             sub _init {
25 8     8   23 my ( $self, $base, $resolver, %options ) = @_;
26              
27 8         75 $self->{base} = File::Spec->rel2abs($base);
28 8         15 $self->{resolver} = $resolver;
29 8         15 $self->{key} = $options{key};
30              
31 8         21 $logger->debug( 'new overlay [', $self->{base}, ']' );
32              
33 8         477 return $self;
34             }
35              
36             sub _overlay_files {
37 8     8   12 my ( $self, $overlays ) = @_;
38              
39 8         20 my %overlay_files = ();
40 8 100       23 foreach my $overlay ( ref($overlays) eq 'ARRAY' ? @$overlays : ($overlays) ) {
41 10         80 $overlay = File::Spec->rel2abs($overlay);
42 10         18 my $base_path_length = length($overlay);
43             find(
44             sub {
45 26 50 66 26   1366 if ( -f $File::Find::name && $_ !~ /~$/ && $_ !~ /^\..+\.swp$/ ) {
      66        
46 10         27 my $relative = _relative_path( $File::Find::name, $base_path_length );
47 10         286 $overlay_files{$relative} = $File::Find::name;
48             }
49             },
50 10         554 $overlay
51             );
52             }
53              
54 8         38 return %overlay_files;
55             }
56              
57             sub overlay {
58 8     8 1 70 my ( $self, $overlays, %options ) = @_;
59              
60 8         18 my %overlay_files = $self->_overlay_files($overlays);
61 8         16 my $destination = $self->{base};
62 8 100 66     40 if ( $options{to} && $options{to} ne $self->{base} ) {
63 5         46 $destination = File::Spec->rel2abs( $options{to} );
64 5         23 my $base_path_length = length( File::Spec->rel2abs( $self->{base} ) );
65             find(
66             sub {
67 22     22   1242 my $relative = _relative_path( $File::Find::name, $base_path_length );
68 22 100       247 if ( -d $File::Find::name ) {
69 9         630 make_path( File::Spec->catdir( $destination, $relative ) );
70             }
71 22 100       668 if ( -f $File::Find::name ) {
72 13         34 my $template = delete( $overlay_files{$relative} );
73 13         125 my $file = File::Spec->catfile( $destination, $relative );
74 13 100       30 if ($template) {
75 7         27 $self->_resolve( $template, $file, $options{resolver} );
76             }
77             else {
78 6         20 copy( $_, $file );
79             }
80             }
81             },
82             $self->{base}
83 5         224 );
84             }
85 8         288 foreach my $relative ( keys(%overlay_files) ) {
86 3         32 my $file = File::Spec->catfile( $destination, $relative );
87 3         366 make_path( ( File::Spec->splitpath($file) )[1] );
88 3         17 $self->_resolve( $overlay_files{$relative}, $file, $options{resolver} );
89             }
90             }
91              
92             sub _relative_path {
93 32     32   54 my ( $path, $base_path_length ) = @_;
94             return
95 32 100       92 length($path) == $base_path_length
96             ? ''
97             : substr( $File::Find::name, $base_path_length + 1 );
98             }
99              
100             sub _resolve {
101 10     10   29 my ( $self, $template, $file, $resolver ) = @_;
102              
103 10 100 100     33 return if ( $resolver && &$resolver( $template, $file ) );
104              
105 9 50       122 if ( -f $file ) {
106 0         0 $logger->debugf(
107             '[%s] already exists, deleting to ensure creation with proper permissions', $file );
108 0         0 unlink($file);
109             }
110              
111 9         34 my $mode = stat($template)->mode() & 07777; ## no critic
112 9         1251 $logger->infof( 'processing [%s] -> [%04o] [%s]', $template, $mode, $file );
113 9 50       1194 sysopen( my $handle, $file, O_CREAT | O_TRUNC | O_WRONLY, $mode )
114             || croak("open $file failed: $!");
115 9         28 eval {
116             print( $handle $self->{resolver}->resolve(
117             filename => $template,
118 9 100       50 ( $self->{key} ? ( key => $self->{key} ) : () )
119             )
120             );
121             };
122 9         22 my $error = $@;
123 9         203 close($handle);
124 9 50       188 croak($error) if ($error);
125             }
126              
127             1;
128              
129             __END__