File Coverage

blib/lib/File/Find/Declare.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package File::Find::Declare;
2             our $VERSION = '0.62';
3              
4             # ABSTRACT: File::Find, declaratively
5              
6 14     14   41051 use strict; #shut up cpants
  14         32  
  14         469  
7 14     14   70 use warnings; #shut up cpants
  14         26  
  14         322  
8 14     14   28621 use Moose;
  0            
  0            
9             use Moose::Util::TypeConstraints;
10             use Moose::Meta::TypeConstraint;
11             use MooseX::StrictConstructor;
12             use File::Util;
13             use Text::Glob 'glob_to_regex';
14             use Number::Compare;
15             use Number::Compare::Date;
16              
17             #file size subtype with Number::Compare semantics
18             subtype 'Size'
19             => as 'Str'
20             => where { $_ =~ m/^(>=|>|<=|<){0,1}\d+(k|ki|m|mi|g|gi){0,1}$/i };
21              
22             #date subtype with Number::Compare::Date semantics
23             subtype 'Date'
24             => as 'Str';
25              
26             #available test directives
27             enum 'Directive' => qw(readable r_readable writable r_writable
28             executable r_executable owned r_owned
29             exists file empty directory
30             nonempty symlink fifo setuid
31             socket setgid block sticky
32             character tty modified accessed
33             ascii changed binary);
34              
35             #file permissions subtype
36             subtype 'Perms'
37             => as 'Str'
38             => where { $_ =~ m/^([r-][w-][x-]){3}$/ };
39              
40             #file bitmask subtype
41             subtype 'Bitmask'
42             => as 'Str'
43             => where { $_ =~ m/^[1-7]{3}$/ };
44              
45             #what the file names should look like
46             has 'like' => (
47             is => 'rw',
48             isa => 'Str|RegexpRef|ArrayRef[Str|RegexpRef]|Undef',
49             predicate => 'has_like',
50             reader => '_get_like',
51             writer => '_set_like',
52             );
53              
54             #what they shouldn't look like
55             has 'unlike' => (
56             is => 'rw',
57             isa => 'Str|RegexpRef|ArrayRef[Str|RegexpRef]|Undef',
58             predicate => 'has_unlike',
59             reader => '_get_unlike',
60             writer => '_set_unlike',
61             );
62              
63             #acceptable file extensions
64             has 'ext' => (
65             is => 'rw',
66             isa => 'Str|ArrayRef[Str]',
67             predicate => 'has_ext',
68             reader => '_get_ext',
69             writer => '_set_ext',
70             );
71              
72             #subs that should take one argument and return
73             #true or false if the file is 'good' or 'bad'
74             has 'subs' => (
75             is => 'rw',
76             isa => 'CodeRef|ArrayRef[CodeRef]',
77             predicate => 'has_subs',
78             reader => '_get_subs',
79             writer => '_set_subs',
80             );
81              
82             #directories to look in
83             has 'dirs' => (
84             is => 'rw',
85             isa => 'Str|ArrayRef[Str]',
86             predicate => 'has_dirs',
87             reader => '_get_dirs',
88             writer => '_set_dirs',
89             );
90              
91             #acceptable file sizes, using Number::Compare semantics
92             has 'size' => (
93             is => 'rw',
94             isa => 'Size|ArrayRef[Size]',
95             predicate => 'has_size',
96             reader => '_get_size',
97             writer => '_set_size',
98             );
99              
100             #created date/time using Number::Compare::Date
101             has 'changed' => (
102             is => 'rw',
103             isa => 'Date|ArrayRef[Date]',
104             predicate => 'has_changed',
105             reader => '_get_changed',
106             writer => '_set_changed',
107             );
108              
109             #modified date/time using Number::Compare::Date/Number::Compare::Duration
110             has 'modified' => (
111             is => 'rw',
112             isa => 'Date|ArrayRef[Date]',
113             predicate => 'has_modified',
114             reader => '_get_modified',
115             writer => '_set_modified',
116             );
117              
118             #accessed date/time using Number::Compare::Date/Number::Compare::Duration
119             has 'accessed' => (
120             is => 'rw',
121             isa => 'Date|ArrayRef[Date]',
122             predicate => 'has_accessed',
123             reader => '_get_accessed',
124             writer => '_set_accessed',
125             );
126              
127             #recursively process subdirectories?
128             has 'recurse' => (
129             is => 'rw',
130             isa => 'Int',
131             default => 0,
132             reader => '_get_recurse',
133             writer => '_set_recurse',
134             );
135              
136             #filetest directives the file should be like
137             has 'is' => (
138             is => 'rw',
139             isa => 'Directive|ArrayRef[Directive]',
140             predicate => 'has_is',
141             reader => '_get_is',
142             writer => '_set_is',
143             );
144              
145             #filetest directives the file shouldn't be like
146             has 'isnt' => (
147             is => 'rw',
148             isa => 'Directive|ArrayRef[Directive]',
149             predicate => 'has_isnt',
150             reader => '_get_isnt',
151             writer => '_set_isnt',
152             );
153              
154             #file owner(s)
155             has 'owner' => (
156             is => 'rw',
157             isa => 'Str|ArrayRef[Str]',
158             predicate => 'has_owner',
159             reader => '_get_owner',
160             writer => '_set_owner',
161             );
162              
163             #file group(s)
164             has 'group' => (
165             is => 'rw',
166             isa => 'Str|ArrayRef[Str]',
167             predicate => 'has_group',
168             reader => '_get_group',
169             writer => '_set_group',
170             );
171              
172             #file permissions
173             has 'perms' => (
174             is => 'rw',
175             isa => 'Perms|Bitmask|ArrayRef[Perms|Bitmask]',
176             predicate => 'has_perms',
177             reader => '_get_perms',
178             writer => '_set_perms',
179             );
180              
181             #where to hold our files before we return them
182             has 'files' => (
183             is => 'ro',
184             isa => 'ArrayRef[Str]',
185             writer => '_set_files',
186             default => sub { [] },
187             );
188              
189              
190             sub BUILD {
191             my $self = shift;
192            
193             $self->like($self->like()) if $self->has_like(); #like
194             $self->unlike($self->unlike()) if $self->has_unlike(); #unlike
195             $self->dirs($self->dirs()) if $self->has_dirs(); #dirs
196             $self->ext($self->ext()) if $self->has_ext(); #ext
197             $self->subs($self->subs()) if $self->has_subs(); #subs
198             $self->size($self->size()) if $self->has_size(); #size
199             $self->changed($self->changed()) if $self->has_changed(); #changed
200             $self->modified($self->modified()) if($self->has_modified()); #modified
201             $self->accessed($self->accessed()) if $self->has_accessed(); #accessed
202             $self->is($self->is()) if $self->has_is(); #is
203             $self->isnt($self->isnt()) if $self->has_isnt(); #isnt
204             $self->owner($self->owner()) if $self->has_owner(); #owner
205             $self->group($self->group()) if $self->has_group(); #group
206             $self->perms($self->perms()) if $self->has_perms(); #perms
207             }
208              
209              
210             sub dirs {
211             my $self = shift;
212             my $dirs = shift;
213            
214             if(!defined($dirs)) {
215             return $self->_get_dirs();
216             }
217            
218             if(Moose::Util::TypeConstraints::find_or_parse_type_constraint('Str')->check($dirs)) {
219             return $self->_set_dirs([$dirs]);
220             } else {
221             return $self->_set_dirs($dirs);
222             }
223             }
224              
225              
226             sub ext {
227             my $self = shift;
228             my $ext = shift;
229            
230             if(!defined($ext)) {
231             return $self->_get_ext();
232             }
233            
234             if(Moose::Util::TypeConstraints::find_or_parse_type_constraint('Str')->check($ext)) {
235             return $self->_set_ext([$ext]);
236             } else {
237             return $self->_set_ext($ext);
238             }
239             }
240              
241              
242             sub subs {
243             my $self = shift;
244             my $subs = shift;
245            
246             if(!defined($subs)) {
247             return $self->_get_subs();
248             }
249            
250             if(Moose::Util::TypeConstraints::find_or_parse_type_constraint('CodeRef')->check($subs)) {
251             return $self->_set_subs([$subs]);
252             } else {
253             return $self->_set_subs($subs);
254             }
255             }
256              
257              
258             sub size {
259             my $self = shift;
260             my $size = shift;
261            
262             if(!defined($size)) {
263             return $self->_get_size();
264             }
265            
266             if(Moose::Util::TypeConstraints::find_or_parse_type_constraint('Size')->check($size)) {
267             return $self->_set_size([$size]);
268             } else {
269             return $self->_set_size($size);
270             }
271             }
272              
273              
274             sub changed {
275             my $self = shift;
276             my $changed = shift;
277              
278             if(!defined($changed)) {
279             return $self->_get_changed();
280             }
281              
282             if(Moose::Util::TypeConstraints::find_or_parse_type_constraint('Date')->check($changed)) {
283             return $self->_set_changed([$changed]);
284             } else {
285             return $self->_set_changed($changed);
286             }
287             }
288              
289              
290             sub modified {
291             my $self = shift;
292             my $modified = shift;
293              
294             if(!defined($modified)) {
295             return $self->_get_modified();
296             }
297              
298             if(Moose::Util::TypeConstraints::find_or_parse_type_constraint('Date')->check($modified)) {
299             return $self->_set_modified([$modified]);
300             } else {
301             return $self->_set_modified($modified);
302             }
303             }
304              
305              
306             sub accessed {
307             my $self = shift;
308             my $accessed = shift;
309              
310             if(!defined($accessed)) {
311             return $self->_get_accessed();
312             }
313              
314             if(Moose::Util::TypeConstraints::find_or_parse_type_constraint('Date')->check($accessed)) {
315             return $self->_set_accessed([$accessed]);
316             } else {
317             return $self->_set_accessed($accessed);
318             }
319             }
320              
321              
322             sub is {
323             my $self = shift;
324             my $is = shift;
325            
326             if(!defined($is)) {
327             return $self->_get_is();
328             }
329            
330             if(Moose::Util::TypeConstraints::find_or_parse_type_constraint('Directive')->check($is)) {
331             $self->_set_is([$is]);
332             } else {
333             return $self->_set_is($is);
334             }
335             }
336              
337              
338             sub isnt {
339             my $self = shift;
340             my $isnt = shift;
341            
342             if(!defined($isnt)) {
343             return $self->_get_isnt();
344             }
345            
346             if(Moose::Util::TypeConstraints::find_or_parse_type_constraint('Directive')->check($isnt)) {
347             $self->_set_isnt([$isnt]);
348             } else {
349             return $self->_set_isnt($isnt);
350             }
351             }
352              
353              
354             sub owner {
355             my $self = shift;
356             my $owner = shift;
357            
358             if (!defined($owner)) {
359             return $self->_get_owner();
360             }
361            
362             if(Moose::Util::TypeConstraints::find_or_parse_type_constraint('Str')->check($owner)) {
363             return $self->_set_owner([$owner]);
364             } else {
365             return $self->_set_owner($owner);
366             }
367             }
368              
369              
370             sub group {
371             my $self = shift;
372             my $group = shift;
373            
374             if (!defined($group)) {
375             return $self->_get_group();
376             }
377            
378             if(Moose::Util::TypeConstraints::find_or_parse_type_constraint('Str')->check($group)) {
379             return $self->_set_group([$group]);
380             } else {
381             return $self->_set_group($group);
382             }
383             }
384              
385              
386             sub perms {
387             my $self = shift;
388             my $perms = shift;
389            
390             if(!defined($perms)) {
391             return $self->_get_perms();
392             }
393            
394             if(Moose::Util::TypeConstraints::find_or_parse_type_constraint('Perms')->check($perms)
395             || Moose::Util::TypeConstraints::find_or_parse_type_constraint('Bitmask')->check($perms)) {
396             return $self->_set_perms([$perms]);
397             } else {
398             return $self->_set_perms($perms);
399             }
400             }
401              
402              
403             sub recurse {
404             my $self = shift;
405             my $recurse = shift;
406            
407             if(!defined($recurse)) {
408             return $self->_get_recurse();
409             }
410            
411             return $self->_set_recurse($recurse);
412             }
413              
414              
415             sub like {
416             my $self = shift;
417             my $like = shift;
418            
419             if(!defined($like)) {
420             return $self->_get_like();
421             }
422            
423             return $self->_set_like(_like_processor($like));
424             }
425              
426              
427             sub unlike {
428             my $self = shift;
429             my $unlike = shift;
430            
431             if(!defined($unlike)) {
432             return $self->_get_unlike();
433             }
434            
435             return $self->_set_unlike(_like_processor($unlike));
436             }
437              
438             sub _like_processor {
439             my ($like) = @_;
440            
441             if(Moose::Util::TypeConstraints::find_or_parse_type_constraint('Str')->check($like)) {
442             #convert to regex, and put in a one-element arrayref
443             $like = [glob_to_regex($like)];
444             } elsif(Moose::Util::TypeConstraints::find_or_parse_type_constraint('RegexpRef')->check($like)) {
445             #convert to a one-element arrayref
446             $like = [$like];
447             } elsif(Moose::Util::TypeConstraints::find_or_parse_type_constraint('ArrayRef')->check($like)) {
448             #check each element in the array ref, and make them all regexen
449             for(my $i = 0; $i <= $#{$like}; ++$i) {
450             if(Moose::Util::TypeConstraints::find_or_parse_type_constraint('Str')->check($like->[$i])) {
451             $like->[$i] = glob_to_regex($like->[$i]);
452             }
453             }
454             } else {
455             #should never happen
456             Carp::croak("Invalid type encountered for an element of like with value $like");
457             }
458            
459             return $like;
460             }
461              
462              
463             sub find {
464             my $self = shift;
465            
466             my @files = ();
467             my @df = ();
468             my $dh;
469             my @dirs;
470             if(defined($self->dirs())) {
471             @dirs = @{$self->dirs()};
472             } else {
473             @dirs = ();
474             }
475            
476             #don't repeat ourselves
477             my %found_dirs = ();
478            
479             #consider each directory
480             while(my $dir = shift @dirs) {
481            
482             #don't repeat ourselves
483             next if defined($found_dirs{$dir});
484             $found_dirs{$dir} = 1;
485            
486             #read the files from the directory, getting rid of . and ..
487             opendir($dh, $dir) or Carp::croak "Could not open directory $dir for reading\n"; #should I warn here?
488             @df = grep { $_ !~ /^\.$/ && $_ !~ /^\.\.$/ } readdir($dh);
489            
490             #append the directory name to the files
491             for(my $i = 0; $i <= $#df; ++$i) {
492             if($dir =~ m!/$!) {
493             $df[$i] = $dir.$df[$i];
494             } else {
495             $df[$i] = $dir.'/'.$df[$i];
496             }
497             }
498            
499             #test each file
500             foreach my $file (@df) {
501             if($self->_test_like($file)
502             && $self->_test_unlike($file)
503             && $self->_test_ext($file)
504             && $self->_test_subs($file)
505             && $self->_test_size($file)
506             && $self->_test_changed($file)
507             && $self->_test_modified($file)
508             && $self->_test_accessed($file)
509             && $self->_test_is($file)
510             && $self->_test_isnt($file)
511             && $self->_test_owner($file)
512             && $self->_test_group($file)
513             && $self->_test_perms($file)) {
514             push(@files, $file);
515             }
516            
517             if($self->recurse() && -d $file) {
518             push(@dirs, $file);
519             }
520             }
521             }
522            
523             $self->_set_files(\@files);
524            
525             #return files found
526             return @files;
527             }
528              
529             sub _test_like {
530             my ($self, $file) = @_;
531            
532             if(!$self->has_like()) { return 1; }
533            
534             foreach my $re (@{$self->like()}) {
535             return 0 if $file !~ $re;
536             }
537            
538             return 1;
539             }
540              
541             sub _test_unlike {
542             my ($self, $file) = @_;
543            
544             if(!$self->has_unlike()) { return 1; }
545            
546             foreach my $re (@{$self->unlike()}) {
547             return 0 if $file =~ $re;
548             }
549            
550             return 1;
551             }
552              
553             sub _test_ext { #ANY extension
554             my ($self, $file) = @_;
555            
556             if(!$self->has_ext()) { return 1; }
557            
558             foreach my $ext (@{$self->ext()}) {
559             return 1 if $file =~ qr/${ext}$/;
560             }
561            
562             return 0;
563             }
564              
565             sub _test_subs {
566             my ($self, $file) = @_;
567            
568             if(!$self->has_subs()) { return 1; }
569            
570             foreach my $sub (@{$self->subs()}) {
571             return 0 if !&$sub($file);
572             }
573            
574             return 1;
575             }
576              
577             sub _test_size {
578             my ($self, $file) = @_;
579            
580             if(!$self->has_size()) { return 1; }
581            
582             my ($f) = File::Util->new();
583            
584             foreach my $size (@{$self->size()}) {
585             return 0 if !Number::Compare->new($size)->test($f->size($file));
586             }
587            
588             return 1;
589             }
590              
591             sub _test_changed {
592             my ($self, $file) = @_;
593            
594             if(!$self->has_changed()) { return 1; }
595            
596             my ($f) = File::Util->new();
597            
598             foreach my $date (@{$self->changed()}) {
599             return 0 if !Number::Compare::Date->new($date)->test($f->last_changed($file));
600             }
601            
602             return 1;
603             }
604              
605             sub _test_modified {
606             my ($self, $file) = @_;
607            
608             if(!$self->has_modified()) { return 1; }
609            
610             my ($f) = File::Util->new();
611            
612             foreach my $date (@{$self->modified()}) {
613             return 0 if !Number::Compare::Date->new($date)->test($f->last_modified($file));
614             }
615            
616             return 1;
617             }
618              
619             sub _test_accessed {
620             my ($self, $file) = @_;
621            
622             if(!$self->has_accessed()) { return 1; }
623            
624             my ($f) = File::Util->new();
625            
626             foreach my $date (@{$self->accessed()}) {
627             return 0 if !Number::Compare::Date->new($date)->test($f->last_access($file));
628             }
629            
630             return 1;
631             }
632              
633             sub _test_is {
634             my ($self, $file) = @_;
635            
636             if(!$self->has_is()) { return 1; }
637            
638             foreach my $directive (@{$self->is()}) {
639             if($directive eq 'readable') {
640             return 0 if !-r $file;
641             } elsif($directive eq 'r_readable') {
642             return 0 if !-R $file;
643             } elsif($directive eq 'writable') {
644             return 0 if !-w $file;
645             } elsif($directive eq 'r_writable') {
646             return 0 if !-W $file;
647             } elsif($directive eq 'executable') {
648             return 0 if !-x $file;
649             } elsif($directive eq 'r_executable') {
650             return 0 if !-X $file;
651             } elsif($directive eq 'owned') {
652             return 0 if !-o $file;
653             } elsif($directive eq 'r_owned') {
654             return 0 if !-O $file;
655             } elsif($directive eq 'exists') {
656             return 0 if !-e $file;
657             } elsif($directive eq 'file') {
658             return 0 if !-f $file;
659             } elsif($directive eq 'empty') {
660             return 0 if !-z $file;
661             } elsif($directive eq 'directory') {
662             return 0 if !-d $file;
663             } elsif($directive eq 'nonempty') {
664             return 0 if !-s $file;
665             } elsif($directive eq 'symlink') {
666             return 0 if !-l $file;
667             } elsif($directive eq 'fifo') {
668             return 0 if !-p $file;
669             } elsif($directive eq 'setuid') {
670             return 0 if !-u $file;
671             } elsif($directive eq 'socket') {
672             return 0 if !-S $file;
673             } elsif($directive eq 'setgid') {
674             return 0 if !-g $file;
675             } elsif($directive eq 'block') {
676             return 0 if !-b $file;
677             } elsif($directive eq 'sticky') {
678             return 0 if !-k $file;
679             } elsif($directive eq 'character') {
680             return 0 if !-c $file;
681             } elsif($directive eq 'tty') {
682             return 0 if !-t $file;
683             } elsif($directive eq 'modified') {
684             return 0 if !-M $file;
685             } elsif($directive eq 'accessed') {
686             return 0 if !-A $file;
687             } elsif($directive eq 'ascii') {
688             return 0 if !-T $file;
689             } elsif($directive eq 'changed') {
690             return 0 if !-C $file;
691             } elsif($directive eq 'binary') {
692             return 0 if !-B $file;
693             }
694             }
695            
696             return 1;
697             }
698              
699             sub _test_isnt {
700             my ($self, $file) = @_;
701            
702             if(!$self->has_isnt()) { return 1; }
703            
704             foreach my $directive (@{$self->isnt()}) {
705             if($directive eq 'readable') {
706             return 0 if -r $file;
707             } elsif($directive eq 'r_readable') {
708             return 0 if -R $file;
709             } elsif($directive eq 'writable') {
710             return 0 if -w $file;
711             } elsif($directive eq 'r_writable') {
712             return 0 if -W $file;
713             } elsif($directive eq 'executable') {
714             return 0 if -x $file;
715             } elsif($directive eq 'r_executable') {
716             return 0 if -X $file;
717             } elsif($directive eq 'owned') {
718             return 0 if -o $file;
719             } elsif($directive eq 'r_owned') {
720             return 0 if -O $file;
721             } elsif($directive eq 'exists') {
722             return 0 if -e $file;
723             } elsif($directive eq 'file') {
724             return 0 if -f $file;
725             } elsif($directive eq 'empty') {
726             return 0 if -z $file;
727             } elsif($directive eq 'directory') {
728             return 0 if -d $file;
729             } elsif($directive eq 'nonempty') {
730             return 0 if -s $file;
731             } elsif($directive eq 'symlink') {
732             return 0 if -l $file;
733             } elsif($directive eq 'fifo') {
734             return 0 if -p $file;
735             } elsif($directive eq 'setuid') {
736             return 0 if -u $file;
737             } elsif($directive eq 'socket') {
738             return 0 if -S $file;
739             } elsif($directive eq 'setgid') {
740             return 0 if -g $file;
741             } elsif($directive eq 'block') {
742             return 0 if -b $file;
743             } elsif($directive eq 'sticky') {
744             return 0 if -k $file;
745             } elsif($directive eq 'character') {
746             return 0 if -c $file;
747             } elsif($directive eq 'tty') {
748             return 0 if -t $file;
749             } elsif($directive eq 'modified') {
750             return 0 if -M $file;
751             } elsif($directive eq 'accessed') {
752             return 0 if -A $file;
753             } elsif($directive eq 'ascii') {
754             return 0 if -T $file;
755             } elsif($directive eq 'changed') {
756             return 0 if -C $file;
757             } elsif($directive eq 'binary') {
758             return 0 if -B $file;
759             }
760             }
761            
762             return 1;
763             }
764              
765             sub _test_owner { #ANY
766             my ($self, $file) = @_;
767            
768             if(!$self->has_owner()) { return 1; }
769            
770             my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat($file);
771             my $name = getpwuid($uid);
772            
773             foreach my $owner (@{$self->owner()}) {
774             return 1 if $owner eq $uid || $owner eq $name;
775             }
776            
777             return 0;
778             }
779              
780             sub _test_group { #ANY
781             my ($self, $file) = @_;
782            
783             if(!$self->has_group()) { return 1; }
784            
785             my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat($file);
786             my $name = getgrgid($gid);
787            
788             foreach my $group (@{$self->group()}) {
789             return 1 if $group eq $gid || $group eq $name;
790             }
791            
792             return 0;
793             }
794              
795             sub _test_perms { #ANY
796             my ($self, $file) = @_;
797            
798             if(!$self->has_perms()) { return 1; }
799            
800             my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat($file);
801             $mode = $mode & 07777;
802             $mode = sprintf('%03o', $mode);
803            
804             foreach my $perm (@{$self->perms()}) {
805             return 1 if $mode == $perm || $mode eq _perm_string_to_octal($perm);
806             }
807            
808             return 0;
809             }
810              
811             sub _perm_string_to_octal {
812             my $str = shift;
813            
814             if($str =~ m/^([r-][w-][x-])([r-][w-][x-])([r-][w-][x-])$/) {
815            
816             my $owner = $1;
817             my $group = $2;
818             my $other = $3;
819            
820             my $vals = {
821             '---' => 0,
822             '--x' => 1,
823             '-w-' => 2,
824             '-wx' => 3,
825             'r--' => 4,
826             'r-x' => 5,
827             'rw-' => 6,
828             'rwx' => 7,
829             };
830            
831             return $vals->{$owner} * 100 + $vals->{$group} * 10 + $vals->{$other};
832             }
833            
834             return $str;
835             }
836              
837              
838              
839             no Moose;
840              
841             1;
842              
843             __END__
844             =pod
845              
846             =head1 NAME
847              
848             File::Find::Declare - File::Find, declaratively
849              
850             =head1 VERSION
851              
852             version 0.62
853              
854             =head1 SYNOPSIS
855              
856             use File::Find::Declare;
857            
858             my $fff = File::Find::Declare->new({ like => qr/foo/, dirs => '/home' });
859             my @files = $fff->find();
860              
861             =head1 DESCRIPTION
862              
863             File::Find::Declare is an object-oriented way to go about find files. With
864             many ways to specify what your are looking for in a file, File::Find::Declare
865             is both simple and powerful. Configuration may be passed at construction
866             time, or it may be set after a new object is created, or set at construction
867             time and then altered later, should you wish to set defaults and then change
868             them.
869              
870             File::Find::Declare is an alternative to L<File::Find> and L<File::Find::Rule>. It is
871             meant to be much simpler than File::Find and behaves differently then
872             File::Find::Rule.
873              
874             =head1 Getting Started with File::Find::Declare
875              
876             File::Find::Declare supports several ways of setting up your search options, and
877             many more ways of specifying file attributes to search for. To use File::Find::Declare,
878             simply,
879              
880             use File::Find::Declare;
881              
882             No methods are exported. This module is meant to be used solely in an
883             object-oriented manner. You can then specify what you want to search for:
884              
885             my $sp = { like => qr/foo/, unlike => qr/bar/, dirs => [ '/home', '/etc' ] };
886              
887             This will search for files whose names contain I<foo>, don't contain I<bar>, and
888             which are located in either I</home> or I</etc>. Create a File::Find::Declare object,
889             like so:
890              
891             my $fff = File::Find::Declare->new($sp);
892              
893             Although you could have (of course) simply done this:
894              
895             my $fff = File::Find::Declare->new({
896             like => qr/foo/,
897             unlike => qr/bar/,
898             dirs => [ '/home', '/etc' ]
899             });
900              
901             And cut out the use of the intermediate variable C<$sp> entirely. To find your files,
902             call C<find()>:
903              
904             my @files = $fff->find();
905              
906             And that's it. To recap:
907              
908             use File::Find::Declare;
909             my $sp = { like => qr/foo/, unlike => qr/bar/, dirs => [ '/home', '/etc' ] };
910             my $fff = File::Find::Declare->new($sp);
911             my @files = $fff->find();
912              
913             =head1 Search Options
914              
915             File::Find::Declare has many possible search options. What follows is a listing of all
916             those options, followed by a discussion of them.
917              
918             my $sp = {
919             like => qr/foo/,
920             unlike => qr/bar/,
921             dirs => '/home',
922             ext => '.txt',
923             subs => sub { $_[0] =~ m/baz/ },
924             size => '>10K',
925             changed => '<2010-01-30',
926             modified => '<2010-01-30',
927             accessed => '>2010-01-30',
928             recurse => 1,
929             is => 'readable',
930             isnt => 'executable',
931             owner => 'foo',
932             group => 'none',
933             perms => 'wr-wr-wr-',
934             };
935              
936             =head2 Specifying like
937              
938             C<like> is how you specify what a filename should look like. You may use regular
939             expressions (using C<qr//>), globs (as strings), or an array reference containing
940             regular expressions and/or globs. If you provide an array reference, the filename
941             must match ALL the given regexen or globs.
942              
943             =head2 Specifying unlike
944              
945             C<unlike> is just the opposite of like. It specifies what a filename should not
946             look like. Again, you may use regular expressions (using C<qr//>), globs (as strings),
947             or an array reference containing regular expressions and/or globs. If you provide an array
948             reference, the filename must match NONE of the given regexen or globs.
949              
950             =head2 Specifying dirs
951              
952             C<dirs> is how you specify where to look. It may be either a string containing a single
953             directory name, or an array reference of directories to use.
954              
955             =head2 Specifying ext
956              
957             C<ext> allows you to search for files by extension. You may specify either a single string
958             containing an extension, or an array reference of such extensions. If you provide an array
959             reference, the filename will match ANY extension.
960              
961             =head2 Specifying subs
962              
963             C<subs> gives you the ability to define your own functions to inspect each filename. It
964             expects either a single code reference, or an array reference containing code references.
965             If you provide an array reference, ALL functions must return true for a given file for it
966             to be considered "matched".
967              
968             =head2 Specifying size
969              
970             C<size> uses L<Number::Compare> semantics to examine the file size. You are therefore free
971             to specify things like C<1024>, C<2K>, C<<=2048>, etc. It expects either a single string
972             containing a size specification, or an array reference containing such specifications. If
973             you provide an array reference, a file's size must match ALL the given constraints.
974              
975             =head2 Specifying changed
976              
977             C<changed> uses L<Number::Compare::Date> semantics to allow you to search based on a file's
978             inode changed time. You may use anything that L<Date::Parse> understands to specify a date,
979             as well as comparison operators to specify a relationship to that date. You should provide
980             either a single string containing such a specification, or an array reference of such
981             specifications. If you provide an array reference, a file's changed time must match ALL of
982             the given conditions.
983              
984             =head2 Specifying modified
985              
986             C<modified> uses L<Number::Compare::Date> semantics to allow you to search based on a file's
987             last modified time. You may use anything that L<Date::Parse> understands to specify a date,
988             as well as comparison operators to specify a relationship to that date. You should provide
989             either a single string containing such a specification, or an array reference of such
990             specifications. If you provide an array reference, a file's modified time must match ALL of
991             the given conditions.
992              
993             =head2 Specifying accessed
994              
995             C<accessed> uses L<Number::Compare::Date> semantics to allow you to search based on a file's
996             last accessed time. You may use anything that L<Date::Parse> understands to specify a date,
997             as well as comparison operators to specify a relationship to that date. You should provide
998             either a single string containing such a specification, or an array reference of such
999             specifications. If you provide an array reference, a file's accessed time must match ALL of
1000             the given conditions.
1001              
1002             =head2 Specifying recurse
1003              
1004             C<recurse> indicates to the module whether or not it should recursively process any directories
1005             it has found. The current behavior is to inspect all directories, not just those that would
1006             match the given predicates. In the future, configuration options will be available to modify this
1007             behavior.
1008              
1009             =head2 Specifying is
1010              
1011             C<is> allows you to test files using Perl's standard filetest operators. Each is given a string alias,
1012             as described below:
1013              
1014             Alias: Operator:
1015             readable -r
1016             r_readable -R
1017             writable -w
1018             r_writable -W
1019             executable -x
1020             r_executable -X
1021             owned -o
1022             r_owned -O
1023             exists -e
1024             file -f
1025             empty -z
1026             directory -d
1027             nonempty -s
1028             symlink -l
1029             fifo -p
1030             setuid -u
1031             socket -S
1032             setgid -g
1033             block -b
1034             sticky -k
1035             character -c
1036             tty -t
1037             modified -M
1038             accessed -A
1039             ascii -T
1040             changed -C
1041             binary -B
1042              
1043             C<is> expects either a string containing a single alias, or an array reference
1044             containing a list of such aliases. If you specify an array reference, any file
1045             must pass ALL of the given tests to be considered "matched".
1046              
1047             =head2 Specifying isnt
1048              
1049             C<isnt> is just the opposite of C<is>. C<isnt> expects either a string containing
1050             a single alias, or an array reference containing a list of such aliases. If you
1051             specify an array reference, any file must pass NONE of the given tests to be
1052             considered "matched". See also L<Specifying is>.
1053              
1054             =head2 Specifying owner
1055              
1056             C<owner> lets you search for a file based on it's owner. It expects either a
1057             string containing a username, a user's ID, or an array reference containing
1058             either of these things. If you specify an array reference, if a file has
1059             ANY matching owner, it will be returned.
1060              
1061             =head2 Specifying group
1062              
1063             C<group> lets you search for a file based on it's group. It expects either a
1064             string containing a groupname, a group's ID, or an array reference containing
1065             either of these things. If you specify an array reference, if a file has
1066             ANY matching group, it will be returned.
1067              
1068             =head2 Specifying perms
1069              
1070             C<perms> gives you the ability to search for a file based on its permissions.
1071             You may specify a string such as C<rwxrw-r--> or three digits such as C<752>,
1072             or an array reference containing either of these. If you specify an array
1073             reference, a file that matches ANY of the permissions given will be considered
1074             matched.
1075              
1076             =head1 METHODS
1077              
1078             =head2 dirs
1079              
1080             The method C<dirs> allows you to set the directories to search in after the
1081             object has been created. It expects an array reference, or a single string.
1082             If called with no arguments, it will return an array reference containing
1083             the directories to search in. See also L<Specifying dirs>.
1084              
1085             =head2 ext
1086              
1087             The method C<ext> allows you to set the extensions to search for after the
1088             object has been created. It expects an array reference, or a single string.
1089             If called with no arguments, it will return an array reference containing
1090             the extensions to search for. See also L<Specifying ext>.
1091              
1092             =head2 subs
1093              
1094             The method C<subs> allows you to set the subroutines to search with after the
1095             object has been created. It expects an array reference, or a single CodeRef.
1096             If called with no arguments, it will return an array reference containing
1097             the subroutines currently set. See also L<Specifying subs>.
1098              
1099             =head2 size
1100              
1101             The method C<ext> allows you to set the file size(s) to search for after the
1102             object has been created. It expects an array reference, or a single string.
1103             If called with no arguments, it will return an array reference containing
1104             the file size(s) to search for. See also L<Specifying size>.
1105              
1106             =head2 changed
1107              
1108             The method C<changed> allows you to set the changed time to search for after the
1109             object has been created. It expects an array reference, or a single string.
1110             If called with no arguments, it will return an array reference containing
1111             the changed time to search for. See also L<Specifying changed>.
1112              
1113             =head2 modified
1114              
1115             The method C<modified> allows you to set the modified time to search for after the
1116             object has been created. It expects an array reference, or a single string.
1117             If called with no arguments, it will return an array reference containing
1118             the modified time to search for. See also L<Specifying modified>.
1119              
1120             =head2 accessed
1121              
1122             The method C<accessed> allows you to set the accessed time to search for after the
1123             object has been created. It expects an array reference, or a single string.
1124             If called with no arguments, it will return an array reference containing
1125             the accessed time to search for. See also L<Specifying accessed>.
1126              
1127             =head2 is
1128              
1129             The method C<is> allows you to set the file tests to search with after the
1130             object has been created. It expects an array reference, or a single string.
1131             If called with no arguments, it will return an array reference containing
1132             the file tests to search with. See also L<Specifying is>.
1133              
1134             =head2 isnt
1135              
1136             The method C<isnt> allows you to set the file tests to search against after the
1137             object has been created. It expects an array reference, or a single string.
1138             If called with no arguments, it will return an array reference containing
1139             the file tests to search against. See also L<Specifying isnt>.
1140              
1141             =head2 owner
1142              
1143             The method C<owner> allows you to set the owner(s) to search for after the
1144             object has been created. It expects an array reference, or a single string.
1145             If called with no arguments, it will return an array reference containing
1146             the owner(s) to search for. See also L<Specifying owner>.
1147              
1148             =head2 group
1149              
1150             The method C<group> allows you to set the group(s) to search for after the
1151             object has been created. It expects an array reference, or a single string.
1152             If called with no arguments, it will return an array reference containing
1153             the group(s) to search for. See also L<Specifying group>.
1154              
1155             =head2 perms
1156              
1157             The method C<perms> allows you to set the permuission(s) to search for after the
1158             object has been created. It expects an array reference, or a single string.
1159             If called with no arguments, it will return an array reference containing
1160             the permission(s) to search for. See also L<Specifying perms>.
1161              
1162             =head2 recurse
1163              
1164             The method C<recurse> allows you to set the recursion property after the
1165             object has been created. It expects a single number, 0 for false, any other for true.
1166             If called with no arguments, it will return whatever recurse is currently set
1167             to. See also L<Specifying recurse>.
1168              
1169             =head2 like
1170              
1171             The method C<like> allows you to set the filename tests to search with after the
1172             object has been created. It expects an array reference, or a single string.
1173             If called with no arguments, it will return an array reference containing
1174             the filename tests to search with. See also L<Specifying like>.
1175              
1176             =head2 unlike
1177              
1178             The method C<unlike> allows you to set the filename tests to search against after the
1179             object has been created. It expects an array reference, or a single string.
1180             If called with no arguments, it will return an array reference containing
1181             the filename tests to search against. See also L<Specifying unlike>.
1182              
1183             =head2 find
1184              
1185             Invoking C<find> will cause the module to search for files based on the
1186             currently set options. It expects no arguments and returns an array of
1187             filenames.
1188              
1189             =for Pod::Coverage BUILD
1190              
1191             =head1 REPOSITORY
1192              
1193             The source code repository for this project is located at:
1194              
1195             http://github.com/f0rk/file-find-declare
1196              
1197             =head1 AUTHOR
1198              
1199             Ryan P. Kelly <rpkelly@cpan.org>
1200              
1201             =head1 COPYRIGHT AND LICENSE
1202              
1203             This software is Copyright (c) 2010 by Ryan P. Kelly.
1204              
1205             This is free software, licensed under:
1206              
1207             The MIT (X11) License
1208              
1209             =cut
1210