File Coverage

blib/lib/File/MultiTemp.pm
Criterion Covered Total %
statement 76 76 100.0
branch 10 16 62.5
condition 4 9 44.4
subroutine 21 21 100.0
pod 5 6 83.3
total 116 128 90.6


line stmt bran cond sub pod time code
1             package File::MultiTemp;
2              
3             # ABSTRACT: manage a hash of temporary files
4              
5 1     1   123599 use v5.14;
  1         14  
6              
7 1     1   541 use Moo;
  1         11347  
  1         5  
8              
9 1     1   1502 use Fcntl qw/ LOCK_EX /;
  1         4  
  1         53  
10 1     1   5 use List::Util qw/ pairs /;
  1         2  
  1         94  
11 1     1   888 use Path::Tiny qw/ path /;
  1         12593  
  1         58  
12 1     1   473 use PerlX::Maybe qw/ maybe /;
  1         2476  
  1         8  
13 1     1   77 use Scalar::Util qw/ openhandle /;
  1         3  
  1         45  
14 1     1   444 use Types::Common::String qw/ SimpleStr /;
  1         118192  
  1         16  
15 1     1   593 use Types::Standard qw/ Bool CodeRef FileHandle HashRef StrMatch /;
  1         2  
  1         8  
16 1     1   1644 use Types::Path::Tiny qw/ Dir File /;
  1         22760  
  1         9  
17              
18             # RECOMMEND PREREQ: Type::Tiny::XS
19              
20 1     1   1081 use namespace::autoclean;
  1         13849  
  1         5  
21              
22             our $VERSION = 'v0.1.3';
23              
24              
25             has template => (
26             is => 'ro',
27             isa => StrMatch[ qr/XXXX/ ],
28             predicate => 1,
29             );
30              
31              
32             has suffix => (
33             is => 'ro',
34             isa => SimpleStr,
35             predicate => 1,
36             );
37              
38              
39             has dir => (
40             is => 'ro',
41             isa => Dir,
42             coerce => \&path,
43             predicate => 1,
44             );
45              
46              
47             has unlink => (
48             is => 'ro',
49             isa => Bool,
50             default => 1,
51             );
52              
53              
54             has init => (
55             is => 'ro',
56             isa => CodeRef,
57             predicate => 1,
58             );
59              
60             has _files => (
61             is => 'ro',
62             isa => HashRef [ File ],
63 1     1   47 builder => sub { return {} },
64             init_arg => undef,
65             );
66              
67             has _file_handles => (
68             is => 'ro',
69             isa => HashRef [ FileHandle ],
70 1     1   6763 builder => sub { return {} },
71             init_arg => undef,
72             );
73              
74             sub _get_tempfile_args {
75 3     3   8 my ($self, $key ) = @_;
76              
77 3         7 my $template;
78              
79 3 50       10 if ( $self->has_template ) {
80 3         17 $template = $self->template =~ s/KEY/${key}/r;
81             }
82              
83             return (
84 3 50       47 maybe TEMPLATE => $template,
    50          
85             maybe SUFFIX => $self->has_suffix ? $self->suffix : undef,
86             maybe DIR => $self->has_dir ? $self->dir->stringify : undef,
87             UNLINK => $self->unlink,
88             );
89              
90             }
91              
92             sub _get_open_file_handle {
93 8     8   22 my ($self, $key, $file, $init) = @_;
94              
95              
96 8         23 my $fhs = $self->_file_handles;
97 8 100       42 if ( my $fh = openhandle( $fhs->{$key} ) ) {
98 3         60 return $fh;
99             }
100              
101             # get file only if we do not have it (otherwise recursion)
102 5   66     28 $file //= $self->file( $key, $init );
103              
104             # filehandle is no longer be open, so overwrite it
105 5         25 my $fh = ( $fhs->{$key} = $file->opena_raw( { locked => 0 } ) );
106              
107             # Path::Tiny locking does not seem to release locks properly, so we need to control locks manually
108 5         695 flock( $fh, LOCK_EX );
109 5         29 return $fh;
110             }
111              
112              
113             sub file {
114 5     5 1 15 my ($self, $key, $init) = @_;
115              
116 5         12 my $files = $self->_files;
117              
118 5 100       22 if ( my $file = $files->{$key} ) {
119 2         10 return $file;
120             }
121              
122 3   33     17 my $file = $files->{$key} //= Path::Tiny->tempfile( $self->_get_tempfile_args($key) );
123 3         19616 my $fh = $self->_get_open_file_handle( $key, $file, $init );
124 3 50 33     29 if ( $init //= $self->init ) {
125 3         11 $init->( $key, $file, $fh );
126             }
127 3         3828 return $file;
128             }
129              
130              
131             sub file_handle {
132 5     5 1 819 my ($self, $key, $init) = @_;
133 5         17 return $self->_get_open_file_handle( $key, undef, $init );
134             }
135              
136              
137             sub keys {
138 1     1 1 4 my ($self) = @_;
139 1         6 my $files = $self->_files;
140 1         2 return [ keys %{ $files } ];
  1         5  
141             }
142              
143              
144             sub files {
145 1     1 1 5358 my ($self) = @_;
146 1         5 my $files = $self->_files;
147 1         2 return [ values %{ $files } ];
  1         6  
148             }
149              
150              
151             sub close {
152 2     2 1 7 my ($self) = @_;
153 2         8 my $fhs = $self->_file_handles;
154 2         5 for my $kv ( pairs %{ $fhs } ) {
  2         34  
155 3         4 my ( $key, $fh ) = @{ $kv };
  3         14  
156 3 50       69 close($fh) if $fh;
157 3         14 delete $fhs->{$key};
158             }
159             }
160              
161             sub DEMOLISH {
162 1     1 0 3351 my ($self, $is_global) = @_;
163 1 50       6 $self->close unless $is_global;
164             }
165              
166              
167             1;
168              
169             __END__