File Coverage

blib/lib/Devel/Examine/Subs.pm
Criterion Covered Total %
statement 571 571 100.0
branch 271 286 94.7
condition 51 63 80.9
subroutine 56 57 98.2
pod 19 20 95.0
total 968 997 97.0


line stmt bran cond sub pod time code
1             package Devel::Examine::Subs;
2 58     58   1365457 use 5.008;
  58         151  
3 58     58   233 use warnings;
  58         60  
  58         1354  
4 58     58   214 use strict;
  58         69  
  58         2367  
5              
6             our $VERSION = '1.69';
7              
8 58     58   198 use Carp;
  58         85  
  58         3005  
9 58     58   25587 use Data::Compare;
  58         473698  
  58         285  
10 58     58   164528 use Data::Dumper;
  58         82458  
  58         2463  
11 58     58   22008 use Devel::Examine::Subs::Engine;
  58         100  
  58         1479  
12 58     58   22804 use Devel::Examine::Subs::Preprocessor;
  58         84  
  58         1528  
13 58     58   21136 use Devel::Examine::Subs::Postprocessor;
  58         86  
  58         1359  
14 58     58   241 use File::Basename;
  58         72  
  58         3443  
15 58     58   631 use File::Copy;
  58         1682  
  58         1990  
16 58     58   26575 use File::Edit::Portable;
  58         1086491  
  58         2329  
17 58     58   27612 use PPI;
  58         5241020  
  58         2194  
18 58     58   434 use Symbol qw(delete_package);
  58         82  
  58         5092  
19              
20             BEGIN {
21              
22             # we need to do some trickery for DTS due to circular referencing,
23             # which broke CPAN installs.
24              
25 58     58   101 eval {
26 58         9156 require Devel::Trace::Subs;
27             };
28              
29 58         180 eval {
30 58         242 import Devel::Trace::Subs qw(trace);
31             };
32              
33 58 50       311 if (! defined &trace){
34 58     0   231097 *trace = sub {};
35             }
36             };
37              
38             #
39             # public methods
40             #
41              
42             sub new {
43            
44             # set up for tracing
45              
46 147 100   147 1 125550 if ($ENV{DES_TRACE}){
47 3         11 $ENV{DTS_ENABLE} = 1;
48 3         8 $ENV{TRACE} = 1;
49             }
50              
51 147 100       435 trace() if $ENV{TRACE};
52              
53 146         264 my $self = {};
54 146         268 bless $self, shift;
55 146         538 my $p = $self->_params(@_);
56              
57             # default configs
58              
59 146         469 $self->{namespace} = __PACKAGE__;
60 146         349 $self->{params}{regex} = 1;
61 146         274 $self->{params}{backup} = 0;
62              
63 146         430 $self->_config($p);
64              
65 146         386 return $self;
66             }
67             sub all {
68            
69 27 100   27 1 10374 trace() if $ENV{TRACE};
70              
71 26         38 my $self = shift;
72 26         95 my $p = $self->_params(@_);
73              
74 26         71 $self->{params}{engine} = 'all';
75            
76 26         75 $self->run($p);
77             }
78             sub has {
79              
80 49 100   49 1 11430 trace() if $ENV{TRACE};
81              
82 48         75 my $self = shift;
83 48         144 my $p = $self->_params(@_);
84              
85 48         101 $self->{params}{post_proc} = 'file_lines_contain';
86 48         92 $self->{params}{engine} = 'has';
87            
88 48         138 $self->run($p);
89             }
90             sub missing {
91            
92 8 100   8 1 5240 trace() if $ENV{TRACE};
93              
94 7         15 my $self = shift;
95 7         27 my $p = $self->_params(@_);
96              
97 7         16 $self->{params}{engine} = 'missing';
98            
99 7         21 $self->run($p);
100             }
101             sub lines {
102            
103 8 100   8 1 3609 trace() if $ENV{TRACE};
104              
105 7         15 my $self = shift;
106 7         24 my $p = $self->_params(@_);
107              
108 7         18 $self->{params}{engine} = 'lines';
109            
110 7 100 66     32 if ($self->{params}{search} || $p->{search}){
111 5         10 $self->{params}{post_proc} = 'file_lines_contain';
112             }
113              
114 7         21 $self->run($p);
115             }
116             sub module {
117            
118 12 100   12 0 2868 trace() if $ENV{TRACE};
119              
120 11         15 my $self = shift;
121              
122 11         11 my $p;
123              
124             # allow for single string value
125              
126 11 100       21 if (@_ == 1){
127 4         4 my %p;
128 4         5 $p{module} = shift;
129 4         9 $p = $self->_params(%p);
130             }
131             else {
132 7         11 $p = $self->_params(@_);
133             }
134              
135             # set the preprocessor up, and have it return before
136             # the building/compiling of file data happens
137              
138 11         14 $self->{params}{pre_proc} = 'module';
139 11         13 $self->{params}{pre_proc_return} = 1;
140              
141 11         10 $self->{params}{engine} = 'module';
142              
143 11         22 $self->run($p);
144             }
145             sub objects {
146            
147 15 100   15 1 52242 trace() if $ENV{TRACE};
148              
149 14         25 my $self = shift;
150 14         31 my $p = $self->_params(@_);
151              
152 14         32 $self->{params}{post_proc} = 'subs';
153 14         23 $self->{params}{engine} = 'objects';
154              
155 14         138 $self->run($p);
156             }
157             sub search_replace {
158            
159 8 100   8 1 4964 trace() if $ENV{TRACE};
160              
161 7         14 my $self = shift;
162 7         17 my $p = $self->_params(@_);
163              
164             $self->{params}{post_proc}
165 7         19 = ['file_lines_contain', 'subs', 'objects'];
166              
167 7         9 $self->{params}{engine} = 'search_replace';
168              
169 7         16 $self->run($p);
170             }
171             sub replace {
172              
173 9 100   9 1 978 trace() if $ENV{TRACE};
174              
175 8         13 my $self = shift;
176 8         18 my $p = $self->_params(@_);
177              
178 8         15 $self->{params}{pre_proc} = 'replace';
179 8         11 $self->{params}{pre_proc_return} = 1;
180              
181 8         22 $self->run($p);
182             }
183             sub inject_after {
184            
185 8 100   8 1 838 trace() if $ENV{TRACE};
186              
187 7         13 my $self = shift;
188 7         15 my $p = $self->_params(@_);
189              
190 7 100 66     26 if (! $p->{injects} && ! $self->{params}{injects}){
191 3         7 $p->{injects} = 1;
192             }
193              
194             $self->{params}{post_proc}
195 7         23 = ['file_lines_contain', 'subs', 'objects'];
196              
197 7         12 $self->{params}{engine} = 'inject_after';
198              
199 7         19 $self->run($p);
200             }
201             sub inject {
202 8 100   8 1 3583 trace() if $ENV{TRACE};
203 7         14 my $self = shift;
204 7         15 my $p = $self->_params(@_);
205              
206             # inject_use/inject_after_sub_def are preprocs
207              
208 7 100 66     46 if ($p->{inject_use} || $p->{inject_after_sub_def} || defined $p->{line_num}){
      100        
209 6         9 $self->{params}{pre_proc} = 'inject';
210 6         8 $self->{params}{pre_proc_return} = 1;
211             }
212              
213 7         19 $self->run($p);
214             }
215             sub remove {
216 3 100   3 1 1402 trace() if $ENV{TRACE};
217              
218 2         8 my $self = shift;
219 2         6 my $p = $self->_params(@_);
220              
221 2         3 $self->{params}{pre_proc} = 'remove';
222 2         3 $self->{params}{pre_proc_return} = 1;
223              
224 2         6 $self->run($p);
225             }
226             sub order {
227 4 100   4 1 847 trace() if $ENV{TRACE};
228              
229 3         12 my $self = shift;
230              
231 3 100       13 if ($self->{params}{directory}){
232 1         200 confess "\norder() can only be called on an individual file, not " .
233             "a directory at this time\n\n";
234             }
235              
236 2         3 return @{ $self->{order} };
  2         11  
237             }
238             sub backup {
239 2 50   2 1 3694 trace() if $ENV{TRACE};
240              
241 2         4 my $self = shift;
242 2   100     10 my $state = shift || 0;
243              
244 2 50       6 $self->{params}{backup} = $state if defined $state;
245 2         4 return $self->{params}{backup};
246             }
247              
248             #
249             # publicly available semi-private developer methods
250             #
251              
252             sub add_functionality {
253              
254 7 100   7 1 908 trace() if $ENV{TRACE};
255            
256 6         13 my $self = shift;
257 6         16 my $p = $self->_params(@_);
258              
259 6         14 $self->_config($p);
260            
261 6         11 my $to_add = $self->{params}{add_functionality};
262 6         10 my $in_prod = $self->{params}{add_functionality_prod};
263              
264 6         22 my @allowed = qw(
265             pre_proc
266             post_proc
267             engine
268             );
269              
270 6         7 my $is_allowed = 0;
271              
272 6         11 for (@allowed){
273 15 100       67 if ($_ eq $to_add){
274 5         6 $is_allowed = 1;
275 5         8 last;
276             }
277             }
278            
279 6 100       21 if (! $is_allowed){
280 1         166 confess "Adding a non-allowed piece of functionality...\n";
281             }
282              
283             my %dt = (
284             pre_proc => sub {
285 1 50   1   3 trace() if $ENV{TRACE};
286             return $in_prod
287 1 50       3 ? $INC{'Devel/Examine/Subs/Preprocessor.pm'}
288             : 'lib/Devel/Examine/Subs/Preprocessor.pm';
289             },
290             post_proc => sub {
291 1 50   1   3 trace() if $ENV{TRACE};
292             return $in_prod
293 1 50       3 ? $INC{'Devel/Examine/Subs/Postprocessor.pm'}
294             : 'lib/Devel/Examine/Subs/Postprocessor.pm';
295             },
296             engine => sub {
297 3 50   3   9 trace() if $ENV{TRACE};
298             return $in_prod
299 3 50       9 ? $INC{'Devel/Examine/Subs/Engine.pm'}
300             : 'lib/Devel/Examine/Subs/Engine.pm';
301             },
302 5         38 );
303              
304 5         18 my $caller = (caller)[1];
305              
306 5 50       156 open my $fh, '<', $caller
307             or confess "can't open the caller file $caller: $!";
308              
309 5         8 my $code_found = 0;
310 5         6 my @code;
311              
312 5         83 while (<$fh>){
313 137         95 chomp;
314 137 100       207 if (m|^#(.*)|){
315 5         9 $code_found = 1;
316 5         25 next;
317             }
318 132 100       243 next if ! $code_found;
319 39 100       62 last if m|^#(.*)|;
320 34         64 push @code, $_;
321             }
322              
323 5         15 my $file = $dt{$to_add}->();
324 5         12 my $copy = $self->{params}{copy};
325              
326 5 50       12 if ($copy) {
327 5 50       26 copy $file, $copy or die $!;
328 5         1737 $file = $copy;
329             }
330              
331 5         6 my $sub_name;
332            
333 5 100       32 if ($code[0] =~ /sub\s+(\w+)/){
334 4         11 $sub_name = $1;
335             }
336             else {
337 1         198 confess "couldn't extract the sub name";
338             }
339              
340 4         22 my $des = Devel::Examine::Subs->new(file => $file);
341              
342 4         15 my $existing_subs = $des->all;
343              
344 4 100       5 if (grep { $sub_name eq $_ } @$existing_subs) {
  45         48  
345 1         588 confess "the sub you're trying to add already exists";
346             }
347              
348 3         23 $des = Devel::Examine::Subs->new(
349             file => $file,
350             engine => 'objects',
351             post_proc => [qw(subs end_of_last_sub)],
352             );
353 3         831 $p = {
354             engine => 'objects',
355             post_proc => [qw(subs end_of_last_sub)],
356             post_proc_return => 1,
357             };
358              
359 3         12 my $start_writing = $des->run($p);
360              
361 3         30 my $rw = File::Edit::Portable->new;
362              
363 3         27 $rw->splice(file => $file, insert => \@code, line => $start_writing);
364              
365 3         12879 my @insert = (" $sub_name => \\&$sub_name,");
366              
367 3         15 my @ret = $rw->splice(
368             file => $file,
369             find => 'my\s+\$dt\s+=\s+\{',
370             insert => \@insert,
371             );
372              
373 3         12975 return 1;
374             }
375             sub engines {
376            
377 3 100   3 1 769 trace() if $ENV{TRACE};
378              
379 2         7 my $self = shift;
380 2         6 my $module = $self->{namespace} . "::Engine";
381 2         9 my $engine = $module->new;
382            
383 2         4 my @engines;
384              
385 2         2 for (keys %{$engine->_dt}){
  2         7  
386 20 100       40 push @engines, $_ if $_ !~ /^_/;
387             }
388 2         14 return @engines;
389             }
390             sub pre_procs {
391            
392 2 50   2 1 808 trace() if $ENV{TRACE};
393              
394 1         6 my $self = shift;
395 1         2 my $module = $self->{namespace} . "::Preprocessor";
396 1         8 my $pre_proc = $module->new;
397              
398 1         1 my @pre_procs;
399              
400 1         1 for (keys %{$pre_proc->_dt}){
  1         2  
401 5 100       12 push @pre_procs, $_ if $_ !~ /^_/;
402             }
403 1         6 return @pre_procs;
404             }
405             sub post_procs {
406            
407 3 100   3 1 733 trace() if $ENV{TRACE};
408              
409 2         7 my $self = shift;
410 2         7 my $module = $self->{namespace} . "::Postprocessor";
411 2         65 my $post_proc = $module->new;
412              
413 2         4 my @post_procs;
414              
415 2         2 for (keys %{$post_proc->_dt}){
  2         5  
416 14 100       32 push @post_procs, $_ if $_ !~ /^_/;
417             }
418 2         14 return @post_procs;
419             }
420             sub run {
421              
422 191 100   191 1 14023 trace() if $ENV{TRACE};
423              
424 190         262 my $self = shift;
425 190         200 my $p = shift;
426              
427 190         462 $self->_config($p);
428              
429 186         498 $self->_run_end(0);
430              
431 186         160 my $struct;
432              
433 186 100       416 if ($self->{params}{directory}){
434 10         33 $struct = $self->_run_directory;
435             }
436             else {
437 176         431 $struct = $self->_core;
438 143 100       505 $self->_write_file if $self->{write_file_contents};
439             }
440              
441 153         482 $self->_run_end(1);
442              
443 153         716 return $struct;
444             }
445             sub valid_params {
446            
447 3 100   3 1 728 trace() if $ENV{TRACE};
448              
449 2         8 my $self = shift;
450 2         2 return %{$self->{valid_params}};
  2         20  
451             }
452              
453             #
454             # private methods
455             #
456              
457             sub _cache {
458            
459 30 100   30   803 trace() if $ENV{TRACE};
460              
461 29         39 my $self = shift;
462 29         28 my $file = shift;
463 29         25 my $struct = shift;
464              
465 29 100       64 if ($self->{params}{cache_dump}){
466              
467 1         8 print Dumper $self->{cache};
468              
469 1 50       266 if ($self->{params}{cache_dump} > 1){
470 1         6 exit;
471             }
472             }
473              
474 28 100 100     180 if (! $struct && $file){
475 25         84 return $self->{cache}{$file};
476             }
477 3 100 66     16 if ($file && $struct){
478 2         5 $self->{cache}{$file} = $struct;
479             }
480             }
481             sub _cache_enabled {
482            
483 812 100   812   2724 trace() if $ENV{TRACE};
484              
485 811         990 my $self = shift;
486 811         1924 return $self->{params}{cache};
487             }
488             sub _cache_safe {
489            
490 371 100   371   1619 trace() if $ENV{TRACE};
491              
492 370         454 my $self = shift;
493 370         410 my $value = shift;
494              
495 370 100       694 $self->{cache_safe} = $value if defined $value;
496              
497 370         647 return $self->{cache_safe};
498             }
499             sub _clean_config {
500            
501 347 100   347   1448 trace() if $ENV{TRACE};
502              
503 346         407 my $self = shift;
504 346         321 my $config_vars = shift; # href of valid params
505 346         323 my $p = shift; # href of params passed in
506              
507 346         1688 for my $var (keys %$config_vars){
508            
509 2505 100       2539 last if ! $self->_run_end;
510              
511             # skip if it's a persistent var
512              
513 2214 100       2815 next if $config_vars->{$var} == 1;
514              
515 1566         1214 delete $self->{params}{$var};
516             }
517              
518             # delete non-valid params
519              
520 346         1155 for my $param (keys %$p){
521 470 100       989 if (! exists $config_vars->{$param}){
522 5         486 print "\n\nDES::_clean_config() deleting invalid param: $param\n";
523 5         12 delete $p->{$param};
524             }
525             }
526             }
527             sub _clean_core_config {
528            
529 156 100   156   1179 trace() if $ENV{TRACE};
530              
531 155         206 my $self = shift;
532              
533             # delete params we collected after _clean_config()
534              
535 155         1571 delete $self->{params}{file_contents};
536 155         277 delete $self->{params}{order};
537              
538 155         374 my @core_phases = qw(
539             pre_proc
540             post_proc
541             engine
542             );
543              
544 155         316 for (@core_phases){
545 465         606 delete $self->{params}{$_};
546             }
547             }
548             sub _config {
549            
550 346 100   346   1923 trace() if $ENV{TRACE};
551              
552 345         400 my $self = shift;
553 345         339 my $p = shift;
554              
555 345         5762 my %valid_params = (
556              
557             # persistent
558              
559             backup => 1,
560             cache => 1,
561             copy => 1,
562             diff => 1,
563             extensions => 1,
564             file => 1,
565             maxdepth => 1,
566             no_indent => 1,
567             regex => 1,
568              
569             # persistent - core phases
570              
571             pre_proc => 1,
572             post_proc => 1,
573             engine => 1,
574              
575             # transient
576              
577             directory => 0,
578             search => 0,
579             replace => 0,
580             injects => 0,
581             code => 0,
582             include => 0,
583             exclude => 0,
584             lines => 0,
585             module => 0,
586             objects_in_hash => 0,
587             pre_proc_dump => 0,
588             post_proc_dump => 0,
589             engine_dump => 0,
590             core_dump => 0,
591             pre_proc_return => 0,
592             post_proc_return => 0,
593             engine_return => 0,
594             config_dump => 0,
595             cache_dump => 0,
596             inject_use => 0,
597             inject_after_sub_def => 0,
598             delete => 0,
599             file_contents => 0,
600             exec => 0, # replace(), search_replace()
601             limit => 0,
602             line_num => 0, # inject()
603             add_functionality => 0,
604             add_functionality_prod => 0,
605             order => 0,
606             );
607              
608 345         540 $self->{valid_params} = \%valid_params;
609              
610             # get previous run's config
611              
612 345         859 %{$self->{previous_run_config}} = %{$self->{params}};
  345         1131  
  345         822  
613              
614             # clean config
615              
616 345         935 $self->_clean_config(\%valid_params, $p);
617              
618 345         571 for my $param (keys %$p){
619              
620             # validate the file
621              
622 464 100       838 if ($param eq 'file'){
623 153         344 $self->_file($p);
624 149         207 next;
625             }
626              
627 311         525 $self->{params}{$param} = $p->{$param};
628             }
629              
630             # check if we can cache
631              
632 341 100       669 if ($self->_cache_enabled) {
633              
634             my @unsafe_cache_params
635 16         37 = qw(file extensions include exclude search);
636              
637 16         23 my $current = $self->{params};
638 16         14 my $previous = $self->{previous_run_config};
639              
640 16         28 for (@unsafe_cache_params) {
641 57   100     167 my $safe = Compare($current->{$_}, $previous->{$_}) || 0;
642              
643 57         1754 $self->_cache_safe($safe);
644              
645 57 100       55 last if !$self->_cache_safe;
646             }
647             }
648              
649 341 100       762 if ($self->{params}{config_dump}){
650 1         6 print Dumper $self->{params};
651             }
652             }
653             sub _file {
654            
655 156 100   156   1088 trace() if $ENV{TRACE};
656              
657 155         197 my $self = shift;
658 155         171 my $p = shift;
659              
660 155 100       493 $self->{params}{file} = defined $p->{file} ? $p->{file} : $self->{params}{file};
661              
662             # if a module was passed in, dig up the file
663              
664 155 100       561 if ($self->{params}{file} =~ /::/){
665              
666 3         5 my $module = $self->{params}{file};
667 3         9 (my $file = $module) =~ s|::|/|g;
668 3         4 $file .= '.pm';
669            
670 3         3 my $module_is_loaded;
671              
672 3 100       10 if (! $INC{$file}){
673            
674 2         2 eval { require $file; import $module; };
  2         920  
  1         38484  
675              
676 2 100       16 if ($@){
677 1         3 $@ = "\nDevel::Examine::Subs::_file() speaking ... " .
678             "Can't transform module to a file name\n\n"
679             . $@;
680 1         235 confess $@;
681             }
682             }
683             else {
684 1         1 $module_is_loaded = 1;
685             }
686              
687             # set the file param
688              
689 2         10 $self->{params}{file} = $INC{$file};
690              
691 2 100       11 if (! $module_is_loaded){
692 1         5 delete_package $module;
693 1         155 delete $INC{$file};
694             }
695             }
696              
697             # configure directory searching for run()
698              
699 154 100       3407 if (-d $self->{params}{file}){
700 11         22 $self->{params}{directory} = 1;
701             $self->{params}{extensions}
702 11 100       44 = defined $p->{extensions} ? $p->{extensions} : [qw(*.pm *.pl)];
703             }
704             else {
705 143 100 66     1432 if (! $self->{params}{file} || ! -f $self->{params}{file}){
706 4         73 die "Invalid file supplied: $self->{params}{file} $!";
707             }
708             }
709              
710 150         250 return $self->{params}{file};
711             }
712             sub _params {
713            
714 298 100   298   1411 trace() if $ENV{TRACE};
715              
716 297         354 my $self = shift;
717 297         740 my %params = @_;
718 297         517 return \%params;
719             }
720             sub _read_file {
721              
722             # this sub prepares a temp copy of the original file,
723             # recseps changed to local platform for PPI
724              
725 297 100   297   1831 trace() if $ENV{TRACE};
726              
727 296         397 my $self = shift;
728 296         304 my $p = shift;
729              
730 296         384 my $file = $p->{file};
731              
732 296 100       569 return if ! $file;
733              
734 263 100       608 if ($self->{params}{backup}) {
735 3         100 my $basename = basename($file);
736 3         6 my $bak = "$basename.bak";
737              
738 3 100       11 copy $file, $bak
739             or confess "DES::_read_file() can't create backup copy $bak!";
740             }
741              
742 262 100       4987 die "Can't call method \"serialize\" on an undefined file\n" if ! -f $file;
743              
744 261         2368 $self->{rw} = File::Edit::Portable->new;
745              
746 261         1492 my $ppi_doc;
747              
748 261 100       1053 if ($self->{rw}->recsep($file, 'hex') ne $self->{rw}->platform_recsep('hex')) {
749 12         10826 my $fh = $self->{rw}->read($file);
750              
751 12         305723 my $tempfile = $self->{rw}->tempfile;
752 12         3067 my $tempfile_name = $tempfile->filename;
753 12         78 my $platform_recsep = $self->{rw}->platform_recsep;
754              
755             $self->{rw}->write(
756 12         5621 copy => $tempfile_name,
757             contents => $fh,
758             recsep => $platform_recsep
759             );
760              
761 12         6521 $ppi_doc = PPI::Document->new($tempfile_name);
762              
763 12         186530 close $tempfile;
764             }
765             else {
766 249         214957 $ppi_doc = PPI::Document->new($file);
767             }
768              
769 261         7198456 @{ $p->{file_contents} } = split /\n/, $ppi_doc->serialize;
  261         721400  
770              
771              
772 261 100       1104 if (! $p->{file_contents}->[0]){
773 2         6 return 0;
774             }
775             else {
776 259         671 $self->{params}{file_contents} = $p->{file_contents};
777 259         1355 return 1;
778             }
779             }
780             sub _run_directory {
781            
782 12 100   12   777 trace() if $ENV{TRACE};
783              
784 11         20 my $self = shift;
785 11         16 my $p = shift;
786              
787 11         18 my $dir = $self->{params}{file};
788              
789 11         94 $self->{rw} = File::Edit::Portable->new;
790              
791             my @files = $self->{rw}->dir(
792             dir => $dir,
793             maxdepth => $self->{params}{maxdepth} || 0,
794             types => $self->{params}{extensions},
795 11   50     135 list => 1,
796             );
797              
798 10         14862 my %struct;
799              
800 10         23 for my $file (@files){
801            
802 112         232 $self->{params}{file} = $file;
803 112         285 my $data = $self->_core($p);
804              
805 112 100       315 $self->_write_file if $self->{write_file_contents};
806              
807 112 100 66     686 if (ref $data eq 'HASH' || ref $data eq 'ARRAY'){
808 84         241 $struct{$file} = $data;
809             }
810             }
811              
812 10         49 return \%struct;
813             }
814             sub _run_end {
815            
816 2849 100   2849   5650 trace() if $ENV{TRACE};
817              
818 2848         2384 my $self = shift;
819 2848         1885 my $value = shift;
820              
821 2848 100       3466 $self->{run_end} = $value if defined $value;
822              
823             # we clean core_config here
824              
825 2848 100       5123 $self->_clean_core_config if $value;
826              
827 2848         3943 return $self->{run_end};
828             }
829             sub _write_file {
830              
831 30 100   30   828 trace() if $ENV{TRACE};
832              
833 29         42 my $self = shift;
834              
835 29         51 my $copy = $self->{params}{copy};
836            
837 29         36 my $file = $self->{params}{file};
838 29         49 my $contents = $self->{write_file_contents};
839              
840 29 100       108 return if ! $file;
841              
842 28 100 100     344 if ($self->{params}{directory} && $copy && ! -d $copy){
      100        
843 2         36 warn "\n\nin directory mode, all files are copied to the dir named " .
844             "in the copy param, which is $copy\n\n";
845              
846 2 50       174 mkdir $copy or confess "can't create directory $copy";
847             }
848 28 100 100     684 if ($copy && -d $copy){
    100          
849 8         34 copy $file, $copy;
850 8         2565 my $filename = basename $file;
851 8         16 $file = "$copy/$filename";
852             }
853             elsif ($copy) {
854 17         24 $file = $copy;
855             }
856              
857 28         32 my $write_response;
858              
859 28         34 eval {
860 28         161 $write_response = $self->{rw}->write(file => $file, contents => $contents);
861             };
862              
863 28 100 66     23956 if ($@ || ! $write_response){
864 1         2 $@ = "\nDevel::Examine::Subs::_write_file() speaking...\n\n" .
865             "File::Edit::Portable::write() returned a failure status.\n\n" .
866             $@;
867 1         198 confess $@;
868             }
869             }
870              
871             #
872             # private methods for core phases
873             #
874              
875             sub _core {
876              
877 290 100   290   1551 trace() if $ENV{TRACE};
878            
879 289         417 my $self = shift;
880              
881 289         376 my $p = $self->{params};
882              
883 289         366 my $search = $self->{params}{search};
884 289         374 my $file = $self->{params}{file};
885              
886 289         712 $self->_read_file($p);
887              
888             # pre processor
889              
890 289         415003 my $data;
891              
892 289 100       1117 if ($self->{params}{pre_proc}){
893 46         111 my $pre_proc = $self->_pre_proc;
894              
895 41         85 $data = $pre_proc->($p, $data);
896              
897 29 100       2779 if ($self->{params}{pre_proc_dump}){
898 1         5 print Dumper $data;
899 1         235 exit;
900             }
901              
902 28 100       61 if ($p->{write_file_contents}){
903 16         28 $self->{write_file_contents} = $p->{write_file_contents};
904             }
905              
906             # for things that don't need to process files
907             # (such as 'module'), return early
908              
909 28 100       137 if ($self->{params}{pre_proc_return}){
910 26         220 return $data;
911             }
912             }
913              
914             # processor
915              
916 245         344 my $subs = $data;
917              
918             # bypass the proc if cache
919              
920 245         787 my $cache_enabled = $self->_cache_enabled;
921 245         663 my $cache_safe = $self->_cache_safe;
922              
923 245 100 100     965 if ($cache_enabled && $cache_safe && $self->_cache($p->{file})){
      66        
924 6         15 $subs = $self->_cache($p->{file});
925             }
926             else {
927 239         777 $subs = $self->_proc($p);
928             }
929              
930 245 100       420190 return if ! $subs;
931              
932             # write to cache
933              
934 224 100 100     784 if ($self->_cache_enabled && ! $self->_cache($p->{file})){
935 2         7 $self->_cache($p->{file}, $subs);
936             }
937              
938             # post processor
939              
940 223 100       704 if ($self->{params}{post_proc}){
941 105         374 for my $post_proc ($self->_post_proc($p, $subs)){
942 137         331 $subs = $post_proc->($p, $subs);
943 137         480 $self->{write_file_contents} = $p->{write_file_contents};
944             }
945             }
946              
947 217 100       1245 if ($self->{params}{post_proc_return}){
948 5         15 return $subs;
949             }
950              
951             # engine
952              
953 212         694 my $engine = $self->_engine($p, $subs);
954              
955 209 100       494 if ($self->{params}{engine}){
956 197         461 $subs = $engine->($p, $subs);
957 192         1358 $self->{write_file_contents} = $p->{write_file_contents};
958             }
959              
960             # core dump
961              
962 204 100       462 if ($self->{params}{core_dump}){
963            
964 1         62 print "\n\t Core Dump called...\n\n";
965 1         13 print "\n\n\t Dumping data... \n\n";
966 1         7 print Dumper $subs;
967              
968 1         116 print "\n\n\t Dumping instance...\n\n";
969 1         3 print Dumper $self;
970              
971 1         404 exit;
972             }
973              
974 203         1266 return $subs;
975             }
976             sub _pre_proc {
977            
978 50 100   50   1971 trace() if $ENV{TRACE};
979              
980 49         91 my $self = shift;
981 49         52 my $p = shift;
982 49         44 my $subs = shift;
983              
984 49         68 my $pre_proc = $self->{params}{pre_proc};
985              
986 49 100 66     238 if (not $pre_proc or $pre_proc eq ''){
987 1         2 return $subs;
988             }
989            
990             # tell _core() to return directly from the pre_processor
991             # if necessary, and bypass post_proc and engine
992              
993 48 100       92 if ($pre_proc eq 'module'){
994 11         15 $self->{params}{pre_proc_return} = 1;
995             }
996              
997 48         46 my $cref;
998            
999 48 100       101 if (not ref($pre_proc) eq 'CODE'){
1000 47         91 my $pre_proc_module = $self->{namespace} . "::Preprocessor";
1001 47         282 my $compiler = $pre_proc_module->new;
1002              
1003 47 100       120 if (! $compiler->exists($pre_proc)){
1004 1         97 confess "Devel::Examine::Subs::_pre_proc() speaking...\n\n" .
1005             "pre_processor '$pre_proc' is not implemented.\n";
1006             }
1007              
1008 46         56 eval {
1009 46         135 $cref = $compiler->{pre_procs}{$pre_proc}->();
1010             };
1011            
1012 46 100       161 if ($@){
1013 4         9 $@ = "\n[Devel::Examine::Subs speaking] " .
1014             "dispatch table in Devel::Examine::Subs::Preprocessor " .
1015             "has a mistyped function as a value, but the key is ok\n\n"
1016             . $@;
1017 4         591 confess $@;
1018             }
1019              
1020             }
1021              
1022 43 100       89 if (ref($pre_proc) eq 'CODE'){
1023 1         2 $cref = $pre_proc;
1024             }
1025            
1026 43         59 return $cref;
1027             }
1028             sub _proc {
1029            
1030             # this method is the core data collection/manipulation
1031             # routine (aka the 'Processor phase') for all of DES
1032              
1033             # make sure all unit tests are successful after any change
1034             # to this subroutine!
1035              
1036             # if you want the data structure to look differently before
1037             # reaching here, use a pre_proc. If you want it different
1038             # afterwards, use a post_proc or an engine
1039              
1040 241 100   241   1391 trace() if $ENV{TRACE};
1041            
1042 240         293 my $self = shift;
1043 240         292 my $p = shift;
1044            
1045 240         439 my $file = $self->{params}{file};
1046              
1047 240 100       525 return {} if ! $file;
1048              
1049 229         789 my $PPI_doc = PPI::Document->new($file);
1050 229         6830299 my $PPI_subs = $PPI_doc->find('PPI::Statement::Sub');
1051              
1052 229 100       2121859 return if ! $PPI_subs;
1053              
1054 208         359 my %subs;
1055 208         641 $subs{$file} = {};
1056 208         306 my @sub_order;
1057              
1058 208         300 for my $PPI_sub (@{$PPI_subs}){
  208         560  
1059            
1060             my $include
1061 1668 100       3630 = defined $self->{params}{include} ? $self->{params}{include} : [];
1062             my $exclude
1063 1668 100       2490 = defined $self->{params}{exclude} ? $self->{params}{exclude} : [];
1064              
1065 1668 100       2701 delete $self->{params}{include} if $exclude->[0];
1066              
1067 1668         3633 my $name = $PPI_sub->name;
1068            
1069 1668         25373 push @sub_order, $name;
1070              
1071             # skip over excluded (or not included) subs
1072              
1073 1668 100       3026 next if grep {$name eq $_ } @$exclude;
  88         152  
1074              
1075 1660 100       2413 if ($include->[0]){
1076 127 100       115 next if (! grep {$name eq $_ && $_} @$include);
  303 100       705  
1077             }
1078              
1079 1565         3296 $subs{$file}{subs}{$name}{start} = $PPI_sub->line_number;
1080 1565         1395944 $subs{$file}{subs}{$name}{start}--;
1081              
1082 1565         2713 my $lines = $PPI_sub =~ y/\n//;
1083              
1084             $subs{$file}{subs}{$name}{end}
1085 1565         350862 = $subs{$file}{subs}{$name}{start} + $lines;
1086              
1087 1565         1759 my $count_start = $subs{$file}{subs}{$name}{start};
1088 1565         1179 $count_start--;
1089              
1090             my $sub_line_count
1091 1565         1880 = $subs{$file}{subs}{$name}{end} - $count_start;
1092              
1093 1565         1799 $subs{$file}{subs}{$name}{num_lines} = $sub_line_count;
1094              
1095 1565         2537 @{ $subs{$file}{subs}{$name}{code} } = split /\n/, $PPI_sub->content;
  1565         170479  
1096             }
1097            
1098 208         405 @{ $p->{order} } = @sub_order;
  208         901  
1099 208         289 @{ $self->{order} } = @sub_order;
  208         698  
1100              
1101 208         1061 return \%subs;
1102             }
1103             sub _post_proc {
1104            
1105 107 100   107   1036 trace() if $ENV{TRACE};
1106              
1107 106         164 my $self = shift;
1108 106         127 my $p = shift;
1109 106         145 my $struct = shift;
1110              
1111 106         186 my $post_proc = $self->{params}{post_proc};
1112              
1113 106         174 my $post_proc_dump = $self->{params}{post_proc_dump};
1114              
1115 106         116 my @post_procs;
1116              
1117 106 100       218 if ($post_proc){
1118              
1119 105         120 my @post_proc_list;
1120              
1121 105 100       298 if (ref $post_proc ne 'ARRAY'){
1122 80         145 push @post_proc_list, $post_proc;
1123             }
1124             else {
1125 25         36 @post_proc_list = @{$post_proc};
  25         66  
1126             }
1127              
1128 105         209 for my $pf (@post_proc_list){
1129              
1130 145         167 my $cref;
1131              
1132 145 100       338 if (ref $pf ne 'CODE'){
1133              
1134 140         340 my $post_proc_module = $self->{namespace} . "::Postprocessor";
1135 140         1081 my $compiler = $post_proc_module->new;
1136              
1137             # post_proc isn't in the dispatch table
1138              
1139 140 100       385 if (! $compiler->exists($pf)){
1140 3         404 confess "\nDevel::Examine::Subs::_post_proc() " .
1141             "speaking...\n\npost_proc '$pf' is not " .
1142             "implemented. '$post_proc' was sent in.\n";
1143             }
1144            
1145 137         227 eval {
1146 137         445 $cref = $compiler->{post_procs}{$pf}->();
1147             };
1148            
1149 137 100       562 if ($@){
1150 1         3 $@ = "\n[Devel::Examine::Subs speaking] " .
1151             "dispatch table in " .
1152             "Devel::Examine::Subs::Postprocessor has a mistyped " .
1153             "function as a value, but the key is ok\n\n"
1154             . $@;
1155 1         99 confess $@;
1156             }
1157             }
1158 141 100       358 if (ref($pf) eq 'CODE'){
1159 5         4 $cref = $pf;
1160             }
1161              
1162 141 100 100     333 if ($post_proc_dump && $post_proc_dump > 1){
1163 1         2 $self->{params}{post_proc_dump}--;
1164 1         1 $post_proc_dump = $self->{params}{post_proc_dump};
1165             }
1166              
1167 141 100 66     360 if ($post_proc_dump && $post_proc_dump == 1){
1168 2         6 my $subs = $cref->($p, $struct);
1169 2         14 print Dumper $subs;
1170 2         806 exit;
1171             }
1172 139         287 push @post_procs, $cref;
1173             }
1174             }
1175             else {
1176 1         2 return;
1177             }
1178 99         263 return @post_procs;
1179             }
1180             sub _engine {
1181            
1182 217 100   217   1453 trace() if $ENV{TRACE};
1183              
1184 216         286 my $self = shift;
1185 216         264 my $p = shift;
1186 216         248 my $struct = shift;
1187              
1188             my $engine
1189 216 100       724 = defined $p->{engine} ? $p->{engine} : $self->{params}{engine};
1190              
1191 216 100 66     1343 if (not $engine or $engine eq ''){
1192 13         25 return $struct;
1193             }
1194              
1195 203         225 my $cref;
1196              
1197 203 100       568 if (not ref($engine) eq 'CODE'){
1198              
1199             # engine is a name
1200              
1201 202         503 my $engine_module = $self->{namespace} . "::Engine";
1202 202         1867 my $compiler = $engine_module->new;
1203              
1204             # engine isn't in the dispatch table
1205              
1206 202 100       659 if (! $compiler->exists($engine)){
1207 1         141 confess "engine '$engine' is not implemented.\n";
1208             }
1209              
1210 201         292 eval {
1211 201         597 $cref = $compiler->{engines}{$engine}->();
1212             };
1213              
1214             # engine has bad func val in dispatch table, but key is ok
1215              
1216 201 100       890 if ($@){
1217 1         2 $@ = "\n[Devel::Examine::Subs speaking] " .
1218             "dispatch table in Devel::Examine::Subs::Engine " .
1219             "has a mistyped function as a value, but the key is ok\n\n"
1220             . $@;
1221 1         185 confess $@;
1222             }
1223             }
1224              
1225 201 100       467 if (ref($engine) eq 'CODE'){
1226 1         1 $cref = $engine;
1227             }
1228              
1229 201 100       498 if ($self->{params}{engine_dump}){
1230 1         3 my $subs = $cref->($p, $struct);
1231 1         5 print Dumper $subs;
1232 1         158 exit;
1233             }
1234              
1235 200         333 return $cref;
1236             }
1237              
1238             #
1239             # pod
1240             #
1241              
1242 1     1   2885 sub _pod{1;} #vim placeholder
1243             1;
1244             __END__