File Coverage

blib/lib/File/Find/Rex.pm
Criterion Covered Total %
statement 75 102 73.5
branch 21 62 33.8
condition 10 24 41.6
subroutine 18 20 90.0
pod 8 8 100.0
total 132 216 61.1


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