File Coverage

blib/lib/File/Temp/Trace.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package File::Temp::Trace;
2              
3             =head1 NAME
4              
5             File::Temp::Trace - Trace the creation of temporary files
6              
7             =head1 VERSION
8              
9             Version 0.02
10              
11             =cut
12              
13             our $VERSION = '0.02';
14              
15             =begin readme
16              
17             =head1 REQUIREMENTS
18              
19             The following packages are required:
20              
21             Attribute::Handlers
22             Carp
23             File::Path
24             File::Spec
25             File::Temp
26             overload
27             Scalar::Util
28             self
29              
30             =end readme
31              
32             =head1 SYNPOSIS
33              
34             package MyPkg;
35              
36             use File::Temp::Trace;
37              
38             my $tmp = File::Temp::Trace->tempdir();
39              
40             print STDERR "New temporary directory ${tmp} created.";
41              
42             sub create_file : skip_temp_log {
43             my ($tmp, $ext) = @_;
44             return $tmp->tempfile( suffix => $ext );
45             }
46              
47             sub create_text {
48             my ($tmp, $ext) = @_;
49             return create_file($tmp, '.txt');
50             }
51              
52             my $fh = create_text($tmp);
53              
54             # $fh->filename will be named "MyPkg-create_text-XXXXXXXX.txt",
55             # where XXXXXXXX is a unique string.
56              
57             =head1 DESCRIPTION
58              
59             This module allows you to trace the creation of temporary files. By
60             default, these files are all created in the same directory, and their
61             names are prefixed by the name of the function or method that created
62             them.
63              
64             You can optionally log the creation of temporary files with a stack
65             trace as well.
66              
67             =cut
68              
69 1     1   19371 use strict;
  1         1  
  1         33  
70 1     1   4 use warnings;
  1         2  
  1         25  
71              
72 1     1   381 use self;
  0            
  0            
73              
74             use overload
75             '""' => \&dir;
76              
77             use Attribute::Handlers;
78             use Carp qw( longmess );
79             use File::Path qw( make_path );
80             use File::Spec;
81             use File::Temp ();
82             use Scalar::Util qw( refaddr );
83              
84             BEGIN {
85             %File::Temp::Trace::SkipName = ( );
86             }
87              
88             sub UNIVERSAL::skip_temp_log : ATTR(CODE) {
89             my ($pkg, $sym, $ref, $attr, $data) = @_;
90             $File::Temp::Trace::SkipName{substr($$sym,1)} = $data;
91             }
92              
93             my %LogFiles = ( );
94              
95             sub _name_to_template {
96             my ($name) = @_;
97             $name =~ s/\:\:/-/g;
98             $name = "UNKNOWN", if (($name eq "") || ($name eq "(eval)"));
99             return "${name}-XXXXXXXX";
100             }
101              
102             =for readme stop
103              
104             Methods are documented below:
105              
106             =head2 tempdir
107              
108             $tmp = File::Temp::Trace->tempdir(%options);
109              
110             Creates a new temporary directory and returns a blessed reference to
111             the name of that temporary directory.
112              
113             The following options may be used:
114              
115             =over
116              
117             =item cleanup
118              
119             Delete the directory and contents once the object is
120             destroyed. True by default.
121              
122             =item template
123              
124             A template for the name of directory. By default, it is
125             C, where C is a unique string.
126              
127             The template name must end with at least C.
128              
129             =item dir
130              
131             The parent directory of the temporary directory. By default, it is in
132             the system temporary directory.
133              
134             =item log
135              
136             Create a log file that gives the time that a temporary file was
137             created, and a L stack trace of the calling methods
138             that created it.
139              
140             Note that if L is true, then the log file will be deleted
141             when the object is destroyed.
142              
143             =back
144              
145             =cut
146              
147             sub tempdir {
148             my $class = shift || __PACKAGE__;
149              
150             my %opts = @args;
151              
152             my %ftopts = ( CLEANUP => 1, TEMPLATE => _name_to_template(__PACKAGE__), TMPDIR => 1 );
153             foreach my $o (qw( cleanup template tmpdir dir )) {
154             $ftopts{ uc($o) } = $opts{$o}, if (exists $opts{$o});
155             }
156              
157             $self = \ File::Temp->newdir($ftopts{TEMPLATE}, %ftopts);
158             bless $self, $class;
159              
160             if ($opts{log}) {
161             $LogFiles{ refaddr $self } = File::Temp->new( TEMPLATE => _name_to_template(__PACKAGE__), DIR => $self->dir, SUFFIX => ".log", UNLINK => 0 );
162             }
163              
164             return $self;
165             }
166              
167             =head2 dir
168              
169             $path = $tmp->dir;
170              
171             The pathname of the temporary directory created by L.
172              
173             Note that the object is overloaded to return the pathname on
174             stringification.
175              
176             =cut
177              
178             sub dir {
179             return ${$self};
180             }
181              
182             =head2 logfile
183              
184             $fh = $tmp->logfile;
185              
186             Returns the filehandle of the log file, or C if the C
187             option was not specified in the constructor.
188              
189             =cut
190              
191             sub logfile {
192             return $LogFiles{ refaddr $self };
193             }
194              
195             =head2 file
196              
197             $fh = $tmp->file(%options);
198              
199             Creates a new temporary file in L, and returns a filehandle.
200              
201             Note that unlike the corresponding method in L, it does
202             not also return a filename. To obtain a filename, use
203              
204             $fh->filename
205              
206             The file is created using L, so other methods from
207             L may be used to query or manipulate the file.
208              
209             The name of the file is of the form C (plus any
210             suffix, if given as an option---see below), where C is the
211             name of the function of method that called L and C is
212             a unique string. This helps with debugging by making it easier to
213             identify which temporary file in L was created by a particular
214             method.
215              
216             In the case where a single method or function is used to create a
217             particular type of file, and is called by several other methods or
218             functions, it can be tagged with the C attribute, so that
219             the name of the caller will come from further down the call stack. For
220             example,
221              
222             sub create_file : skip_temp_log {
223             ...
224             }
225              
226             sub fun_a {
227             create_file(...);
228             }
229              
230             sub fun_b {
231             create_file(...);
232             }
233              
234             In this case, the two temporary files will be labelled with C
235             and C rather than both with C.
236              
237             The following options may be used.
238              
239             =over
240              
241             =item unlink
242              
243             If set to true, delete the file when the filehandle is destroyed. This
244             is set disabled by default, since the parent temporary directory is
245             normally set to be deleted.
246              
247             =item suffix
248              
249             The suffix (or extension) of the file.
250              
251             =item exlock
252              
253             The exclusive lock flag. True by default.
254              
255             =item log
256              
257             Create a separate log file when this file is created. The log file has
258             the same filename as the this file, plug the C<.log> suffix.
259              
260             (In theory this is unsafe, as it does not ensure that a file with the
261             same name exists, though such a case in unlikely.)
262              
263             =item dir
264              
265             Create a subdirectory in the L directory, if it does not already
266             exist, and put the temporary file in there.
267              
268             =back
269              
270             =head2 tempfile
271              
272             $fh = tempfile(%options);
273              
274             This is an alias of L.
275              
276             =cut
277              
278             sub tempfile {
279             my $level = 1;
280             my @frame = ( );
281             my $name;
282             do {
283             @frame = caller($level++);
284             $name = $frame[3] || "";
285             } while ($name && (exists $File::Temp::Trace::SkipName{$name}));
286              
287             my %opts = @args;
288              
289             my %ftopts = ( UNLINK => 0, TEMPLATE => _name_to_template($name), DIR => $self->dir, EXLOCK => 1 );
290             foreach my $o (qw( unlink suffix exlock )) {
291             $ftopts{ uc($o) } = $opts{$o}, if (exists $opts{$o});
292             }
293              
294             if (exists $opts{dir}) {
295             $ftopts{DIR} = File::Spec->catfile(File::Spec->splitdir($self->dir), File::Spec->splitdir($opts{dir}));
296             make_path($ftopts{DIR});
297             }
298              
299             my $fh = File::Temp->new(%ftopts);
300             if ((my $lh = $self->logfile) || ($opts{log})) {
301             my $ts = sprintf("[%s]", (scalar gmtime()));
302             my $msg = sprintf("%s File %s created%s", $ts, $fh->filename, longmess());
303             $msg =~ s/\n(.)/\n$ts $1/g;
304              
305             if ($lh) { print $lh $msg; }
306             if ($opts{log}) {
307             open my $fhlh, sprintf(">%s.log", $fh->filename);
308             print $fhlh $msg;
309             close $fhlh;
310             }
311             }
312             return $fh;
313             }
314              
315             =begin readme
316              
317             =head1 REVISION HISTORY
318              
319             =for readme include file=Changes type=text
320              
321             =end readme
322              
323             =for readme continue
324              
325             =head1 SEE ALSO
326              
327             L
328              
329             =head1 AUTHOR
330              
331             Robert Rothenberg, C<< >>
332              
333             =for readme stop
334              
335             =head1 BUGS
336              
337             Please report any bugs or feature requests to
338             C, or through the web interface at
339             L.
340              
341             =head1 SUPPORT
342              
343             You can find documentation for this module with the perldoc command.
344              
345             perldoc File::Temp::Trace
346              
347             You can also look for information at:
348              
349             =over 4
350              
351             =item * RT: CPAN's request tracker
352              
353             L
354              
355             =item * AnnoCPAN: Annotated CPAN documentation
356              
357             L
358              
359             =item * CPAN Ratings
360              
361             L
362              
363             =item * Search CPAN
364              
365             L
366              
367             =item * GitHub
368              
369             L
370              
371             =back
372              
373             =for readme continue
374              
375             =head1 LICENSE AND COPYRIGHT
376              
377             Copyright 2011 Robert Rothenberg.
378              
379             This program is free software; you can redistribute it and/or modify it
380             under the terms of either: the GNU General Public License as published
381             by the Free Software Foundation; or the Artistic License.
382              
383             See http://dev.perl.org/licenses/ for more information.
384              
385             =cut
386              
387             1;
388