File Coverage

blib/lib/File/Find/Rex.pm
Criterion Covered Total %
statement 78 106 73.5
branch 23 70 32.8
condition 10 24 41.6
subroutine 19 21 90.4
pod 8 9 88.8
total 138 230 60.0


line stmt bran cond sub pod time code
1             # File: File/Find/Rex.pm
2             # Description:
3             # Revisions: 2018.01.21 - Roland Ayala - Created
4             #
5             # License: Artistic License 2.0
6             #
7             package File::Find::Rex;
8              
9 2     2   131672 use 5.006;
  2         68  
10 2     2   13 use strict;
  2         4  
  2         50  
11 2     2   11 use warnings;
  2         4  
  2         109  
12             our $VERSION = '1.02';
13              
14             # import modules
15 2     2   12 use Carp;
  2         4  
  2         120  
16 2     2   15 use Cwd 'abs_path';
  2         2  
  2         109  
17 2     2   15 use File::Basename;
  2         2  
  2         171  
18 2     2   13 use File::Find;
  2         5  
  2         104  
19 2     2   12 use File::Spec 'canonpath';
  2         3  
  2         85  
20 2     2   1289 use if $^O eq 'MSWin32', 'Win32::File';
  2         28  
  2         11  
21              
22             sub new {
23 1     1 1 88 my ( $class, $options, $callback ) = @_;
24 1 50 33     9 if ( defined $options && ref $options ne 'HASH' ) {
25 0         0 croak 'options expects hash reference';
26             }
27 1 50 33     4 if ( defined $callback && ref $callback ne 'CODE' ) {
28 0         0 croak 'callback expects code reference';
29             }
30              
31 1         4 my $self = {
32             _options => $options,
33             _callback => $callback
34             };
35 1         2 bless $self, $class;
36 1         3 return $self;
37             }
38              
39             sub set_option {
40 0     0 1 0 my ( $self, $key, $value ) = @_;
41 0 0       0 if ( defined $key ) {
42 0         0 $self->{_options}->{$key} = $value;
43             }
44 0         0 return;
45             }
46              
47             sub is_ignore_dirs {
48 1     1 1 3 my $self = shift;
49             return
50             defined $self->{_options}->{ignore_dirs}
51 1 50       11 ? $self->{_options}->{ignore_dirs} > 0
    50          
52             ? 1
53             : 0
54             : 0;
55             }
56              
57             sub is_ignore_hidden {
58 5     5 1 6 my $self = shift;
59             return
60             defined $self->{_options}->{ignore_hidden}
61 5 0       20 ? $self->{_options}->{ignore_hidden} > 0
    50          
62             ? 1
63             : 0
64             : 0;
65             }
66              
67             sub is_ignore_symlink {
68 5     5 0 7 my $self = shift;
69             return
70             defined $self->{_options}->{ignore_symlink}
71 5 0       14 ? $self->{_options}->{ignore_symlink} > 0
    50          
72             ? 1
73             : 0
74             : 0;
75             }
76              
77             sub is_recursive {
78 0     0 1 0 my $self = shift;
79             return
80             defined $self->{_options}->{recursive}
81 0 0       0 ? $self->{_options}->{recursive} > 0
    0          
82             ? 1
83             : 0
84             : 0;
85             }
86              
87             sub get_last_modified_earliest {
88 2     2 1 4 my $self = shift;
89 2         3 my $val = $self->{_options}->{last_modified_earliest};
90 2 50       5 if ( defined $val ) {
91              
92             # ensure that value set is an integer value, because caller should
93             # be setting option using epoch timevalue
94 0 0       0 $val =~ m/^[\d]*$/gxs or $val = undef;
95             }
96 2         5 return $val;
97             }
98              
99             sub get_last_modified_latest {
100 2     2 1 4 my $self = shift;
101 2         4 my $val = $self->{_options}->{last_modified_latest};
102 2 50       4 if ( defined $val ) {
103              
104             # ensure that value set is an integer value, because caller should
105             # be setting option using epoch timevalue
106 0 0       0 $val =~ m/^[\d]*$/gxs or $val = undef;
107             }
108 2         4 return $val;
109             }
110              
111             sub query {
112 1     1 1 9 my ( $self, $source, $regexp, $context ) = @_;
113 1 50       4 defined $source or croak 'source path expected';
114 1 50 33     21 if ( defined $regexp && ref $regexp ne 'Regexp' ) {
115 0         0 croak 'regular expression expected';
116             }
117              
118             # Initialize an empty array. If caller sets a callback then this empty
119             # array is returned, else wanted callback will push any files found onto
120             # the array.
121 1         3 my @files = ();
122              
123 1 50       19 if ( -e $source ) {
124              
125             # Get the absolute path in case caller specifies relative path to source
126             # directory so recursive behavior in find_files_wanted works correctly,
127             # and for logging purposes.
128 1         19 $source = abs_path($source);
129              
130 1 50       13 if ( -d $source ) # source is a directory
131             {
132 1         6 File::Find::find(
133             _make_wanted(
134             \&_callback, $self, \@files, $source, $regexp, $context
135             ),
136             $source
137             );
138             }
139             else # source is a file
140             {
141 0         0 _callback( $self, \@files, $source, $regexp, $context, 1 );
142             }
143             }
144             else {
145 0         0 warn 'No such file or directory' . "\n";
146             }
147              
148 1         9 return @files;
149             }
150              
151             sub _make_wanted {
152 1     1   3 my @args = @_; # freeze the args
153 1         3 my $wanted = shift @args;
154 1     6   87 return sub { $wanted->(@args); };
  6         17  
155             }
156              
157             sub _callback {
158 6     6   15 my ( $self, $files, $source, $regexp, $context, $dummy ) = @_;
159 6 50       14 my $file = defined $dummy ? $source : $File::Find::name;
160              
161             # if the file is a directory and caller has specified to ignore dirs in
162             # results set then jump to end.
163 6 100 66     80 unless ( -d $file && $self->is_ignore_dirs ) {
164             NEXT: {
165 5         11 my ( $fbase, $fdir, $ftype ) = fileparse( $file, '\.[^\.]*' );
  5         161  
166 5         15 my $filename = $fbase . $ftype;
167              
168             # handle ignore_hidden option
169 5 50       13 if ( $self->is_ignore_hidden ) {
170              
171             # method for determining if file is hidden depends on if windows
172             # or not.
173 0         0 my $is_visible;
174 0 0       0 if ( $^O eq 'MSWin32' ) {
175 0         0 my $attr;
176 0         0 Win32::File::GetAttributes( $file, $attr );
177 0         0 $is_visible = !( $attr & Win32::File::HIDDEN() );
178             }
179             else {
180 0         0 $is_visible = ( $filename !~ /^[.]/gxs );
181             }
182 0 0       0 $is_visible or last NEXT;
183             }
184              
185             # ignore if file is symbolic link and option set to ignore
186 5 50       11 if ( $self->is_ignore_symlink ) {
187 0 0       0 ! -l $file or last NEXT;
188             }
189              
190             # handle regex pattern rule if set
191 5 50 33     79 if ( defined $regexp && !-d $file ) {
192 5 100       36 $filename =~ $regexp or last NEXT;
193             }
194              
195             # handle last modified window rules if set
196 2         6 my $oldest = $self->get_last_modified_earliest;
197 2         12 my $newest = $self->get_last_modified_latest;
198 2 50 33     17 if ( defined $oldest || defined $newest ) {
199              
200             # capture last modified timestamp from file
201 0         0 my $timestamp = ( stat $file )[9];
202 0 0       0 if ( defined $oldest ) { $timestamp >= $oldest or last NEXT; }
  0 0       0  
203 0 0       0 if ( defined $newest ) { $timestamp <= $newest or last NEXT; }
  0 0       0  
204             }
205              
206 2         15 my $cfile = File::Spec->canonpath($file);
207 2 50       7 if ( defined $self->{_callback} ) {
208 0         0 $self->{_callback}->( $cfile, $context );
209             }
210             else {
211 2         3 push @{$files}, $cfile;
  2         6  
212             }
213             }
214             }
215 6 50 66     77 if ( -d $file && ( uc $file ) ne ( uc $source ) && !$self->is_recursive ) {
      33        
216 0         0 $File::Find::prune = 1;
217             }
218              
219 6         157 return;
220             }
221              
222             1; # End of File::Find::Rex
223              
224             __END__