File Coverage

blib/lib/Path/Abstract/Underload.pm
Criterion Covered Total %
statement 189 195 96.9
branch 84 94 89.3
condition 38 44 86.3
subroutine 42 42 100.0
pod 0 24 0.0
total 353 399 88.4


line stmt bran cond sub pod time code
1             package Path::Abstract::Underload;
2             BEGIN {
3 5     5   108 $Path::Abstract::Underload::VERSION = '0.096';
4             }
5             # ABSTRACT: Path::Abstract without stringification overloading
6              
7 5     5   31 use warnings;
  5         10  
  5         152  
8 5     5   26 use strict;
  5         10  
  5         398  
9              
10              
11             use Sub::Exporter -setup => {
12             exports => [ path => sub { sub {
13 0         0 return __PACKAGE__->new(@_)
14 5         66 } } ],
  0         0  
15 5     5   29 };
  5         48  
16 5     5   1980 use Scalar::Util qw/blessed/;
  5         12  
  5         331  
17 5     5   30 use Carp;
  5         7  
  5         859  
18              
19             require Path::Abstract::Fast; # For now...
20              
21              
22             sub new {
23 554     554 0 254115 my $path = "";
24 554         1550 my $self = bless \$path, shift;
25 554         1460 $self->set(@_);
26 554         2682 return $self;
27             }
28              
29             sub clone {
30 49     49 0 75 my $self = shift;
31 49         86 my $path = $$self;
32 49         263 return bless \$path, ref $self;
33             }
34              
35             sub _canonize(@) {
36 5     5   34 no warnings 'uninitialized';
  5         9  
  5         3894  
37 1125 100 66     3213 @_ = map {
38 1117 100       3500 $_ = ref && (ref eq "Path::Abstract::Underload" || blessed $_ && $_->isa("Path::Abstract::Underload")) ? $$_ : $_;
39 1125 100       3702 length() ? $_ : ();
40             } map {
41 786     786   1281 ref eq "ARRAY" ? @$_ : $_
42             } @_;
43 786   100     20059 my $leading = $_[0] && substr($_[0], 0, 1) eq '/';
44 786         1605 my $path = join '/', @_;
45 786   100     2794 my $trailing = $path && substr($path, -1) eq '/';
46              
47             # From File::Spec::Unix::canonpath
48 786         1202 $path =~ s|/{2,}|/|g; # xx////xx -> xx/xx
49 786         1060 $path =~ s{(?:/\.)+(?:/|\z)}{/}g; # xx/././xx -> xx/xx
50 786 50       3204 $path =~ s|^(?:\./)+||s unless $path eq "./"; # ./xx -> xx
51 786         1113 $path =~ s|^/(?:\.\./)+|/|; # /../../xx -> xx
52 786         1448 $path =~ s|^/\.\.$|/|; # /.. -> /
53 786 100       2123 $path =~ s|/\z|| unless $path eq "/"; # xx/ -> xx
54 786 100 100     4584 $path .= '/' if $path ne "/" && $trailing;
55              
56 786 100       1732 $path =~ s/^\/+// unless $leading;
57 786         3065 return $path;
58             }
59              
60             sub set {
61 713     713 0 966 my $self = shift;
62 713         1369 $$self = _canonize @_;
63 713         1970 return $self;
64             }
65              
66             sub is_empty {
67 525     525 0 654 my $self = shift;
68 525         1982 return $$self eq "";
69             }
70 5     5   49 for (qw(is_nil)) { no strict 'refs'; *$_ = \&is_empty }
  5         22  
  5         1689  
71              
72             sub is_root {
73 45     45 0 69 my $self = shift;
74 45         266 return $$self eq "/";
75             }
76              
77             sub is_tree {
78 30     30 0 48 my $self = shift;
79 30         251 return substr($$self, 0, 1) eq "/";
80             }
81              
82             sub is_branch {
83 19     19 0 36 my $self = shift;
84 19 100       98 Path::Abstract->_0_093_warn if $Path::Abstract::_0_093_warn;
85             # return $$self && substr($$self, 0, 1) ne "/";
86 19   100     680 return ! $$self || substr($$self, 0, 1) ne "/";
87             }
88              
89             sub to_tree {
90 11     11 0 18 my $self = shift;
91 11 100       25 $$self = "/$$self" unless $self->is_tree;
92 11         102 return $self;
93             }
94              
95             sub to_branch {
96 11     11 0 17 my $self = shift;
97 11         20 $$self =~ s/^\///;
98 11         112 return $self;
99             }
100              
101             sub list {
102 23     23 0 50 my $self = shift;
103 23 100       101 Path::Abstract->_0_093_warn if $Path::Abstract::_0_093_warn;
104 23         579 return grep { length $_ } split m/\//, $$self;
  49         192  
105             }
106 5     5   33 for (qw()) { no strict 'refs'; *$_ = \&list }
  5         11  
  5         2872  
107              
108             sub split {
109 4     4 0 6 my $self = shift;
110 4 50       13 Path::Abstract->_0_093_warn if $Path::Abstract::_0_093_warn;
111 4         30 my @split = split m/(?<=.)\/(?=.)/, $$self;
112 4         33 return @split;
113             }
114              
115             sub first {
116 19     19 0 35 my $self = shift;
117 19 100       84 Path::Abstract->_0_093_warn if $Path::Abstract::_0_093_warn;
118 19         455 return $self->at(0);
119             }
120              
121             sub last {
122 22     22 0 56 my $self = shift;
123 22 100       87 Path::Abstract->_0_093_warn if $Path::Abstract::_0_093_warn;
124 22         472 return $self->at(-1);
125             }
126              
127             sub at {
128 65     65 0 137 my $self = shift;
129 65 100       200 return '' if $self->is_empty;
130 54         218 my @path = split '/', $$self;
131 54 50 66     194 return '' if 1 == @path && '' eq $path[0];
132 54         88 my $index = shift;
133 54 100 100     295 if (0 > $index) {
    100          
134 25         38 $index += @path;
135             }
136             elsif (! defined $path[0] || ! length $path[0]) {
137 19         39 $index += 1
138             }
139 54 100       161 return '' if $index >= @path;
140 48 100 100     369 $index -= 1 if $index == @path - 1 && ! defined $path[$index] || ! length $path[$index];
      66        
141 48 100 66     294 return '' unless defined $path[$index] && length $path[$index];
142 45         742 return $path[$index];
143             }
144              
145             sub beginning {
146 8     8 0 18 my $self = shift;
147 8         42 my ($beginning) = $$self =~ m{^(\/?[^/]*)};
148 8         38 return $beginning;
149             }
150              
151             sub ending {
152 38     38 0 56 my $self = shift;
153 38         190 my ($ending) = $$self =~ m{([^/]*\/?)$};
154 38         115 return $ending;
155             }
156              
157             sub get {
158 501     501 0 50339 my $self = shift;
159 501         3913 return $$self;
160             }
161 5     5   37 for (qw(path stringify)) { no strict 'refs'; *$_ = \&get }
  5         15  
  5         512  
162              
163             sub push {
164 73     73 0 109 my $self = shift;
165 73         168 $$self = _canonize $$self, @_;
166 73         390 return $self;
167             }
168 5     5   30 for (qw(down)) { no strict 'refs'; *$_ = \&push }
  5         11  
  5         3892  
169              
170             sub child {
171 15     15 0 955 my $self = shift;
172 15         44 my $child = $self->clone;
173 15         54 return $child->push(@_);
174             }
175              
176             sub append {
177 10     10 0 24 my $self = shift;
178 10 50       26 return $self unless @_;
179 10         38 $self->set($$self . join '/', @_);
180 10         21 return $self;
181             }
182              
183             sub extension {
184 30     30 0 49 my $self = shift;
185              
186 30         38 my $extension;
187 30 50 66     171 if (@_ && ! defined $_[0]) {
    100          
188 0         0 $extension = '';
189             }
190             elsif (ref $_[0] eq '') {
191 26         45 $extension = shift;
192             }
193              
194 30         32 my $options;
195 30 100       57 if (ref $_[0] eq 'HASH') {
196 5         8 $options = shift;
197             }
198             else {
199 25         63 $options = { match => shift };
200             }
201              
202 30   100     113 my $matcher = $options->{match} || 1;
203 30 100       79 if ('*' eq $matcher) {
204 1         3 $matcher = '';
205             }
206 30 50 66     202 if (ref $matcher eq 'Regexp') {
    50          
207             }
208             elsif ($matcher eq '' || $matcher =~ m/^\d+$/) {
209 30         366 $matcher = qr/((?:\.[^\.]+){1,$matcher})$/;
210             }
211             else {
212 0         0 $matcher = qr/$matcher/;
213             }
214              
215 30         86 my $ending = $self->ending;
216 30 100       63 if (! defined $extension) {
217 10 100 100     34 return '' if $self->is_empty || $self->is_root;
218 8         94 return join '', $ending =~ $matcher;
219             }
220             else {
221 20 100       67 if ('' eq $extension) {
    100          
222             }
223             elsif ($extension !~ m/^\./) {
224 3         6 $extension = '.' . $extension;
225             }
226              
227 20 100 100     48 if ($self->is_empty || $self->is_root) {
228 6         16 $self->append($extension);
229             }
230             else {
231 14 100       124 if ($ending =~ s/$matcher/$extension/) {
232 13         28 $self->pop;
233 13         36 $self->push($ending);
234             }
235             else {
236 1         4 $self->append($extension);
237             }
238             }
239 20         118 return $self;
240             }
241            
242             }
243              
244             my %pop_re = (
245             '' => qr{(/)?([^/]+)(/)?$},
246             '$' => qr{(/)?([^/]+/?)()$},
247             );
248              
249             sub _pop {
250 190     190   709 my $self = shift;
251 190 50       354 return '' if $self->is_empty;
252 190         441 my $count = shift @_;
253 190 100       444 $count = 1 unless defined $count;
254 190         223 my ($greedy_lead, $re);
255 190 100       3107 if ($count =~ s/([\^\$\*])$//) {
256 84 100       343 $greedy_lead = 1 if $1 ne '$';
257 84 100       325 $re = $pop_re{'$'} if $1 ne '^';
258             }
259 190 100       525 $re = $pop_re{''} unless $re;
260 190 100       445 $count = 1 unless length $count;
261              
262             {
263 190         287 my @popped;
  190         240  
264 5     5   37 no warnings 'uninitialized';
  5         17  
  5         1931  
265              
266 190         485 while ($count--) {
267 273 100       2692 if ($$self =~ s/$re//) {
268 262         515 my $popped = $2;
269 262 50       790 unshift(@popped, $popped) if $popped;
270 262 100       942 if (! length $$self) {
271 86 100       171 if ($greedy_lead) {
272 32         81 substr $popped[0], 0, 0, $1;
273             }
274             else {
275 54         131 $$self .= $1;
276             }
277 86         159 last;
278             }
279             }
280             else {
281 11         24 last;
282             }
283             }
284              
285 190         648 return \@popped;
286             }
287             }
288              
289             #my %pop_re = (
290             # '' => qr{(.)?([^/]+)/?$},
291             # '+' => qr{(.)?([^/]+)/?$},
292             # '*' => qr{(.)?([^/]+/?)$},
293             #);
294              
295             #sub _pop {
296             # my $self = shift;
297             # return '' if $self->is_empty;
298             # my $count = shift @_;
299             # $count = 1 unless defined $count;
300             # my ($greed, $greed_plus, $greed_star);
301             # if ($count =~ s/([+*])$//) {
302             # $greed = $1;
303             # if ($greed eq '+') { $greed_plus = 1 }
304             # else { $greed_star = 1 }
305             # }
306             # else {
307             # $greed = '';
308             # }
309             # my $re = $pop_re{$greed};
310             # $count = 1 unless length $count;
311             # my @popped;
312              
313             # while ($count--) {
314             # if ($$self =~ s/$re//) {
315             # my $popped = $2;
316             # unshift(@popped, $popped) if $popped;
317             # if ($1 && $1 eq '/' && ! length $$self) {
318             # if ($greed) {
319             # substr $popped[0], 0, 0, $1;
320             # }
321             # else {
322             # $$self = $1;
323             # }
324             # last;
325             # }
326             # elsif (! $$self) {
327             # last;
328             # }
329             # }
330             # }
331             # return \@popped;
332             #}
333              
334             sub pop {
335 150     150 0 560 my $self = shift;
336 150 100       363 return (ref $self)->new('') if $self->is_empty;
337 137         365 my $popped = $self->_pop(@_);
338 137         620 return (ref $self)->new(join '/', @$popped);
339             }
340              
341             sub up {
342 69     69 0 141 my $self = shift;
343 69 100       142 return $self if $self->is_empty;
344 53         146 $self->_pop(@_);
345 53         600 return $self;
346             }
347              
348             #sub up {
349             # my $self = shift;
350             # return $self if $self->is_empty;
351             # my $count = 1;
352             # $count = shift @_ if @_;
353             # while (! $self->is_empty && $count--) {
354             # if ($$self =~ s/(^|^\/|\/)([^\/]+)$//) {
355             # if ($1 && ! length $$self) {
356             # $$self = $1;
357             # last;
358             # }
359             # elsif (! $$self) {
360             # last;
361             # }
362             # }
363             # }
364             # return $self;
365             #}
366              
367             sub parent {
368 11     11 0 17 my $self = shift;
369 11         24 my $parent = $self->clone;
370 11         31 return $parent->up(1, @_);
371             }
372              
373             BEGIN {
374 5     5   31 no strict 'refs';
  5         11  
  5         1383  
375 5     5   10 eval { require Path::Class };
  5         4908  
376 5 50       322381 if ($@) {
377 0         0 *dir = *file = sub { croak "Path::Class is not available" };
  0         0  
378             }
379             else {
380 5     1   47 *file = sub { return Path::Class::file(shift->get, @_) };
  1         10  
381 5     1   288 *dir = sub { return Path::Class::dir(shift->get, @_) };
  1         1311  
382             }
383             }
384              
385             1; # End of Path::Abstract::Underload
386              
387             __END__