File Coverage

blib/lib/Test/TempDir/Factory.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Test::TempDir::Factory;
2             # ABSTRACT: A factory for creating L<Test::TempDir::Handle> objects
3             $Test::TempDir::Factory::VERSION = '0.09';
4 4     4   63350 use Moose;
  0            
  0            
5             use Carp qw(croak carp);
6             use File::Spec;
7             use File::Temp;
8             use Path::Class;
9              
10             use MooseX::Types::Path::Class qw(Dir);
11              
12             use Test::TempDir::Handle;
13              
14             use namespace::autoclean 0.08;
15              
16             has lock => (
17             isa => "Bool",
18             is => "rw",
19             default => 1,
20             );
21              
22             has lock_opts => (
23             isa => "HashRef",
24             is => "rw",
25             default => sub { { lock_type => "NONBLOCKING" } },
26             );
27              
28             has lock_attempts => (
29             isa => "Int",
30             is => "rw",
31             default => 2,
32             );
33              
34             has dir_name => (
35             isa => Dir,
36             is => "rw",
37             coerce => 1,
38             default => sub { dir($ENV{TEST_TEMPDIR} || $ENV{TEST_TMPDIR} || "tmp") },
39             );
40              
41             has cleanup_policy => (
42             isa => "Str",
43             is => "rw",
44             default => sub { $ENV{TEST_TEMPDIR_CLEANUP} || "success" },
45             );
46              
47             has t_dir => (
48             isa => Dir,
49             is => "rw",
50             coerce => 1,
51             default => sub { dir("t") },
52             );
53              
54             has options => (
55             isa => "HashRef",
56             is => "rw",
57             default => sub { {} },
58             );
59              
60             has use_subdir => (
61             isa => "Bool",
62             is => "rw",
63             default => sub { $ENV{TEST_TEMPDIR_USE_SUBDIR} ? 1 : 0 },
64             );
65              
66             has subdir_template => (
67             isa => "Str",
68             is => "rw",
69             default => File::Temp::TEMPXXX,
70             );
71              
72             has handle_class => (
73             isa => "ClassName",
74             is => "rw",
75             default => "Test::TempDir::Handle",
76             handles => { new_handle => "new" },
77             );
78              
79             has verbose => (
80             isa => "Bool",
81             is => "rw",
82             default => 0,
83             );
84              
85             sub create {
86             my ( $self, @args ) = @_;
87              
88             my ( $path, $lock ) = $self->create_and_lock( $self->base_path(@args), @args );
89              
90             my $h = $self->new_handle(
91             dir => $path,
92             ( defined($lock) ? ( lock => $lock ) : () ),
93             cleanup_policy => $self->cleanup_policy,
94             @args,
95             );
96              
97             $h->empty;
98              
99             return $h;
100             }
101              
102             sub create_and_lock {
103             my ( $self, $preferred, @args ) = @_;
104              
105             if ( $self->use_subdir ) {
106             $preferred = $self->make_subdir($preferred);
107             } else {
108             $preferred->mkpath unless -d $preferred;
109             }
110              
111             unless ( $self->lock ) {
112             return $preferred;
113             } else {
114             croak "When locking is enabled you must call create_and_lock in list context" unless wantarray;
115             if ( my $lock = $self->try_lock($preferred) ) {
116             return ( $preferred, $lock );
117             }
118              
119             return $self->create_and_lock_fallback(@args);
120             }
121             }
122              
123             sub create_and_lock_fallback {
124             my ( $self, @args ) = @_;
125              
126             my $base = $self->fallback_base_path;
127              
128             for ( 1 .. $self->lock_attempts ) {
129             my $dir = $self->make_subdir($base);
130              
131             if ( $self->lock ) {
132             if ( my $lock = $self->try_lock($dir) ) {
133             return ( $dir, $lock );
134             }
135              
136             rmdir $dir;
137             } else {
138             return $dir;
139             }
140             }
141              
142             croak "Unable to create locked tempdir";
143             }
144              
145             sub try_lock {
146             my ( $self, $path ) = @_;
147              
148             return 1 if !$self->lock;
149              
150             require File::NFSLock;
151             File::NFSLock->new({
152             file => $path->stringify . ".lock", # FIXME $path->file ? make sure it's not zapped by empty
153             %{ $self->lock_opts },
154             });
155             }
156              
157             sub make_subdir {
158             my ( $self, $dir ) = @_;
159             $dir->mkpath unless -d $dir;
160             dir( File::Temp::tempdir( $self->subdir_template, DIR => $dir->stringify ) );
161             }
162              
163             sub base_path {
164             my ( $self, @args ) = @_;
165              
166             my $dir = $self->dir_name;
167              
168             return $dir if -d $dir and -w $dir;
169              
170             my $t = $self->t_dir;
171              
172             if ( -d $t and -w $t ) {
173             $dir = $t->subdir($dir);
174             return $dir if -d $dir && -w $dir or not -e $dir;
175             }
176              
177             $self->blurt("$t is not writable, using fallback");
178              
179             return $self->fallback_base_path(@args);
180             }
181              
182             sub blurt {
183             my ( $self, @blah ) = @_;
184             if ( $self->can("logger") and my $logger = $self->logger ) {
185             $logger->warn(@blah);
186             } else {
187             return unless $self->verbose;
188             carp(@blah);
189             }
190             }
191              
192             sub fallback_base_path {
193             return dir(File::Spec->tmpdir);
194             }
195              
196             __PACKAGE__
197              
198             __END__
199              
200             =pod
201              
202             =encoding UTF-8
203              
204             =head1 NAME
205              
206             Test::TempDir::Factory - A factory for creating L<Test::TempDir::Handle> objects
207              
208             =head1 VERSION
209              
210             version 0.09
211              
212             =head1 SYNOPSIS
213              
214             my $f = Test::TempDir::Factory->new;
215              
216             my $d = $f->create;
217              
218             $d->empty;
219              
220             # ...
221              
222             $d->cleanup
223              
224             =head1 DESCRIPTION
225              
226             This class creates L<Test::TempDir::Handle> objects with the right C<dir>
227             parameter, taking care of obtaining locks, creating directories, and handling
228             fallback logic.
229              
230             =head1 ATTRIBUTES
231              
232             =head2 C<lock>
233              
234             Whether or not to enable locking.
235              
236             Defaults to true.
237              
238             =head2 C<lock_opts>
239              
240             A hash reference to pass to L<File::NFSLock>.
241              
242             Defaults to C<NONBLOCKING>
243              
244             =head2 C<lock_attempts>
245              
246             How many times to try to create and lock a directory.
247              
248             Defaults to 2.
249              
250             =head2 C<dir_name>
251              
252             The directory under C<t_dir> to use.
253              
254             Defaults to C<tmp>
255              
256             =head2 C<t_dir>
257              
258             Defaults to C<t>
259              
260             =head2 C<use_subdir>
261              
262             Whether to always use a temporary subdirectory under the temporary root.
263              
264             This means that with a C<success> cleanup policy all failures are retained.
265              
266             When disabled, C<t/tmp> will be used directly as C<temp_root>.
267              
268             Defaults to true.
269              
270             =head2 C<subdir_template>
271              
272             The template to pass to C<tempdir>. Defaults to C<File::Temp::TEMPXXX>.
273              
274             =head2 C<handle_class>
275              
276             Defaults to L<Test::TempDir::Handle>.
277              
278             =head2 C<verbose>
279              
280             Whether or not to C<carp> diagnostics when falling back.
281              
282             If you subclass this factory and add a C<logger> method a la L<MooseX::Logger>
283             then this parameter is ignored and all messages will be C<warn>ed on the
284             logger.
285              
286             =head1 METHODS
287              
288             =head2 C<create>
289              
290             Create a L<Test::TempDir::Handle> object with a proper C<dir> attribute.
291              
292             =head1 AUTHOR
293              
294             יובל קוג'מן (Yuval Kogman) <nothingmuch@woobling.org>
295              
296             =head1 COPYRIGHT AND LICENSE
297              
298             This software is copyright (c) 2006 by יובל קוג'מן (Yuval Kogman).
299              
300             This is free software; you can redistribute it and/or modify it under
301             the same terms as the Perl 5 programming language system itself.
302              
303             =cut