File Coverage

lib/Badger/Filesystem/Visitor.pm
Criterion Covered Total %
statement 89 96 92.7
branch 50 72 69.4
condition 16 27 59.2
subroutine 18 20 90.0
pod 10 18 55.5
total 183 233 78.5


line stmt bran cond sub pod time code
1             #========================================================================
2             #
3             # Badger::Filesystem::Visitor
4             #
5             # DESCRIPTION
6             # Base class visitor object for traversing a filesystem.
7             #
8             # AUTHOR
9             # Andy Wardley
10             #
11             #========================================================================
12              
13             package Badger::Filesystem::Visitor;
14              
15             use Badger::Class
16 1         14 version => 0.01,
17             debug => 0,
18             base => 'Badger::Base',
19             import => 'class',
20             utils => 'params',
21             constants => 'ARRAY CODE REGEX ON WILDCARD',
22             config => [
23             'files|accept|class:FILES',
24             'no_files|ignore|class:NO_FILES',
25             'dirs|directories|class:DIRS',
26             'no_dirs|no_directories|class:NO_DIRS',
27             'in_dirs|in_directories|enter|class:IN_DIRS',
28             'not_in_dirs|not_in_directories|leave|class:NOT_IN_DIRS',
29             'accept_file',
30             'reject_file',
31             'accept_dir|accept_directory',
32             'reject_dir|reject_directory',
33             'enter_dir|enter_directory',
34             'leave_dir|leave_directory',
35             ],
36             messages => {
37             no_node => 'No node specified to %s',
38             bad_filter => 'Invalid test in %s specification: %s',
39             },
40             alias => {
41             init => \&init_visitor,
42             collect_dir => \&collect_dir,
43             enter_dir => \&enter_directory,
44             visit_dir => \&visit_directory,
45             visit_dir_kids => \&visit_directory_children,
46 1     1   771 };
  1         1  
47              
48 1     1   6 use Badger::Debug ':dump';
  1         2  
  1         3  
49             our @FILTERS = qw( files dirs in_dirs no_files no_dirs not_in_dirs );
50             our $ALL = 0;
51             our $FILES = 1;
52             our $DIRS = 1;
53             our $IN_DIRS = 0;
54             our $NO_FILES = 0;
55             our $NO_DIRS = 0;
56             our $NOT_IN_DIRS = 0;
57              
58              
59             sub init_visitor {
60 14     14 0 29 my ($self, $config) = @_;
61 14         33 my $class = $self->class;
62 14         18 my ($item, $long);
63              
64 14         51 $self->configure($config);
65            
66             $self->{ in_dirs } = 1
67 14 100       40 if $config->{ recurse };
68            
69 14         26 $self->{ collect } = [ ];
70 14         38 $self->{ identify } = { };
71              
72 14         36 $self->init_filters;
73              
74 14         15 $self->debug("init_visitor() => ", $self->dump) if DEBUG;
75              
76 14         45 return $self;
77             }
78              
79              
80             sub init_filters {
81 14     14 0 16 my $self = shift;
82 14         25 my ($filter, $tests, $test, $type);
83            
84 14         26 foreach $filter (@FILTERS) {
85 84   100     162 $tests = $self->{ $filter } || next; # skip over false values
86 38         39 $self->debug("filter: $filter => $tests\n") if DEBUG;
87 38 100       95 $tests = $self->{ $filter } = [$tests]
88             unless ref $tests eq ARRAY;
89            
90             # NOTE: $test is aliasing list item so we can change it
91 38         60 foreach $test (@$tests) {
92 43 50       70 $self->debug(" - test: $test\n") if $DEBUG;
93 43 50       92 last unless $test; # false test always fails
94            
95 43 100       138 if ($type = ref $test) {
    100          
    100          
96 6 50 66     28 return $self->error_msg( bad_filter => $filter => $test )
97             unless $type eq CODE or $type eq REGEX;
98             # OK
99             }
100             elsif ($test eq ON) {
101             # OK
102             }
103             elsif ($test =~ WILDCARD) {
104             # changing test affects list item via regex
105 6         17 $test =~ s/\./<>/g; # . => <> (tmp)
106 6         17 $test =~ s/\?/./g; # ? => .
107 6         12 $test =~ s/\*/.*/g; # * => .*
108 6         11 $test =~ s/<>/\\./g; # <> => \.
109 6         85 $test = qr/^$test$/;
110 6 50       20 $self->debug("transmogrified wildcard into regex: $test\n") if $DEBUG;
111             }
112             }
113            
114             $self->debug(
115 38 50       68 "initialised $filter tests: ",
116             $self->dump_data_inline($tests),
117             "\n"
118             ) if $DEBUG;
119             }
120             }
121              
122              
123             sub visit {
124 14     14 1 18 my $self = shift;
125 14   50     51 my $node = shift || return $self->error_msg( no_node => 'visit' );
126 14         32 $node->enter($self);
127             }
128              
129              
130             sub visit_path {
131 0     0 1 0 my ($self, $path) = @_;
132             # TODO: we have nothing going on here
133 0 0       0 $self->debug("visiting path: $path\n") if $DEBUG;
134             }
135              
136              
137             sub visit_file {
138 196     196 1 276 my ($self, $file) = @_;
139              
140 196 100       289 return $self->filter_file($file)
141             ? $self->accept_file($file)
142             : $self->reject_file($file);
143             }
144              
145              
146             sub visit_directory {
147 60     60 1 85 my ($self, $dir) = @_;
148 60 50       97 $self->debug("visiting directory: $dir\n") if $DEBUG;
149              
150 60 100 50     109 $self->filter_directory($dir)
      50        
151             ? $self->accept_directory($dir) || return
152             : $self->reject_directory($dir) || return;
153              
154 60 100       119 return $self->filter_entry($dir)
155             ? $self->enter_directory($dir)
156             : $self->leave_directory($dir);
157             }
158              
159              
160             sub filter {
161 420     420 1 669 my ($self, $filter, $method, $item) = @_;
162 420   66     865 my $tests = $self->{ $filter } || do {
163             $self->debug("No filter defined for $filter") if DEBUG;
164             return 0;
165             };
166 282         308 my ($test, $type);
167              
168 282 50       401 $self->debug("filter($filter, $method, $item) tests: $tests\n") if $DEBUG;
169            
170 282         399 foreach $test (@$tests) {
171 318 50       492 $self->debug(" - test: $test\n") if $DEBUG;
172 318 100       610 if ($test eq ON) {
    100          
173 66         191 return 1;
174             }
175             elsif ($type = ref $test) {
176 132 100       230 if ($type eq CODE) {
    50          
177             # $self->debug("calling code: ". $test->($item, $self));
178 40 100       78 return 1 if $test->($item, $self);
179             }
180             elsif ($type eq REGEX) {
181 92 100       189 return 1 if $item->$method =~ $test;
182             }
183             else {
184 0         0 return $self->error_msg( bad_filter => $filter => $test );
185             }
186             }
187             else {
188 120 100       243 return 1 if $item->$method eq $test;
189             }
190             }
191 174 50       335 $self->debug(" - ALL FAIL - ignore\n") if $DEBUG;
192 174         553 return 0;
193             }
194              
195              
196             sub filter_file {
197 196     196 0 251 my ($self, $file) = @_;
198 196   66     271 return $self->filter( files => name => $file )
199             && ! $self->filter( no_files => name => $file );
200             }
201              
202              
203             sub filter_directory {
204 60     60 0 85 my ($self, $dir) = @_;
205 60   66     103 return $self->filter( dirs => name => $dir )
206             && ! $self->filter( no_dirs => name => $dir );
207             }
208              
209              
210             sub filter_entry {
211 60     60 0 75 my ($self, $dir) = @_;
212 60   100     82 return $self->filter( in_dirs => name => $dir )
213             && ! $self->filter( not_in_dirs => name => $dir );
214             }
215              
216              
217             sub accept_file {
218 43     43 1 68 my ($self, $file) = @_;
219 43         42 $self->debug("accept_file($file)") if DEBUG;
220             $self->{ accept_file }->($self, $file)
221 43 100       117 if $self->{ accept_file };
222 43         136 return $self->collect($file);
223              
224             # return $self->filter( files => name => @_ )
225             # && ! $self->filter( no_files => name => @_ );
226             }
227              
228              
229             sub reject_file {
230 153     153 0 225 my ($self, $file) = @_;
231 153         164 $self->debug("reject_file($file)") if DEBUG;
232             return $self->{ reject_file }
233 153 50       407 ? $self->{ reject_file }->($self, $file)
234             : 1;
235             }
236              
237              
238             sub accept_directory {
239 7     7 1 11 my ($self, $dir) = @_;
240 7         11 $self->debug("accept_dir($dir)") if DEBUG;
241             $self->{ accept_dir }->($self, $dir) || return
242 7 50 0     13 if $self->{ accept_dir };
243 7         13 return $self->collect($dir);
244             }
245              
246              
247             sub reject_directory {
248 53     53 0 80 my ($self, $dir) = @_;
249 53         55 $self->debug("reject_directory($dir)") if DEBUG;
250             return $self->{ reject_dir }
251 53 50       124 ? $self->{ reject_dir }->($self, $dir)
252             : 1;
253             }
254              
255             sub enter_directory {
256 64     64 1 107 my ($self, $dir) = @_;
257 64 50       101 $self->debug("visiting directory children: $dir") if $DEBUG;
258             $self->{ enter_dir }->($self, $dir) || return
259 64 50 0     115 if $self->{ enter_dir };
260            
261             $_->accept($self)
262 64         129 for $dir->children;
263             # for $dir->children($self->{ all });
264 64         366 return 1;
265             }
266              
267              
268             sub leave_directory {
269 10     10 0 24 my ($self, $dir) = @_;
270 10         16 $self->debug("leave_directory($dir)") if DEBUG;
271             return $self->{ leave_dir }
272 10 50       34 ? $self->{ leave_dir }->($self, $dir)
273             : 1;
274             }
275              
276              
277             sub collect {
278 63     63 1 79 my $self = shift;
279 63         78 my $collect = $self->{ collect };
280 63 100       128 push(@$collect, @_) if @_;
281             return wantarray
282 63 100       242 ? @$collect
283             : $collect;
284             }
285              
286              
287             # identify() is not currently used
288              
289             sub identify {
290 0     0 1   my ($self, $params) = self_params(@_);
291 0           my $identify = $self->{ identify };
292 0 0         @$identify{ keys %$params } = values %$params
293             if %$params;
294             return wantarray
295 0 0         ? %$identify
296             : $identify;
297             }
298              
299              
300             1;
301              
302             __END__