File Coverage

inc/Path/Class/Dir.pm
Criterion Covered Total %
statement 33 146 22.6
branch 3 72 4.1
condition 1 15 6.6
subroutine 10 30 33.3
pod 19 19 100.0
total 66 282 23.4


line stmt bran cond sub pod time code
1             #line 1
2             package Path::Class::Dir;
3              
4             $VERSION = '0.17';
5 1     1   6  
  1         2  
  1         34  
6 1     1   5 use strict;
  1         2  
  1         24  
7 1     1   476 use Path::Class::File;
  1         5  
  1         30  
8 1     1   7 use Path::Class::Entity;
  1         2  
  1         21  
9 1     1   5 use Carp();
  1         2  
  1         105  
10             use base qw(Path::Class::Entity);
11 1     1   1541  
  1         4  
  1         19  
12 1     1   5 use IO::Dir ();
  1         2  
  1         1942  
13             use File::Path ();
14              
15 2     2 1 19 sub new {
16             my $self = shift->SUPER::new();
17              
18             # If the only arg is undef, it's probably a mistake. Without this
19             # special case here, we'd return the root directory, which is a
20             # lousy thing to do to someone when they made a mistake. Return
21 2 50 33     19 # undef instead.
22             return if @_==1 && !defined($_[0]);
23 2         17  
24             my $s = $self->_spec;
25 2 50       12
    50          
26             my $first = (@_ == 0 ? $s->curdir :
27             $_[0] eq '' ? (shift, $s->rootdir) :
28             shift()
29             );
30 2         61
31 2         30 ($self->{volume}, my $dirs) = $s->splitpath( $s->canonpath($first) , 1);
32             $self->{dirs} = [$s->splitdir($s->catdir($dirs, @_))];
33 2         14  
34             return $self;
35             }
36 0     0 1 0  
37             sub is_dir { 1 }
38              
39 0     0 1 0 sub as_foreign {
40             my ($self, $type) = @_;
41 0         0  
42 0         0 my $foreign = do {
43 0         0 local $self->{file_spec_class} = $self->_spec_class($type);
44             $self->SUPER::new;
45             };
46            
47 0         0 # Clone internal structure
48 0         0 $foreign->{volume} = $self->{volume};
49 0 0       0 my ($u, $fu) = ($self->_spec->updir, $foreign->_spec->updir);
  0         0  
  0         0  
50 0         0 $foreign->{dirs} = [ map {$_ eq $u ? $fu : $_} @{$self->{dirs}}];
51             return $foreign;
52             }
53              
54 2     2 1 3 sub stringify {
55 2         9 my $self = shift;
56 2         399 my $s = $self->_spec;
57 2         5 return $s->catpath($self->{volume},
58             $s->catdir(@{$self->{dirs}}),
59             '');
60             }
61 0     0 1 0  
62             sub volume { shift()->{volume} }
63              
64 0 0   0 1 0 sub file {
65 0         0 local $Path::Class::Foreign = $_[0]->{file_spec_class} if $_[0]->{file_spec_class};
66             return Path::Class::File->new(@_);
67             }
68              
69 0     0 1 0 sub dir_list {
70 0         0 my $self = shift;
71 0 0       0 my $d = $self->{dirs};
72             return @$d unless @_;
73 0         0
74 0 0       0 my $offset = shift;
  0         0  
75             if ($offset < 0) { $offset = $#$d + $offset + 1 }
76 0 0       0
    0          
77             return wantarray ? @$d[$offset .. $#$d] : $d->[$offset] unless @_;
78 0         0
79 0 0       0 my $length = shift;
  0         0  
80 0         0 if ($length < 0) { $length = $#$d + $length + 1 - $offset }
81             return @$d[$offset .. $length + $offset - 1];
82             }
83              
84 0     0 1 0 sub subdir {
85 0         0 my $self = shift;
86             return $self->new($self, @_);
87             }
88              
89 0     0 1 0 sub parent {
90 0         0 my $self = shift;
91 0         0 my $dirs = $self->{dirs};
92             my ($curdir, $updir) = ($self->_spec->curdir, $self->_spec->updir);
93 0 0       0  
    0          
    0          
    0          
94 0         0 if ($self->is_absolute) {
95 0         0 my $parent = $self->new($self);
  0         0  
96 0         0 pop @{$parent->{dirs}};
97             return $parent;
98 0         0  
99 0         0 } elsif ($self eq $curdir) {
100             return $self->new($updir);
101              
102 0         0 } elsif (!grep {$_ ne $updir} @$dirs) { # All updirs
103             return $self->new($self, $updir); # Add one more
104              
105 0         0 } elsif (@$dirs == 1) {
106             return $self->new($curdir);
107              
108 0         0 } else {
109 0         0 my $parent = $self->new($self);
  0         0  
110 0         0 pop @{$parent->{dirs}};
111             return $parent;
112             }
113             }
114              
115             sub relative {
116             # File::Spec->abs2rel before version 3.13 returned the empty string
117 0     0 1 0 # when the two paths were equal - work around it here.
118 0         0 my $self = shift;
119 0 0       0 my $rel = $self->_spec->abs2rel($self->stringify, @_);
120             return $self->new( length $rel ? $rel : $self->_spec->curdir );
121             }
122 0     0 1 0  
123 2     2 1 10 sub open { IO::Dir->new(@_) }
124 0     0 1   sub mkpath { File::Path::mkpath(shift()->stringify, @_) }
125             sub rmtree { File::Path::rmtree(shift()->stringify, @_) }
126              
127 0     0 1   sub remove {
128             rmdir( shift() );
129             }
130              
131 0     0 1   sub recurse {
132 0           my $self = shift;
133             my %opts = (preorder => 1, depthfirst => 0, @_);
134 0 0        
135             my $callback = $opts{callback}
136             or Carp::croak( "Must provide a 'callback' parameter to recurse()" );
137 0          
138             my @queue = ($self);
139 0          
140             my $visit_entry;
141             my $visit_dir =
142             $opts{depthfirst} && $opts{preorder}
143 0     0     ? sub {
144 0           my $dir = shift;
145 0           $callback->($dir);
146             unshift @queue, $dir->children;
147             }
148             : $opts{preorder}
149 0     0     ? sub {
150 0           my $dir = shift;
151 0           $callback->($dir);
152             push @queue, $dir->children;
153             }
154 0     0     : sub {
155 0           my $dir = shift;
156 0           $visit_entry->($_) foreach $dir->children;
157 0 0 0       $callback->($dir);
    0          
158             };
159            
160 0     0     $visit_entry = sub {
161 0 0         my $entry = shift;
  0            
162 0           if ($entry->is_dir) { $visit_dir->($entry) } # Will call $callback
163 0           else { $callback->($entry) }
164             };
165 0          
166 0           while (@queue) {
167             $visit_entry->( shift @queue );
168             }
169             }
170              
171 0     0 1   sub children {
172             my ($self, %opts) = @_;
173 0 0        
174             my $dh = $self->open or Carp::croak( "Can't open directory $self: $!" );
175 0          
176 0           my @out;
177             while (my $entry = $dh->read) {
178 0 0 0       # XXX What's the right cross-platform way to do this?
      0        
179 0           next if (!$opts{all} && ($entry eq '.' || $entry eq '..'));
180 0 0         push @out, $self->file($entry);
181             $out[-1] = $self->subdir($entry) if -d $out[-1];
182 0           }
183             return @out;
184             }
185              
186 0     0 1   sub next {
187 0 0         my $self = shift;
188 0 0         unless ($self->{dh}) {
189             $self->{dh} = $self->open or Carp::croak( "Can't open directory $self: $!" );
190             }
191 0          
192 0 0         my $next = $self->{dh}->read;
193 0           unless (defined $next) {
194 0           delete $self->{dh};
195             return undef;
196             }
197            
198 0           # Figure out whether it's a file or directory
199 0 0         my $file = $self->file($next);
200 0           $file = $self->subdir($next) if -d $file;
201             return $file;
202             }
203              
204 0     0 1   sub subsumes {
205 0 0         my ($self, $other) = @_;
206             die "No second entity given to subsumes()" unless $other;
207 0 0        
208 0 0         $other = $self->new($other) unless UNIVERSAL::isa($other, __PACKAGE__);
209             $other = $other->dir unless $other->is_dir;
210 0 0        
    0          
211 0           if ($self->is_absolute) {
212             $other = $other->absolute;
213 0           } elsif ($other->is_absolute) {
214             $self = $self->absolute;
215             }
216 0            
217 0           $self = $self->cleanup;
218             $other = $other->cleanup;
219 0 0          
220 0 0         if ($self->volume) {
221             return 0 unless $other->volume eq $self->volume;
222             }
223              
224             # The root dir subsumes everything (but ignore the volume because
225 0 0         # we've already checked that)
  0            
  0            
226             return 1 if "@{$self->{dirs}}" eq "@{$self->new('')->{dirs}}";
227 0          
228 0           my $i = 0;
  0            
229 0 0         while ($i <= $#{ $self->{dirs} }) {
  0            
230 0 0         return 0 if $i > $#{ $other->{dirs} };
231 0           return 0 if $self->{dirs}[$i] ne $other->{dirs}[$i];
232             $i++;
233 0           }
234             return 1;
235             }
236              
237 0     0 1   sub contains {
238 0   0       my ($self, $other) = @_;
239             return !!(-d $self and (-e $other or -l $other) and $self->subsumes($other));
240             }
241              
242             1;
243             __END__