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         15 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   928 };
  1         2  
47              
48 1     1   7 use Badger::Debug ':dump';
  1         2  
  1         4  
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 26 my ($self, $config) = @_;
61 14         32 my $class = $self->class;
62 14         21 my ($item, $long);
63              
64 14         53 $self->configure($config);
65            
66             $self->{ in_dirs } = 1
67 14 100       38 if $config->{ recurse };
68            
69 14         31 $self->{ collect } = [ ];
70 14         38 $self->{ identify } = { };
71              
72 14         34 $self->init_filters;
73              
74 14         16 $self->debug("init_visitor() => ", $self->dump) if DEBUG;
75              
76 14         31 return $self;
77             }
78              
79              
80             sub init_filters {
81 14     14 0 19 my $self = shift;
82 14         21 my ($filter, $tests, $test, $type);
83            
84 14         25 foreach $filter (@FILTERS) {
85 84   100     167 $tests = $self->{ $filter } || next; # skip over false values
86 38         38 $self->debug("filter: $filter => $tests\n") if DEBUG;
87 38 100       97 $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         53 foreach $test (@$tests) {
92 43 50       145 $self->debug(" - test: $test\n") if $DEBUG;
93 43 50       94 last unless $test; # false test always fails
94            
95 43 100       131 if ($type = ref $test) {
    100          
    100          
96 6 50 66     30 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         19 $test =~ s/\./<>/g; # . => <> (tmp)
106 6         17 $test =~ s/\?/./g; # ? => .
107 6         11 $test =~ s/\*/.*/g; # * => .*
108 6         15 $test =~ s/<>/\\./g; # <> => \.
109 6         91 $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       74 "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 19 my $self = shift;
125 14   50     51 my $node = shift || return $self->error_msg( no_node => 'visit' );
126 14         39 $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 261 my ($self, $file) = @_;
139              
140 196 100       283 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 89 my ($self, $dir) = @_;
148 60 50       109 $self->debug("visiting directory: $dir\n") if $DEBUG;
149              
150 60 100 50     120 $self->filter_directory($dir)
      50        
151             ? $self->accept_directory($dir) || return
152             : $self->reject_directory($dir) || return;
153              
154 60 100       115 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 632 my ($self, $filter, $method, $item) = @_;
162 420   66     957 my $tests = $self->{ $filter } || do {
163             $self->debug("No filter defined for $filter") if DEBUG;
164             return 0;
165             };
166 282         330 my ($test, $type);
167              
168 282 50       385 $self->debug("filter($filter, $method, $item) tests: $tests\n") if $DEBUG;
169            
170 282         378 foreach $test (@$tests) {
171 318 50       439 $self->debug(" - test: $test\n") if $DEBUG;
172 318 100       655 if ($test eq ON) {
    100          
173 66         250 return 1;
174             }
175             elsif ($type = ref $test) {
176 132 100       238 if ($type eq CODE) {
    50          
177             # $self->debug("calling code: ". $test->($item, $self));
178 40 100       75 return 1 if $test->($item, $self);
179             }
180             elsif ($type eq REGEX) {
181 92 100       178 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       273 return 1 if $item->$method eq $test;
189             }
190             }
191 174 50       364 $self->debug(" - ALL FAIL - ignore\n") if $DEBUG;
192 174         557 return 0;
193             }
194              
195              
196             sub filter_file {
197 196     196 0 261 my ($self, $file) = @_;
198 196   66     277 return $self->filter( files => name => $file )
199             && ! $self->filter( no_files => name => $file );
200             }
201              
202              
203             sub filter_directory {
204 60     60 0 77 my ($self, $dir) = @_;
205 60   66     97 return $self->filter( dirs => name => $dir )
206             && ! $self->filter( no_dirs => name => $dir );
207             }
208              
209              
210             sub filter_entry {
211 60     60 0 79 my ($self, $dir) = @_;
212 60   100     157 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 86 my ($self, $file) = @_;
219 43         43 $self->debug("accept_file($file)") if DEBUG;
220             $self->{ accept_file }->($self, $file)
221 43 100       83 if $self->{ accept_file };
222 43         87 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 211 my ($self, $file) = @_;
231 153         160 $self->debug("reject_file($file)") if DEBUG;
232             return $self->{ reject_file }
233 153 50       381 ? $self->{ reject_file }->($self, $file)
234             : 1;
235             }
236              
237              
238             sub accept_directory {
239 7     7 1 18 my ($self, $dir) = @_;
240 7         8 $self->debug("accept_dir($dir)") if DEBUG;
241             $self->{ accept_dir }->($self, $dir) || return
242 7 50 0     16 if $self->{ accept_dir };
243 7         12 return $self->collect($dir);
244             }
245              
246              
247             sub reject_directory {
248 53     53 0 77 my ($self, $dir) = @_;
249 53         61 $self->debug("reject_directory($dir)") if DEBUG;
250             return $self->{ reject_dir }
251 53 50       112 ? $self->{ reject_dir }->($self, $dir)
252             : 1;
253             }
254              
255             sub enter_directory {
256 64     64 1 113 my ($self, $dir) = @_;
257 64 50       160 $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         135 for $dir->children;
263             # for $dir->children($self->{ all });
264 64         398 return 1;
265             }
266              
267              
268             sub leave_directory {
269 10     10 0 16 my ($self, $dir) = @_;
270 10         15 $self->debug("leave_directory($dir)") if DEBUG;
271             return $self->{ leave_dir }
272 10 50       32 ? $self->{ leave_dir }->($self, $dir)
273             : 1;
274             }
275              
276              
277             sub collect {
278 63     63 1 85 my $self = shift;
279 63         72 my $collect = $self->{ collect };
280 63 100       128 push(@$collect, @_) if @_;
281             return wantarray
282 63 100       236 ? @$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__