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         12 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   603 };
  1         2  
47              
48 1     1   7 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 19 my ($self, $config) = @_;
61 14         27 my $class = $self->class;
62 14         19 my ($item, $long);
63              
64 14         38 $self->configure($config);
65            
66             $self->{ in_dirs } = 1
67 14 100       27 if $config->{ recurse };
68            
69 14         24 $self->{ collect } = [ ];
70 14         29 $self->{ identify } = { };
71              
72 14         29 $self->init_filters;
73              
74 14         11 $self->debug("init_visitor() => ", $self->dump) if DEBUG;
75              
76 14         22 return $self;
77             }
78              
79              
80             sub init_filters {
81 14     14 0 15 my $self = shift;
82 14         16 my ($filter, $tests, $test, $type);
83            
84 14         20 foreach $filter (@FILTERS) {
85 84   100     145 $tests = $self->{ $filter } || next; # skip over false values
86 38         29 $self->debug("filter: $filter => $tests\n") if DEBUG;
87 38 100       71 $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         42 foreach $test (@$tests) {
92 43 50       56 $self->debug(" - test: $test\n") if $DEBUG;
93 43 50       73 last unless $test; # false test always fails
94            
95 43 100       101 if ($type = ref $test) {
    100          
    100          
96 6 50 66     18 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         11 $test =~ s/\?/./g; # ? => .
107 6         12 $test =~ s/\*/.*/g; # * => .*
108 6         8 $test =~ s/<>/\\./g; # <> => \.
109 6         71 $test = qr/^$test$/;
110 6 50       16 $self->debug("transmogrified wildcard into regex: $test\n") if $DEBUG;
111             }
112             }
113            
114             $self->debug(
115 38 50       63 "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     41 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 229 my ($self, $file) = @_;
139              
140 196 100       239 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 73 my ($self, $dir) = @_;
148 60 50       83 $self->debug("visiting directory: $dir\n") if $DEBUG;
149              
150 60 100 50     99 $self->filter_directory($dir)
      50        
151             ? $self->accept_directory($dir) || return
152             : $self->reject_directory($dir) || return;
153              
154 60 100       82 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 559 my ($self, $filter, $method, $item) = @_;
162 420   66     716 my $tests = $self->{ $filter } || do {
163             $self->debug("No filter defined for $filter") if DEBUG;
164             return 0;
165             };
166 282         254 my ($test, $type);
167              
168 282 50       337 $self->debug("filter($filter, $method, $item) tests: $tests\n") if $DEBUG;
169            
170 282         305 foreach $test (@$tests) {
171 318 50       378 $self->debug(" - test: $test\n") if $DEBUG;
172 318 100       541 if ($test eq ON) {
    100          
173 66         159 return 1;
174             }
175             elsif ($type = ref $test) {
176 132 100       191 if ($type eq CODE) {
    50          
177             # $self->debug("calling code: ". $test->($item, $self));
178 40 100       67 return 1 if $test->($item, $self);
179             }
180             elsif ($type eq REGEX) {
181 92 100       143 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       209 return 1 if $item->$method eq $test;
189             }
190             }
191 174 50       287 $self->debug(" - ALL FAIL - ignore\n") if $DEBUG;
192 174         461 return 0;
193             }
194              
195              
196             sub filter_file {
197 196     196 0 208 my ($self, $file) = @_;
198 196   66     224 return $self->filter( files => name => $file )
199             && ! $self->filter( no_files => name => $file );
200             }
201              
202              
203             sub filter_directory {
204 60     60 0 61 my ($self, $dir) = @_;
205 60   66     74 return $self->filter( dirs => name => $dir )
206             && ! $self->filter( no_dirs => name => $dir );
207             }
208              
209              
210             sub filter_entry {
211 60     60 0 70 my ($self, $dir) = @_;
212 60   100     66 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 50 my ($self, $file) = @_;
219 43         40 $self->debug("accept_file($file)") if DEBUG;
220             $self->{ accept_file }->($self, $file)
221 43 100       61 if $self->{ accept_file };
222 43         69 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 173 my ($self, $file) = @_;
231 153         127 $self->debug("reject_file($file)") if DEBUG;
232             return $self->{ reject_file }
233 153 50       345 ? $self->{ reject_file }->($self, $file)
234             : 1;
235             }
236              
237              
238             sub accept_directory {
239 7     7 1 10 my ($self, $dir) = @_;
240 7         4 $self->debug("accept_dir($dir)") if DEBUG;
241             $self->{ accept_dir }->($self, $dir) || return
242 7 50 0     14 if $self->{ accept_dir };
243 7         9 return $self->collect($dir);
244             }
245              
246              
247             sub reject_directory {
248 53     53 0 65 my ($self, $dir) = @_;
249 53         44 $self->debug("reject_directory($dir)") if DEBUG;
250             return $self->{ reject_dir }
251 53 50       111 ? $self->{ reject_dir }->($self, $dir)
252             : 1;
253             }
254              
255             sub enter_directory {
256 64     64 1 82 my ($self, $dir) = @_;
257 64 50       83 $self->debug("visiting directory children: $dir") if $DEBUG;
258             $self->{ enter_dir }->($self, $dir) || return
259 64 50 0     93 if $self->{ enter_dir };
260            
261             $_->accept($self)
262 64         112 for $dir->children;
263             # for $dir->children($self->{ all });
264 64         305 return 1;
265             }
266              
267              
268             sub leave_directory {
269 10     10 0 15 my ($self, $dir) = @_;
270 10         10 $self->debug("leave_directory($dir)") if DEBUG;
271             return $self->{ leave_dir }
272 10 50       28 ? $self->{ leave_dir }->($self, $dir)
273             : 1;
274             }
275              
276              
277             sub collect {
278 63     63 1 68 my $self = shift;
279 63         63 my $collect = $self->{ collect };
280 63 100       105 push(@$collect, @_) if @_;
281             return wantarray
282 63 100       184 ? @$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__