File Coverage

blib/lib/File/Version.pm
Criterion Covered Total %
statement 15 95 15.7
branch 0 46 0.0
condition 0 28 0.0
subroutine 5 12 41.6
pod 0 7 0.0
total 20 188 10.6


line stmt bran cond sub pod time code
1             package File::Version;
2              
3 1     1   893 use 5.006;
  1         3  
  1         38  
4 1     1   5 use strict;
  1         1  
  1         27  
5 1     1   5 use warnings;
  1         10  
  1         25  
6 1     1   1021 use integer;
  1         9  
  1         4  
7              
8             require Exporter;
9             our @ISA = qw(Exporter);
10             our @EXPORT = qw(); # nothing to export, this is OO
11             our $VERSION = '0.02';
12 1     1   65 use Carp;
  1         2  
  1         1846  
13              
14             # file-private functions
15              
16             my $recursive_find = sub {
17             my $self = shift;
18             my $regex = shift;
19             my($loc_recursive, $rec_depth, @matches);
20             $loc_recursive = sub {
21             my $path = shift;
22             return if $self->{RECURSION_DEPTH} && (scalar @$path > $rec_depth);
23             my $str_path = join('/', @$path);
24             substr($str_path, 0, 0) = '/' unless $str_path =~ /^\.+\/?/;
25             opendir(PH, $str_path);
26             my @files = grep(!/^\.\.?$/, readdir(PH));
27             #weed out symbolic links unless FOLLOW_SYMBOLIC is true
28             @files = grep( !( -l $_ ), @files) unless $self->{FOLLOW_SYMBOLIC};
29             closedir(PH);
30             for(@files) {
31             my @temp = @$path;
32             push(@temp, $_);
33             my $string_loc = join('/', @temp);
34             substr($string_loc, 0, 0) = '/' unless $string_loc =~ /^\.+\/?/;
35             next unless (-r $string_loc);
36             if( -f $string_loc && /$regex/ ) {
37             push(@matches, $string_loc);
38             }
39             if( -d $string_loc ) {
40             &$loc_recursive(\@temp);
41             }
42             }
43             };
44              
45             for(@ { $self->{SEARCH_PATHS} }) {
46            
47             my @dir_parts = grep(/./, split '/');
48             $rec_depth = ($self->{RECURSION_DEPTH} + scalar @dir_parts);
49             &$loc_recursive([ @dir_parts ]);
50             }
51             @matches ? return \@matches : return;
52             };
53              
54             # constructor
55              
56             sub new {
57 0     0 0   my $invocant = shift;
58 0   0       my $class = ref($invocant) || $invocant;
59 0           my $self = { SEARCH_PATHS => [ qw(.) ], # search current directory
60             RECURSION_DEPTH => 0,
61             FOLLOW_SYMBOLIC => 0, # default: don't follow symlinks
62             };
63             # validate user supplied arguments
64 0           my %args = @_;
65 0 0 0       croak( "Invalid file" ) unless( $args{FILE} && -f $args{FILE} && -r _);
      0        
66 0 0         if($args{FILE} =~ /^(.*\/)?((?:\d+_)+?)(?:0_)*_(.+)$/) {
    0          
67 0 0         $self->{FILE} = $3 or croak( "Invalid file.\n" );
68 0   0       $self->{VERSION} = $2 || '';
69             }
70             elsif($args{FILE} =~ /^(.*\/)?(.+)$/) {
71 0 0         $self->{FILE} = $2 or croak( "Invalid file.\n" );
72 0           $self->{VERSION} = '';
73             }
74 0           else { croak( "Invalid file" ) };
75 0 0         $self->{FILE_PATH} = $1 if $1;
76 0 0         $self->{WHOLE} = $& or croak( "Invalid file.\n" );
77              
78 0 0 0       if ( $args{SEARCH_PATHS} && @{ $args{SEARCH_PATHS} }) {
  0            
79 0           for(@{ $args{SEARCH_PATHS} }) {
  0            
80 0 0 0       if( -d && -r) {
81 0           push @{ $self->{SEARCH_PATHS} }, $_;
  0            
82             }
83             else {
84 0           carp ( "Directory does not exist: $_\n");
85             }
86             }
87             }
88 0 0         if ($args{RECURSION_DEPTH}) {
89 0 0         croak ( "Invalid RECURSION_DEPTH: $args{RECURSION_DEPTH}\n")
90             unless $args{RECURSION_DEPTH} =~ /^[+]?\d+$/;
91 0           $self->{RECURSION_DEPTH} = $args{RECURSION_DEPTH};
92             }
93 0 0         $self->{FOLLOW_SYMBOLIC} = $args{FOLLOW_SYMBOLIC} ? 1 : 0;
94 0           bless $self, $class;
95 0           return $self;
96             }
97              
98             # OO functions
99              
100             sub recursion_depth {
101 0     0 0   my $self = shift;
102 0 0         if(@_) { $self->{RECURSION_DEPTH} = shift }
  0            
103 0           return $self->{RECURSION_DEPTH};
104             }
105              
106             sub follow_symbolic {
107 0     0 0   my $self = shift;
108 0           my %args = @_;
109 0 0         $self->{FOLLOW_SYMBOLIC} = $args{FOLLOW_SYMBOLIC} ? 1 : 0;
110 0           return $self->{FOLLOW_SYMBOLIC};
111             }
112              
113             sub search_paths {
114 0     0 0   my $self = shift;
115 0           my %args = @_;
116 0 0         if (@{ $args{SEARCH_PATHS} }) {
  0            
117 0           for(@{ $args{SEARCH_PATHS} }) {
  0            
118 0 0 0       if( -d && -r) {
119 0           push @{ $self->{SEARCH_PATHS} }, $_;
  0            
120             }
121             else {
122 0           carp ( "SEARCH_PATHS not set: $_\n");
123             }
124             }
125             }
126 0           else { carp ( "SEARCH_PATHS not set.\n"); }
127 0           return $self->{SEARCH_PATHS};
128             }
129              
130             sub locate_ancestors {
131 0     0 0   my $self = shift;
132 0           my @gens;
133 0           @gens = split('_', $self->{VERSION});
134 0           my $gen_count = 0;
135 0           while(@gens) {
136 0           my $match;
137 0           my $regex = join('_', @gens) . "_(?:0_)*_$self->{FILE}";
138 0   0       $regex = eval { qr/$regex/ } || croak "Invalid pattern.\n";
139 0 0         ($match = &$recursive_find($self, $regex)) && do {
140 0           for(@$match) { $self->{ANCESTORS}{$gen_count}{$_}++; }
  0            
141             };
142 0           $gen_count++;
143 0           pop(@gens);
144             };
145 0           return $self->{ANCESTORS};
146             }
147              
148             sub locate_descendants {
149 0     0 0   my $self = shift;
150 0           my $match;
151 0   0       my $regex = eval { qr/^$self->{VERSION}(?:\d+_)+?(?:0_)*_$self->{FILE}/ }
152             || croak "Invalid pattern.\n";
153 0 0         ($match = &$recursive_find($self, $regex)) && do {
154 0           for(@$match) {
155 0 0         if( my($gen) = /$self->{VERSION}((\d+_)+?)(?:0_)*_/) {
156 0           my $count = ($gen =~ tr/\_//);
157 0 0         (my $last = $+) =~ s/\D//g if $+;
158 0   0       $self->{DESCENDANTS}{$count}{$_} = $last || 0;
159             }
160             }
161             };
162 0           return $self->{DESCENDANTS};
163             };
164              
165             sub next_version {
166 0     0 0   my $self = shift;
167 0           my $high = 0;
168 0 0         &locate_descendants($self) unless $self->{DESCENDANTS};
169 0 0         $self->{DESCENDANTS} && do {
170 0           for(keys(% { $self->{DESCENDANTS}{1} } )) {
  0            
171 0 0         $high = $self->{DESCENDANTS}{1}{$_} if $high < $self->{DESCENDANTS}{1}{$_};
172             }
173             };
174 0           $self->{NEXT_VERSION} = $self->{VERSION} . ++$high . "__$self->{FILE}";
175 0           return $self->{NEXT_VERSION};
176             }
177              
178             1;
179             __END__