File Coverage

lib/Path/FindDev/Object.pm
Criterion Covered Total %
statement 50 81 61.7
branch 12 26 46.1
condition 1 3 33.3
subroutine 11 13 84.6
pod 4 4 100.0
total 78 127 61.4


line stmt bran cond sub pod time code
1 2     2   76077 use 5.008; # utf8
  2         10  
  2         97  
2 2     2   12 use strict;
  2         5  
  2         126  
3 2     2   14 use warnings;
  2         17  
  2         70  
4 2     2   1246 use utf8;
  2         17  
  2         15  
5              
6             package Path::FindDev::Object;
7              
8             our $VERSION = '0.5.2';
9              
10             # ABSTRACT: Object oriented guts to FindDev
11              
12             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
13              
14             our $ENV_KEY_DEBUG = 'PATH_FINDDEV_DEBUG';
15             our $DEBUG = ( exists $ENV{$ENV_KEY_DEBUG} ? $ENV{$ENV_KEY_DEBUG} : undef );
16              
17              
18              
19              
20              
21              
22              
23              
24              
25              
26              
27              
28              
29             use Class::Tiny 0.010 'set', 'uplevel_max', {
30             nest_retry => sub {
31 1         35 return 0;
32             },
33             isdev => sub {
34 1         856 require Path::IsDev::Object;
35 1 50       2093 return Path::IsDev::Object->new( ( $_[0]->has_set ? ( set => $_[0]->set ) : () ) );
36             },
37 2     2   3016 };
  2         7561  
  2         25  
38              
39              
40              
41              
42              
43              
44              
45              
46              
47             ## no critic (RequireArgUnpacking)
48              
49              
50              
51              
52              
53              
54              
55 1     1 1 21 sub has_set { return exists $_[0]->{set} }
56              
57              
58              
59              
60              
61              
62              
63              
64              
65              
66              
67              
68              
69              
70              
71 1     1 1 7 sub has_uplevel_max { return exists $_[0]->{uplevel_max} }
72              
73              
74              
75              
76              
77              
78              
79              
80              
81              
82              
83              
84              
85              
86              
87              
88              
89              
90             my $instances = {};
91             my $instance_id = 0;
92              
93              
94              
95              
96              
97              
98              
99              
100              
101              
102              
103              
104              
105              
106             sub _instance_id {
107 0     0   0 my ($self) = @_;
108 0         0 require Scalar::Util;
109 0         0 my $addr = Scalar::Util::refaddr($self);
110 0 0       0 return $instances->{$addr} if exists $instances->{$addr};
111 0         0 $instances->{$addr} = sprintf '%x', $instance_id++;
112 0         0 return $instances->{$addr};
113             }
114              
115              
116              
117              
118              
119              
120              
121              
122              
123              
124             sub BUILD {
125 1     1 1 85 my ($self) = @_;
126 1 50       7 return $self unless $DEBUG;
127 0         0 $self->_debug('{');
128 0 0       0 $self->_debug( ' set => ' . $self->set ) if $self->has_set;
129 0 0       0 $self->_debug( ' uplevel_max => ' . $self->uplevel_max ) if $self->uplevel_max;
130 0         0 $self->_debug( ' nest_retry => ' . $self->nest_retry );
131 0         0 $self->_debug( ' isdev => ' . $self->isdev );
132 0         0 $self->_debug('}');
133 0         0 return $self;
134             }
135              
136              
137              
138              
139              
140              
141              
142              
143              
144              
145              
146              
147              
148             sub _debug {
149 3     3   98 my ( $self, $message ) = @_;
150 3 50       13 return unless $DEBUG;
151 0         0 my $id = $self->_instance_id;
152 0         0 return *STDERR->printf( qq{[Path::FindDev=%s] %s\n}, $id, $message );
153             }
154              
155              
156              
157              
158              
159              
160              
161              
162              
163             sub _error {
164 0     0   0 my ( $self, $message ) = @_;
165 0         0 my $id = $self->_instance_id;
166 0         0 my $f_message = sprintf qq{[Path::FindDev=%s] %s\n}, $id, $message;
167 0         0 require Carp;
168 0         0 Carp::croak($f_message);
169             }
170              
171              
172              
173              
174              
175              
176              
177              
178              
179              
180              
181              
182              
183              
184              
185              
186             sub _step {
187 2     2   3 my ( $self, $search_root, $dev_levels, $uplevels ) = @_;
188              
189 2 100       54 if ( $self->isdev->matches($search_root) ) {
190 1         3751 $self->_debug( 'Found dev dir' . $search_root );
191 1         3 ${$dev_levels}++;
  1         3  
192 1 50       2 return { type => 'found', path => $search_root } if ${$dev_levels} >= $self->nest_retry;
  1         30  
193 0         0 $self->_debug( sprintf 'Ignoring found dev dir due to dev_levels(%s) < nest_retry(%s)', ${$dev_levels}, $self->nest_retry );
  0         0  
194             }
195 1 50       98965 if ( $search_root->is_rootdir ) {
196 0         0 $self->_debug('OS Root hit ( ->is_rootdir )');
197 0         0 return { type => 'stop' };
198             }
199 1 50 33     15 if ( $self->has_uplevel_max and ${$uplevels} > $self->uplevel_max ) {
  0         0  
200 0         0 $self->_debug( 'Stopping search due to uplevels(%s) >= uplevel_max(%s)', ${$uplevels}, $self->uplevel_max );
  0         0  
201 0         0 return { type => 'stop' };
202             }
203              
204 1         5 return { type => 'next' };
205             }
206              
207              
208              
209              
210              
211              
212              
213              
214              
215             sub find_dev {
216 1     1 1 3 my ( $self, $path ) = @_;
217 1         8 require Path::Tiny;
218 1         5 my $search_root = Path::Tiny::path($path)->absolute->realpath;
219 1         197 $self->_debug( 'Finding dev for ' . $path );
220 1         2 my $dev_levels = 0;
221 1         2 my $uplevels = 0 - 1;
222 2         4 FLOW: {
223 1         21 $uplevels++;
224 2         8 my $result = $self->_step( $search_root, \$dev_levels, \$uplevels );
225 2 100       18 if ( 'next' eq $result->{type} ) {
226 1         6 $self->_debug( 'Trying ../ : ' . $search_root->parent );
227 1         8 $search_root = $search_root->parent;
228 1         35 redo FLOW;
229             }
230 1 50       8 if ( 'stop' eq $result->{type} ) {
231 0         0 return;
232             }
233 1 50       5 if ( 'found' eq $result->{type} ) {
234 1         8 return $result->{path};
235             }
236 0           $self->_error( 'Unexpected end of flow control with _step response type' . $result->{type} );
237             }
238 0           return;
239             }
240             1;
241              
242             __END__
243              
244             =pod
245              
246             =encoding UTF-8
247              
248             =head1 NAME
249              
250             Path::FindDev::Object - Object oriented guts to FindDev
251              
252             =head1 VERSION
253              
254             version 0.5.2
255              
256             =head1 SYNOPSIS
257              
258             require Path::FindDev::Object;
259             my $finder = Path::FindDev::Object->new();
260             my $dev = $finder->find_dev($path);
261              
262             =head1 DESCRIPTION
263              
264             This module implements the innards of L<< C<Path::FindDev>|Path::FindDev >>, and is
265             only recommended for use if the Exporter C<API> is insufficient for your needs.
266              
267             =head1 METHODS
268              
269             =head2 C<has_set>
270              
271             Determines if the C<set> attribute exists
272              
273             =head2 C<has_uplevel_max>
274              
275             Determines if the C<uplevel_max> attribute is provided.
276              
277             =head2 C<find_dev>
278              
279             Find a parent at, or above C<$OtherPath> that resembles a C<devel> directory.
280              
281             my $path = $object->find_dev( $OtherPath );
282              
283             =head1 ATTRIBUTES
284              
285             =head2 C<set>
286              
287             B<(optional)>
288              
289             The C<Path::IsDev::HeuristicSet> subclass for your desired Heuristics.
290              
291             =head2 C<uplevel_max>
292              
293             If provided, limits the number of C<uplevel> iterations done.
294              
295             ( that is, limits the number of times it will step up the hierarchy )
296              
297             =head2 C<nest_retry>
298              
299             The number of C<dev> directories to C<ignore> in the hierarchy.
300              
301             This is provided in the event you have a C<dev> directory within a C<dev> directory, and you wish
302             to resolve an outer directory instead of an inner one.
303              
304             By default, this is C<0>, or "stop at the first C<dev> directory"
305              
306             =head2 C<isdev>
307              
308             The L<< C<Path::IsDev>|Path::IsDev >> object that checks nodes for C<dev>-ishness.
309              
310             =head1 PRIVATE METHODS
311              
312             =head2 C<_instance_id>
313              
314             An opportunistic sequence number for help with debug messages.
315              
316             Note: This is not guaranteed to be unique per instance, only guaranteed
317             to be constant within the life of the object.
318              
319             Based on C<refaddr>, and giving out new ids when new C<refaddr>'s are seen.
320              
321             my $id = $object->_instance_id;
322              
323             =head2 C<BUILD>
324              
325             C<BUILD> is an implementation detail of C<Moo>/C<Moose>.
326              
327             This module hooks C<BUILD> to give a self report of the object
328             to C<*STDERR> after C<< ->new >> when under C<$DEBUG>
329              
330             =head2 C<_debug>
331              
332             The debugger callback.
333              
334             export PATH_FINDDEV_DEBUG=1
335              
336             to get debug info.
337              
338             $object->_debug($message);
339              
340             =head2 C<_error>
341              
342             The error reporting callback.
343              
344             $object->_error($message);
345              
346             =head2 C<_step>
347              
348             Inner code path of tree walking.
349              
350             my ($dev_levels, $uplevels ) = (0,0);
351              
352             my $result = $object->_step( path($somepath), \$dev_levels, \$uplevels );
353              
354             $result->{type} eq 'stop' # if flow control should end
355             $result->{type} eq 'next' # if flow control should ascend to parent
356             $result->{type} eq 'found' # if flow control has found the "final" dev directory
357              
358             =begin MetaPOD::JSON v1.1.0
359              
360             {
361             "namespace":"Path::FindDev::Object",
362             "interface":"class",
363             "inherits":"Class::Tiny::Object"
364             }
365              
366              
367             =end MetaPOD::JSON
368              
369             =head1 AUTHOR
370              
371             Kent Fredric <kentfredric@gmail.com>
372              
373             =head1 COPYRIGHT AND LICENSE
374              
375             This software is copyright (c) 2014 by Kent Fredric <kentfredric@gmail.com>.
376              
377             This is free software; you can redistribute it and/or modify it under
378             the same terms as the Perl 5 programming language system itself.
379              
380             =cut