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   140658 use v5.14;
  1         12  
6              
7 1     1   565 use Moo;
  1         11453  
  1         8  
8              
9 1     1   1519 use Fcntl qw/ LOCK_EX /;
  1         2  
  1         44  
10 1     1   5 use List::Util qw/ pairs /;
  1         2  
  1         107  
11 1     1   6 use Path::Tiny qw/ path /;
  1         2  
  1         54  
12 1     1   523 use PerlX::Maybe qw/ maybe /;
  1         2540  
  1         6  
13 1     1   61 use Scalar::Util qw/ openhandle /;
  1         3  
  1         41  
14 1     1   468 use Types::Common::String qw/ SimpleStr /;
  1         121439  
  1         13  
15 1     1   550 use Types::Standard qw/ Bool CodeRef FileHandle HashRef StrMatch /;
  1         2  
  1         6  
16 1     1   1690 use Types::Path::Tiny qw/ Dir File /;
  1         28290  
  1         8  
17              
18             # RECOMMEND PREREQ: Type::Tiny::XS
19              
20 1     1   1060 use namespace::autoclean;
  1         14063  
  1         4  
21              
22             our $VERSION = 'v0.1.4';
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   46 builder => sub { return {} },
64             init_arg => undef,
65             );
66              
67             has _file_handles => (
68             is => 'ro',
69             isa => HashRef [ FileHandle ],
70 1     1   7402 builder => sub { return {} },
71             init_arg => undef,
72             );
73              
74             sub _get_tempfile_args {
75 3     3   7 my ($self, $key ) = @_;
76              
77 3         6 my $template;
78              
79 3 50       11 if ( $self->has_template ) {
80 3         30 $template = $self->template =~ s/KEY/${key}/r;
81             }
82              
83             return (
84 3 50       54 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   19 my ($self, $key, $file, $init) = @_;
94              
95              
96 8         25 my $fhs = $self->_file_handles;
97 8 100       39 if ( my $fh = openhandle( $fhs->{$key} ) ) {
98 3         87 return $fh;
99             }
100              
101             # get file only if we do not have it (otherwise recursion)
102 5   66     27 $file //= $self->file( $key, $init );
103              
104             # filehandle is no longer be open, so overwrite it
105 5         26 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         698 flock( $fh, LOCK_EX );
109 5         40 return $fh;
110             }
111              
112              
113             sub file {
114 5     5 1 14 my ($self, $key, $init) = @_;
115              
116 5         12 my $files = $self->_files;
117              
118 5 100       19 if ( my $file = $files->{$key} ) {
119 2         13 return $file;
120             }
121              
122 3   33     16 my $file = $files->{$key} //= Path::Tiny->tempfile( $self->_get_tempfile_args($key) );
123 3         20311 my $fh = $self->_get_open_file_handle( $key, $file, $init );
124 3 50 33     26 if ( $init //= $self->init ) {
125 3         11 $init->( $key, $file, $fh );
126             }
127 3         3715 return $file;
128             }
129              
130              
131             sub file_handle {
132 5     5 1 831 my ($self, $key, $init) = @_;
133 5         16 return $self->_get_open_file_handle( $key, undef, $init );
134             }
135              
136              
137             sub keys {
138 1     1 1 3 my ($self) = @_;
139 1         6 my $files = $self->_files;
140 1         3 return [ keys %{ $files } ];
  1         6  
141             }
142              
143              
144             sub files {
145 1     1 1 5604 my ($self) = @_;
146 1         4 my $files = $self->_files;
147 1         3 return [ values %{ $files } ];
  1         5  
148             }
149              
150              
151             sub close {
152 2     2 1 6 my ($self) = @_;
153 2         6 my $fhs = $self->_file_handles;
154 2         4 for my $kv ( pairs %{ $fhs } ) {
  2         35  
155 3         5 my ( $key, $fh ) = @{ $kv };
  3         14  
156 3 50       73 close($fh) if $fh;
157 3         18 delete $fhs->{$key};
158             }
159             }
160              
161             sub DEMOLISH {
162 1     1 0 3409 my ($self, $is_global) = @_;
163 1 50       7 $self->close unless $is_global;
164             }
165              
166              
167             1;
168              
169             __END__