File Coverage

lib/Path/Extended/Dir.pm
Criterion Covered Total %
statement 154 166 92.7
branch 75 86 87.2
condition 17 23 73.9
subroutine 32 34 94.1
pod 22 22 100.0
total 300 331 90.6


line stmt bran cond sub pod time code
1             package Path::Extended::Dir;
2              
3 27     27   2329 use strict;
  27         36  
  27         960  
4 27     27   118 use warnings;
  27         37  
  27         773  
5 27     27   103 use base qw( Path::Extended::Entity );
  27         39  
  27         2366  
6 27     27   141 use Path::Extended::File;
  27         53  
  27         46400  
7              
8             sub _initialize {
9 189     189   325 my ($self, @args) = @_;
10              
11 189 100       1129 my $dir = @args ? File::Spec->catdir( @args ) : File::Spec->curdir;
12              
13 189         1532 $self->{_stringify_absolute} = 1; # always true for ::Extended::Dir
14 189         245 $self->{is_dir} = 1;
15 189         507 $self->_set_path($dir);
16              
17 189         509 $self;
18             }
19              
20             sub new_from_file {
21 0     0 1 0 my ($class, $file) = @_;
22              
23 0         0 require File::Basename;
24 0         0 my $dir = File::Basename::dirname( $file );
25              
26 0         0 my $self = $class->new( $dir );
27             }
28              
29             sub _parts {
30 41     41   68 my ($self, $abs) = @_;
31              
32 41 100       112 my $path = $abs ? $self->_absolute : $self->path;
33 41         356 my ($vol, $dir, $file) = File::Spec->splitpath( $path );
34 41         177 return split '/', "$dir$file";
35             }
36              
37             sub basename {
38 8     8 1 23 my $self = shift;
39              
40 8         24 return ($self->_parts)[-1];
41             }
42              
43             sub open {
44 54     54 1 77 my $self = shift;
45              
46 54 100       118 $self->close if $self->is_open;
47              
48             opendir my $dh, $self->_absolute
49 54 100       233 or do { $self->log( error => "Can't open $self: $!" ); return; };
  1         6  
  1         15  
50              
51 53 100 100     317 return $dh if $self->{_compat} && defined wantarray;
52              
53 36         59 $self->{handle} = $dh;
54              
55 36         101 $self;
56             }
57              
58             sub close {
59 53     53 1 89 my $self = shift;
60              
61 53 100       155 if ( my $dh = delete $self->{handle} ) {
62 36         359 closedir $dh;
63             }
64             }
65              
66             sub read {
67 24     24 1 221 my $self = shift;
68              
69 24 100       41 return unless $self->is_open;
70              
71 23         50 my $dh = $self->_handle;
72 23         167 readdir $dh;
73             }
74              
75             sub seek {
76 2     2 1 179 my ($self, $pos) = @_;
77              
78 2 100       5 return unless $self->is_open;
79              
80 1         3 my $dh = $self->_handle;
81 1   50     8 seekdir $dh, $pos || 0;
82             }
83              
84             sub tell {
85 6     6 1 8 my $self = shift;
86              
87 6 100       11 return unless $self->is_open;
88              
89 5         13 my $dh = $self->_handle;
90 5         21 telldir $dh;
91             }
92              
93             sub rewind {
94 2     2 1 2 my $self = shift;
95              
96 2 100       5 return unless $self->is_open;
97              
98 1         2 my $dh = $self->_handle;
99 1         7 rewinddir $dh;
100             }
101              
102             sub find {
103 5     5 1 1635 my ($self, $rule, %options) = @_;
104              
105 5         20 $self->_find( file => $rule, %options );
106             }
107              
108             sub find_dir {
109 3     3 1 321 my ($self, $rule, %options) = @_;
110              
111 3         10 $self->_find( directory => $rule, %options );
112             }
113              
114             sub _find {
115 9     9   14 my ($self, $type, $rule, %options) = @_;
116              
117 9 100       53 return unless $type =~ /^(?:directory|file)$/;
118              
119 8         1138 require File::Find::Rule;
120              
121 10         22 my @items = grep { $_->_relative($self->_absolute) !~ m{/\.} }
  10         3450  
122 8         12736 map { $self->_related( $type, $_ ) }
123             File::Find::Rule->$type->name($rule)->in($self->_absolute);
124              
125 8 100       1068 if ( $options{callback} ) {
126 2         5 @items = $options{callback}->( @items );
127             }
128              
129 8         27 return @items;
130             }
131              
132             sub rmdir {
133 29     29 1 7699 my ($self, @args) = @_;
134              
135 29 50       120 $self->close if $self->is_open;
136              
137 29 100       86 if ( $self->exists ) {
138 28         159 require File::Path;
139 28         71 eval { File::Path::rmtree( $self->_absolute, @args ); 1 }
  28         132  
140 28 50       48 or do { my $err = $@; $self->log( error => $err ); return; };
  0         0  
  0         0  
  0         0  
141             }
142 29         196 $self;
143             }
144              
145             *rmtree = *remove = \&rmdir;
146              
147             sub mkdir {
148 47     47 1 119 my $self = shift;
149              
150 47 100       165 unless ( $self->exists ) {
151 46         254 require File::Path;
152 46         150 eval { File::Path::mkpath( $self->_absolute ); 1 }
  46         218  
153 46 50       58 or do { my $err = $@; $self->log( error => $err ); return; };
  0         0  
  0         0  
  0         0  
154             }
155 47         101 $self;
156             }
157              
158             *mkpath = \&mkdir;
159              
160             sub next {
161 20     20 1 1254 my $self = shift;
162              
163 20 100       43 $self->open unless $self->is_open;
164 20         44 my $next = $self->read;
165 20 100       60 unless ( defined $next ) {
166 4         14 $self->close;
167 4         16 return;
168             }
169 16 100       35 if ( -d File::Spec->catdir( $self->_absolute, $next ) ) {
170 11         28 return $self->_related( dir => $next );
171             }
172             else {
173 5         34 return $self->_related( file => $next );
174             }
175             }
176              
177 50     50 1 188 sub file { shift->_related( file => @_ ); }
178 30     30 1 534 sub subdir { shift->_related( dir => @_ ); }
179              
180             sub file_or_dir {
181 3     3 1 5 my ($self, @args) = @_;
182              
183 3         22 my $file = $self->_related( file => @args );
184 3 100       7 return $self->_related( dir => @args ) if -d $file->_absolute;
185 2         8 return $file;
186             }
187              
188             sub dir_or_file {
189 3     3 1 7 my ($self, @args) = @_;
190              
191 3         6 my $dir = $self->_related( dir => @args );
192 3 100       5 return $self->_related( file => @args ) if -f $dir->_absolute;
193 2         6 return $dir;
194             }
195              
196             sub children {
197 45     45 1 133 my ($self, %options) = @_;
198              
199 45 50       83 my $dh = $self->open or Carp::croak "Can't open directory $self: $!";
200              
201 45         123 my @children;
202 45         191 while (defined(my $entry = readdir $dh)) {
203 178 100 100     990 next if (!$options{all} && ( $entry eq '.' || $entry eq '..' ));
      33        
204 88 100       178 my $type = ( -d File::Spec->catdir($self->_absolute, $entry) )
205             ? 'dir' : 'file';
206 88         270 my $child = $self->_related( $type => $entry );
207 88 100 66     247 if ($options{prune} or $options{no_hidden}) {
208 76 100       167 if (ref $options{prune} eq 'Regexp') {
    100          
209 6 100       24 next if $entry =~ /$options{prune}/;
210             }
211             elsif (ref $options{prune} eq 'CODE') {
212 6 100       13 next if $options{prune}->($child);
213             }
214             else {
215 64 100       158 next if $entry =~ /^\./;
216             }
217             }
218 83         291 push @children, $child;
219             }
220 45         111 $self->close;
221 45         413 return @children;
222             }
223              
224             sub recurse { # adapted from Path::Class::Dir
225 11     11 1 5286 my $self = shift;
226             my %opts = (
227             preorder => 1,
228             depthfirst => 0,
229             prune => 1,
230 11 100 66 0   92 (@_ == 1 && ref $_[0] eq ref sub {}) ? (callback => $_[0]) : @_
  0         0  
231             );
232              
233 11 50       39 my $callback = $opts{callback}
234             or Carp::croak "Must provide a 'callback' parameter to recurse()";
235              
236 11         21 my @queue = ($self);
237              
238 11         10 my $visit_entry;
239             my $visit_dir =
240             $opts{depthfirst} && $opts{preorder}
241             ? sub {
242 10     10   10 my $dir = shift;
243 10         45 $callback->($dir);
244 10         41 unshift @queue, $dir->children( prune => $opts{prune} );
245             }
246             : $opts{preorder}
247             ? sub {
248 23     23   22 my $dir = shift;
249 23         45 $callback->($dir);
250 23         95 push @queue, $dir->children( prune => $opts{prune} );
251             }
252             : sub {
253 10     10   9 my $dir = shift;
254 10         25 $visit_entry->($_) for $dir->children( prune => $opts{prune} );
255 10         55 $callback->($dir);
256 11 100 100     91 };
    100          
257              
258             $visit_entry = sub {
259 90     90   104 my $entry = shift;
260 90 100       234 if ($entry->is_dir) { $visit_dir->($entry) }
  43         63  
261 47         82 else { $callback->($entry) }
262 11         36 };
263              
264 11         28 while (@queue) {
265 74         177 $visit_entry->( shift @queue );
266             }
267             }
268              
269             sub volume {
270 11     11 1 14 my $self = shift;
271              
272 11         31 my ($vol) = File::Spec->splitpath( $self->path );
273 11         31 return $vol;
274             }
275              
276             sub subsumes {
277 11     11 1 23 my ($self, $other) = @_;
278              
279 11 50       25 Carp::croak "No second entity given to subsumes()" unless $other;
280 11         30 my $class = $self->_class('dir');
281 11 100       76 $other = $class->new($other) unless UNIVERSAL::isa($other, $class);
282 11 50       34 $other = $other->dir unless $other->is_dir;
283              
284 11 50       29 if ( $self->volume ) {
285 0 0       0 return 0 unless $other->volume eq $self->volume;
286             }
287              
288 11         27 my @my_parts = $self->_parts(1);
289 11         21 my @other_parts = $other->_parts(1);
290              
291 11 50       29 return 0 if @my_parts > @other_parts;
292              
293 11         13 my $i = 0;
294 11         22 while ( $i < @my_parts ) {
295 45 100       97 return 0 unless $my_parts[$i] eq $other_parts[$i];
296 41         55 $i++;
297             }
298 7         42 return 1;
299             }
300              
301             sub contains {
302 2     2 1 5 my ($self, $other) = @_;
303 2   66     9 return !!(-d $self and (-e $other or -l $other) and $self->subsumes($other));
304             }
305              
306             1;
307              
308             __END__