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 57     57   1396256 use 5.008;
  57         162  
3 57     57   221 use warnings;
  57         70  
  57         1427  
4 57     57   202 use strict;
  57         80  
  57         2670  
5              
6             our $VERSION = '1.68';
7              
8 57     57   201 use Carp;
  57         108  
  57         3170  
9 57     57   25990 use Data::Compare;
  57         482327  
  57         307  
10 57     57   168189 use Data::Dumper;
  57         80758  
  57         2564  
11 57     57   22950 use Devel::Examine::Subs::Engine;
  57         101  
  57         1583  
12 57     57   23015 use Devel::Examine::Subs::Preprocessor;
  57         96  
  57         1679  
13 57     57   21722 use Devel::Examine::Subs::Postprocessor;
  57         96  
  57         1441  
14 57     57   257 use File::Basename;
  57         56  
  57         3649  
15 57     57   633 use File::Copy;
  57         1695  
  57         2110  
16 57     57   26938 use File::Edit::Portable;
  57         1095464  
  57         2430  
17 57     57   27600 use PPI;
  57         5243155  
  57         2162  
18 57     57   435 use Symbol qw(delete_package);
  57         88  
  57         5307  
19              
20             BEGIN {
21              
22             # we need to do some trickery for DTS due to circular referencing,
23             # which broke CPAN installs.
24              
25 57     57   114 eval {
26 57         8880 require Devel::Trace::Subs;
27             };
28              
29 57         170 eval {
30 57         233 import Devel::Trace::Subs qw(trace);
31             };
32              
33 57 50       605 if (! defined &trace){
34 57     0   231732 *trace = sub {};
35             }
36             };
37              
38             #
39             # public methods
40             #
41              
42             sub new {
43            
44             # set up for tracing
45              
46 146 100   146 1 127594 if ($ENV{DES_TRACE}){
47 3         11 $ENV{DTS_ENABLE} = 1;
48 3         9 $ENV{TRACE} = 1;
49             }
50              
51 146 100       427 trace() if $ENV{TRACE};
52              
53 145         266 my $self = {};
54 145         278 bless $self, shift;
55 145         516 my $p = $self->_params(@_);
56              
57             # default configs
58              
59 145         492 $self->{namespace} = __PACKAGE__;
60 145         379 $self->{params}{regex} = 1;
61 145         273 $self->{params}{backup} = 0;
62              
63 145         433 $self->_config($p);
64              
65 145         384 return $self;
66             }
67             sub all {
68            
69 27 100   27 1 8136 trace() if $ENV{TRACE};
70              
71 26         45 my $self = shift;
72 26         105 my $p = $self->_params(@_);
73              
74 26         73 $self->{params}{engine} = 'all';
75            
76 26         95 $self->run($p);
77             }
78             sub has {
79              
80 49 100   49 1 11243 trace() if $ENV{TRACE};
81              
82 48         76 my $self = shift;
83 48         154 my $p = $self->_params(@_);
84              
85 48         116 $self->{params}{post_proc} = 'file_lines_contain';
86 48         93 $self->{params}{engine} = 'has';
87            
88 48         138 $self->run($p);
89             }
90             sub missing {
91            
92 8 100   8 1 4607 trace() if $ENV{TRACE};
93              
94 7         14 my $self = shift;
95 7         21 my $p = $self->_params(@_);
96              
97 7         17 $self->{params}{engine} = 'missing';
98            
99 7         20 $self->run($p);
100             }
101             sub lines {
102            
103 8 100   8 1 3769 trace() if $ENV{TRACE};
104              
105 7         15 my $self = shift;
106 7         24 my $p = $self->_params(@_);
107              
108 7         17 $self->{params}{engine} = 'lines';
109            
110 7 100 66     30 if ($self->{params}{search} || $p->{search}){
111 5         9 $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 2741 trace() if $ENV{TRACE};
119              
120 11         14 my $self = shift;
121              
122 11         12 my $p;
123              
124             # allow for single string value
125              
126 11 100       22 if (@_ == 1){
127 4         4 my %p;
128 4         6 $p{module} = shift;
129 4         6 $p = $self->_params(%p);
130             }
131             else {
132 7         14 $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         15 $self->{params}{pre_proc} = 'module';
139 11         13 $self->{params}{pre_proc_return} = 1;
140              
141 11         12 $self->{params}{engine} = 'module';
142              
143 11         18 $self->run($p);
144             }
145             sub objects {
146            
147 15 100   15 1 77349 trace() if $ENV{TRACE};
148              
149 14         22 my $self = shift;
150 14         33 my $p = $self->_params(@_);
151              
152 14         32 $self->{params}{post_proc} = 'subs';
153 14         22 $self->{params}{engine} = 'objects';
154              
155 14         100 $self->run($p);
156             }
157             sub search_replace {
158            
159 8 100   8 1 7262 trace() if $ENV{TRACE};
160              
161 7         16 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         12 $self->{params}{engine} = 'search_replace';
168              
169 7         35 $self->run($p);
170             }
171             sub replace {
172              
173 9 100   9 1 856 trace() if $ENV{TRACE};
174              
175 8         14 my $self = shift;
176 8         17 my $p = $self->_params(@_);
177              
178 8         14 $self->{params}{pre_proc} = 'replace';
179 8         13 $self->{params}{pre_proc_return} = 1;
180              
181 8         20 $self->run($p);
182             }
183             sub inject_after {
184            
185 8 100   8 1 782 trace() if $ENV{TRACE};
186              
187 7         16 my $self = shift;
188 7         23 my $p = $self->_params(@_);
189              
190 7 100 66     37 if (! $p->{injects} && ! $self->{params}{injects}){
191 3         6 $p->{injects} = 1;
192             }
193              
194             $self->{params}{post_proc}
195 7         22 = ['file_lines_contain', 'subs', 'objects'];
196              
197 7         11 $self->{params}{engine} = 'inject_after';
198              
199 7         19 $self->run($p);
200             }
201             sub inject {
202 7 100   7 1 4370 trace() if $ENV{TRACE};
203 6         12 my $self = shift;
204 6         18 my $p = $self->_params(@_);
205              
206             # inject_use/inject_after_sub_def are preprocs
207              
208 6 100 66     42 if ($p->{inject_use} || $p->{inject_after_sub_def} || defined $p->{line_num}){
      100        
209 5         12 $self->{params}{pre_proc} = 'inject';
210 5         8 $self->{params}{pre_proc_return} = 1;
211             }
212              
213 6         17 $self->run($p);
214             }
215             sub remove {
216 3 100   3 1 1415 trace() if $ENV{TRACE};
217              
218 2         7 my $self = shift;
219 2         5 my $p = $self->_params(@_);
220              
221 2         4 $self->{params}{pre_proc} = 'remove';
222 2         4 $self->{params}{pre_proc_return} = 1;
223              
224 2         5 $self->run($p);
225             }
226             sub order {
227 4 100   4 1 797 trace() if $ENV{TRACE};
228              
229 3         12 my $self = shift;
230              
231 3 100       11 if ($self->{params}{directory}){
232 1         235 confess "\norder() can only be called on an individual file, not " .
233             "a directory at this time\n\n";
234             }
235              
236 2         4 return @{ $self->{order} };
  2         12  
237             }
238             sub backup {
239 2 50   2 1 3598 trace() if $ENV{TRACE};
240              
241 2         3 my $self = shift;
242 2   100     10 my $state = shift || 0;
243              
244 2 50       7 $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 779 trace() if $ENV{TRACE};
255            
256 6         12 my $self = shift;
257 6         16 my $p = $self->_params(@_);
258              
259 6         12 $self->_config($p);
260            
261 6         10 my $to_add = $self->{params}{add_functionality};
262 6         10 my $in_prod = $self->{params}{add_functionality_prod};
263              
264 6         16 my @allowed = qw(
265             pre_proc
266             post_proc
267             engine
268             );
269              
270 6         7 my $is_allowed = 0;
271              
272 6         13 for (@allowed){
273 15 100       66 if ($_ eq $to_add){
274 5         6 $is_allowed = 1;
275 5         7 last;
276             }
277             }
278            
279 6 100       19 if (! $is_allowed){
280 1         171 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       10 ? $INC{'Devel/Examine/Subs/Engine.pm'}
300             : 'lib/Devel/Examine/Subs/Engine.pm';
301             },
302 5         37 );
303              
304 5         15 my $caller = (caller)[1];
305              
306 5 50       190 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         92 while (<$fh>){
313 137         86 chomp;
314 137 100       202 if (m|^#(.*)|){
315 5         10 $code_found = 1;
316 5         10 next;
317             }
318 132 100       207 next if ! $code_found;
319 39 100       64 last if m|^#(.*)|;
320 34         61 push @code, $_;
321             }
322              
323 5         16 my $file = $dt{$to_add}->();
324 5         8 my $copy = $self->{params}{copy};
325              
326 5 50       15 if ($copy) {
327 5 50       29 copy $file, $copy or die $!;
328 5         1796 $file = $copy;
329             }
330              
331 5         7 my $sub_name;
332            
333 5 100       30 if ($code[0] =~ /sub\s+(\w+)/){
334 4         12 $sub_name = $1;
335             }
336             else {
337 1         206 confess "couldn't extract the sub name";
338             }
339              
340 4         23 my $des = Devel::Examine::Subs->new(file => $file);
341              
342 4         15 my $existing_subs = $des->all;
343              
344 4 100       9 if (grep { $sub_name eq $_ } @$existing_subs) {
  45         45  
345 1         1077 confess "the sub you're trying to add already exists";
346             }
347              
348 3         28 $des = Devel::Examine::Subs->new(
349             file => $file,
350             engine => 'objects',
351             post_proc => [qw(subs end_of_last_sub)],
352             );
353 3         1093 $p = {
354             engine => 'objects',
355             post_proc => [qw(subs end_of_last_sub)],
356             post_proc_return => 1,
357             };
358              
359 3         16 my $start_writing = $des->run($p);
360              
361 3         37 my $rw = File::Edit::Portable->new;
362              
363 3         27 $rw->splice(file => $file, insert => \@code, line => $start_writing);
364              
365 3         91063 my @insert = (" $sub_name => \\&$sub_name,");
366              
367 3         23 my @ret = $rw->splice(
368             file => $file,
369             find => 'my\s+\$dt\s+=\s+\{',
370             insert => \@insert,
371             );
372              
373 3         14252 return 1;
374             }
375             sub engines {
376            
377 3 100   3 1 746 trace() if $ENV{TRACE};
378              
379 2         11 my $self = shift;
380 2         7 my $module = $self->{namespace} . "::Engine";
381 2         10 my $engine = $module->new;
382            
383 2         3 my @engines;
384              
385 2         4 for (keys %{$engine->_dt}){
  2         4  
386 20 100       43 push @engines, $_ if $_ !~ /^_/;
387             }
388 2         15 return @engines;
389             }
390             sub pre_procs {
391            
392 2 50   2 1 804 trace() if $ENV{TRACE};
393              
394 1         5 my $self = shift;
395 1         4 my $module = $self->{namespace} . "::Preprocessor";
396 1         7 my $pre_proc = $module->new;
397              
398 1         1 my @pre_procs;
399              
400 1         2 for (keys %{$pre_proc->_dt}){
  1         3  
401 5 100       12 push @pre_procs, $_ if $_ !~ /^_/;
402             }
403 1         7 return @pre_procs;
404             }
405             sub post_procs {
406            
407 3 100   3 1 794 trace() if $ENV{TRACE};
408              
409 2         7 my $self = shift;
410 2         4 my $module = $self->{namespace} . "::Postprocessor";
411 2         67 my $post_proc = $module->new;
412              
413 2         9 my @post_procs;
414              
415 2         3 for (keys %{$post_proc->_dt}){
  2         9  
416 14 100       33 push @post_procs, $_ if $_ !~ /^_/;
417             }
418 2         19 return @post_procs;
419             }
420             sub run {
421              
422 190 100   190 1 717671 trace() if $ENV{TRACE};
423              
424 189         280 my $self = shift;
425 189         209 my $p = shift;
426              
427 189         424 $self->_config($p);
428              
429 185         656 $self->_run_end(0);
430              
431 185         223 my $struct;
432              
433 185 100       422 if ($self->{params}{directory}){
434 10         32 $struct = $self->_run_directory;
435             }
436             else {
437 175         450 $struct = $self->_core;
438 142 100       508 $self->_write_file if $self->{write_file_contents};
439             }
440              
441 152         525 $self->_run_end(1);
442              
443 152         734 return $struct;
444             }
445             sub valid_params {
446            
447 3 100   3 1 727 trace() if $ENV{TRACE};
448              
449 2         7 my $self = shift;
450 2         3 return %{$self->{valid_params}};
  2         18  
451             }
452              
453             #
454             # private methods
455             #
456              
457             sub _cache {
458            
459 30 100   30   862 trace() if $ENV{TRACE};
460              
461 29         42 my $self = shift;
462 29         33 my $file = shift;
463 29         52 my $struct = shift;
464              
465 29 100       69 if ($self->{params}{cache_dump}){
466              
467 1         6 print Dumper $self->{cache};
468              
469 1 50       246 if ($self->{params}{cache_dump} > 1){
470 1         4 exit;
471             }
472             }
473              
474 28 100 100     216 if (! $struct && $file){
475 25         104 return $self->{cache}{$file};
476             }
477 3 100 66     18 if ($file && $struct){
478 2         6 $self->{cache}{$file} = $struct;
479             }
480             }
481             sub _cache_enabled {
482            
483 804 100   804   2881 trace() if $ENV{TRACE};
484              
485 803         1108 my $self = shift;
486 803         2193 return $self->{params}{cache};
487             }
488             sub _cache_safe {
489            
490 368 100   368   1606 trace() if $ENV{TRACE};
491              
492 367         453 my $self = shift;
493 367         508 my $value = shift;
494              
495 367 100       777 $self->{cache_safe} = $value if defined $value;
496              
497 367         713 return $self->{cache_safe};
498             }
499             sub _clean_config {
500            
501 345 100   345   1446 trace() if $ENV{TRACE};
502              
503 344         427 my $self = shift;
504 344         332 my $config_vars = shift; # href of valid params
505 344         320 my $p = shift; # href of params passed in
506              
507 344         1721 for my $var (keys %$config_vars){
508            
509 2503 100       2569 last if ! $self->_run_end;
510              
511             # skip if it's a persistent var
512              
513 2214 100       2855 next if $config_vars->{$var} == 1;
514              
515 1566         1275 delete $self->{params}{$var};
516             }
517              
518             # delete non-valid params
519              
520 344         1126 for my $param (keys %$p){
521 467 100       974 if (! exists $config_vars->{$param}){
522 5         538 print "\n\nDES::_clean_config() deleting invalid param: $param\n";
523 5         15 delete $p->{$param};
524             }
525             }
526             }
527             sub _clean_core_config {
528            
529 155 100   155   1202 trace() if $ENV{TRACE};
530              
531 154         207 my $self = shift;
532              
533             # delete params we collected after _clean_config()
534              
535 154         1658 delete $self->{params}{file_contents};
536 154         281 delete $self->{params}{order};
537              
538 154         399 my @core_phases = qw(
539             pre_proc
540             post_proc
541             engine
542             );
543              
544 154         309 for (@core_phases){
545 462         618 delete $self->{params}{$_};
546             }
547             }
548             sub _config {
549            
550 344 100   344   2092 trace() if $ENV{TRACE};
551              
552 343         417 my $self = shift;
553 343         338 my $p = shift;
554              
555 343         5873 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 343         542 $self->{valid_params} = \%valid_params;
609              
610             # get previous run's config
611              
612 343         866 %{$self->{previous_run_config}} = %{$self->{params}};
  343         1118  
  343         890  
613              
614             # clean config
615              
616 343         975 $self->_clean_config(\%valid_params, $p);
617              
618 343         599 for my $param (keys %$p){
619              
620             # validate the file
621              
622 461 100       887 if ($param eq 'file'){
623 152         390 $self->_file($p);
624 148         217 next;
625             }
626              
627 309         554 $self->{params}{$param} = $p->{$param};
628             }
629              
630             # check if we can cache
631              
632 339 100       719 if ($self->_cache_enabled) {
633              
634             my @unsafe_cache_params
635 16         47 = qw(file extensions include exclude search);
636              
637 16         24 my $current = $self->{params};
638 16         12 my $previous = $self->{previous_run_config};
639              
640 16         28 for (@unsafe_cache_params) {
641 57   100     202 my $safe = Compare($current->{$_}, $previous->{$_}) || 0;
642              
643 57         2012 $self->_cache_safe($safe);
644              
645 57 100       66 last if !$self->_cache_safe;
646             }
647             }
648              
649 339 100       756 if ($self->{params}{config_dump}){
650 1         7 print Dumper $self->{params};
651             }
652             }
653             sub _file {
654            
655 155 100   155   1090 trace() if $ENV{TRACE};
656              
657 154         191 my $self = shift;
658 154         176 my $p = shift;
659              
660 154 100       511 $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 154 100       613 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         4 my $module_is_loaded;
671              
672 3 100       9 if (! $INC{$file}){
673            
674 2         4 eval { require $file; import $module; };
  2         890  
  1         34661  
675              
676 2 100       10 if ($@){
677 1         2 $@ = "\nDevel::Examine::Subs::_file() speaking ... " .
678             "Can't transform module to a file name\n\n"
679             . $@;
680 1         231 confess $@;
681             }
682             }
683             else {
684 1         2 $module_is_loaded = 1;
685             }
686              
687             # set the file param
688              
689 2         11 $self->{params}{file} = $INC{$file};
690              
691 2 100       12 if (! $module_is_loaded){
692 1         5 delete_package $module;
693 1         162 delete $INC{$file};
694             }
695             }
696              
697             # configure directory searching for run()
698              
699 153 100       3692 if (-d $self->{params}{file}){
700 11         25 $self->{params}{directory} = 1;
701             $self->{params}{extensions}
702 11 100       48 = defined $p->{extensions} ? $p->{extensions} : [qw(*.pm *.pl)];
703             }
704             else {
705 142 100 66     1414 if (! $self->{params}{file} || ! -f $self->{params}{file}){
706 4         66 die "Invalid file supplied: $self->{params}{file} $!";
707             }
708             }
709              
710 149         258 return $self->{params}{file};
711             }
712             sub _params {
713            
714 296 100   296   1440 trace() if $ENV{TRACE};
715              
716 295         386 my $self = shift;
717 295         754 my %params = @_;
718 295         505 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 293 100   293   1785 trace() if $ENV{TRACE};
726              
727 292         440 my $self = shift;
728 292         317 my $p = shift;
729              
730 292         401 my $file = $p->{file};
731              
732 292 100       577 return if ! $file;
733              
734 259 100       581 if ($self->{params}{backup}) {
735 3         147 my $basename = basename($file);
736 3         6 my $bak = "$basename.bak";
737              
738 3 100       13 copy $file, $bak
739             or confess "DES::_read_file() can't create backup copy $bak!";
740             }
741              
742 258 100       5323 die "Can't call method \"serialize\" on an undefined file\n" if ! -f $file;
743              
744 257         2362 $self->{rw} = File::Edit::Portable->new;
745              
746 257         1558 my $ppi_doc;
747              
748 257 100       1119 if ($self->{rw}->recsep($file, 'hex') ne $self->{rw}->platform_recsep('hex')) {
749 12         11014 my $fh = $self->{rw}->read($file);
750              
751 12         283966 my $tempfile = $self->{rw}->tempfile;
752 12         2817 my $tempfile_name = $tempfile->filename;
753 12         67 my $platform_recsep = $self->{rw}->platform_recsep;
754              
755             $self->{rw}->write(
756 12         5383 copy => $tempfile_name,
757             contents => $fh,
758             recsep => $platform_recsep
759             );
760              
761 12         6074 $ppi_doc = PPI::Document->new($tempfile_name);
762              
763 12         178443 close $tempfile;
764             }
765             else {
766 245         934840 $ppi_doc = PPI::Document->new($file);
767             }
768              
769 257         7204396 @{ $p->{file_contents} } = split /\n/, $ppi_doc->serialize;
  257         731578  
770              
771              
772 257 100       1070 if (! $p->{file_contents}->[0]){
773 2         8 return 0;
774             }
775             else {
776 255         633 $self->{params}{file_contents} = $p->{file_contents};
777 255         1502 return 1;
778             }
779             }
780             sub _run_directory {
781            
782 12 100   12   807 trace() if $ENV{TRACE};
783              
784 11         21 my $self = shift;
785 11         17 my $p = shift;
786              
787 11         19 my $dir = $self->{params}{file};
788              
789 11         103 $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     147 list => 1,
796             );
797              
798 10         16975 my %struct;
799              
800 10         28 for my $file (@files){
801            
802 109         245 $self->{params}{file} = $file;
803 109         320 my $data = $self->_core($p);
804              
805 109 100       346 $self->_write_file if $self->{write_file_contents};
806              
807 109 100 66     688 if (ref $data eq 'HASH' || ref $data eq 'ARRAY'){
808 81         258 $struct{$file} = $data;
809             }
810             }
811              
812 10         66 return \%struct;
813             }
814             sub _run_end {
815            
816 2845 100   2845   5423 trace() if $ENV{TRACE};
817              
818 2844         2396 my $self = shift;
819 2844         1869 my $value = shift;
820              
821 2844 100       3719 $self->{run_end} = $value if defined $value;
822              
823             # we clean core_config here
824              
825 2844 100       5047 $self->_clean_core_config if $value;
826              
827 2844         3832 return $self->{run_end};
828             }
829             sub _write_file {
830              
831 29 100   29   839 trace() if $ENV{TRACE};
832              
833 28         56 my $self = shift;
834              
835 28         51 my $copy = $self->{params}{copy};
836            
837 28         38 my $file = $self->{params}{file};
838 28         39 my $contents = $self->{write_file_contents};
839              
840 28 100       131 return if ! $file;
841              
842 27 100 100     538 if ($self->{params}{directory} && $copy && ! -d $copy){
      100        
843 2         33 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       220 mkdir $copy or confess "can't create directory $copy";
847             }
848 27 100 100     752 if ($copy && -d $copy){
    100          
849 8         33 copy $file, $copy;
850 8         3119 my $filename = basename $file;
851 8         19 $file = "$copy/$filename";
852             }
853             elsif ($copy) {
854 16         31 $file = $copy;
855             }
856              
857 27         27 my $write_response;
858              
859 27         35 eval {
860 27         183 $write_response = $self->{rw}->write(file => $file, contents => $contents);
861             };
862              
863 27 100 66     23958 if ($@ || ! $write_response){
864 1         3 $@ = "\nDevel::Examine::Subs::_write_file() speaking...\n\n" .
865             "File::Edit::Portable::write() returned a failure status.\n\n" .
866             $@;
867 1         194 confess $@;
868             }
869             }
870              
871             #
872             # private methods for core phases
873             #
874              
875             sub _core {
876              
877 286 100   286   1485 trace() if $ENV{TRACE};
878            
879 285         429 my $self = shift;
880              
881 285         385 my $p = $self->{params};
882              
883 285         402 my $search = $self->{params}{search};
884 285         379 my $file = $self->{params}{file};
885              
886 285         755 $self->_read_file($p);
887              
888             # pre processor
889              
890 285         431578 my $data;
891              
892 285 100       1184 if ($self->{params}{pre_proc}){
893 45         118 my $pre_proc = $self->_pre_proc;
894              
895 40         94 $data = $pre_proc->($p, $data);
896              
897 28 100       2984 if ($self->{params}{pre_proc_dump}){
898 1         5 print Dumper $data;
899 1         236 exit;
900             }
901              
902 27 100       51 if ($p->{write_file_contents}){
903 15         30 $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 27 100       175 if ($self->{params}{pre_proc_return}){
910 25         215 return $data;
911             }
912             }
913              
914             # processor
915              
916 242         399 my $subs = $data;
917              
918             # bypass the proc if cache
919              
920 242         886 my $cache_enabled = $self->_cache_enabled;
921 242         688 my $cache_safe = $self->_cache_safe;
922              
923 242 100 100     1042 if ($cache_enabled && $cache_safe && $self->_cache($p->{file})){
      66        
924 6         22 $subs = $self->_cache($p->{file});
925             }
926             else {
927 236         901 $subs = $self->_proc($p);
928             }
929              
930 242 100       447071 return if ! $subs;
931              
932             # write to cache
933              
934 221 100 100     890 if ($self->_cache_enabled && ! $self->_cache($p->{file})){
935 2         8 $self->_cache($p->{file}, $subs);
936             }
937              
938             # post processor
939              
940 220 100       785 if ($self->{params}{post_proc}){
941 104         442 for my $post_proc ($self->_post_proc($p, $subs)){
942 136         375 $subs = $post_proc->($p, $subs);
943 136         514 $self->{write_file_contents} = $p->{write_file_contents};
944             }
945             }
946              
947 214 100       1399 if ($self->{params}{post_proc_return}){
948 5         17 return $subs;
949             }
950              
951             # engine
952              
953 209         753 my $engine = $self->_engine($p, $subs);
954              
955 206 100       533 if ($self->{params}{engine}){
956 194         492 $subs = $engine->($p, $subs);
957 189         1595 $self->{write_file_contents} = $p->{write_file_contents};
958             }
959              
960             # core dump
961              
962 201 100       537 if ($self->{params}{core_dump}){
963            
964 1         77 print "\n\t Core Dump called...\n\n";
965 1         8 print "\n\n\t Dumping data... \n\n";
966 1         7 print Dumper $subs;
967              
968 1         112 print "\n\n\t Dumping instance...\n\n";
969 1         3 print Dumper $self;
970              
971 1         361 exit;
972             }
973              
974 200         1305 return $subs;
975             }
976             sub _pre_proc {
977            
978 49 100   49   2013 trace() if $ENV{TRACE};
979              
980 48         123 my $self = shift;
981 48         59 my $p = shift;
982 48         60 my $subs = shift;
983              
984 48         69 my $pre_proc = $self->{params}{pre_proc};
985              
986 48 100 66     266 if (not $pre_proc or $pre_proc eq ''){
987 1         3 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 47 100       109 if ($pre_proc eq 'module'){
994 11         15 $self->{params}{pre_proc_return} = 1;
995             }
996              
997 47         45 my $cref;
998            
999 47 100       107 if (not ref($pre_proc) eq 'CODE'){
1000 46         98 my $pre_proc_module = $self->{namespace} . "::Preprocessor";
1001 46         328 my $compiler = $pre_proc_module->new;
1002              
1003 46 100       127 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 45         57 eval {
1009 45         173 $cref = $compiler->{pre_procs}{$pre_proc}->();
1010             };
1011            
1012 45 100       161 if ($@){
1013 4         10 $@ = "\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         807 confess $@;
1018             }
1019              
1020             }
1021              
1022 42 100       86 if (ref($pre_proc) eq 'CODE'){
1023 1         2 $cref = $pre_proc;
1024             }
1025            
1026 42         67 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 238 100   238   1331 trace() if $ENV{TRACE};
1041            
1042 237         349 my $self = shift;
1043 237         323 my $p = shift;
1044            
1045 237         475 my $file = $self->{params}{file};
1046              
1047 237 100       565 return {} if ! $file;
1048              
1049 226         866 my $PPI_doc = PPI::Document->new($file);
1050 226         6889536 my $PPI_subs = $PPI_doc->find('PPI::Statement::Sub');
1051              
1052 226 100       2146922 return if ! $PPI_subs;
1053              
1054 205         358 my %subs;
1055 205         675 $subs{$file} = {};
1056 205         325 my @sub_order;
1057              
1058 205         318 for my $PPI_sub (@{$PPI_subs}){
  205         581  
1059            
1060             my $include
1061 1663 100       3733 = defined $self->{params}{include} ? $self->{params}{include} : [];
1062             my $exclude
1063 1663 100       2622 = defined $self->{params}{exclude} ? $self->{params}{exclude} : [];
1064              
1065 1663 100       2901 delete $self->{params}{include} if $exclude->[0];
1066              
1067 1663         3810 my $name = $PPI_sub->name;
1068            
1069 1663         26570 push @sub_order, $name;
1070              
1071             # skip over excluded (or not included) subs
1072              
1073 1663 100       3114 next if grep {$name eq $_ } @$exclude;
  88         142  
1074              
1075 1655 100       2577 if ($include->[0]){
1076 127 100       144 next if (! grep {$name eq $_ && $_} @$include);
  303 100       739  
1077             }
1078              
1079 1560         3542 $subs{$file}{subs}{$name}{start} = $PPI_sub->line_number;
1080 1560         1426119 $subs{$file}{subs}{$name}{start}--;
1081              
1082 1560         2952 my $lines = $PPI_sub =~ y/\n//;
1083              
1084             $subs{$file}{subs}{$name}{end}
1085 1560         363563 = $subs{$file}{subs}{$name}{start} + $lines;
1086              
1087 1560         1754 my $count_start = $subs{$file}{subs}{$name}{start};
1088 1560         1176 $count_start--;
1089              
1090             my $sub_line_count
1091 1560         1941 = $subs{$file}{subs}{$name}{end} - $count_start;
1092              
1093 1560         1911 $subs{$file}{subs}{$name}{num_lines} = $sub_line_count;
1094              
1095 1560         2535 @{ $subs{$file}{subs}{$name}{code} } = split /\n/, $PPI_sub->content;
  1560         171736  
1096             }
1097            
1098 205         453 @{ $p->{order} } = @sub_order;
  205         1011  
1099 205         360 @{ $self->{order} } = @sub_order;
  205         764  
1100              
1101 205         1142 return \%subs;
1102             }
1103             sub _post_proc {
1104            
1105 106 100   106   1011 trace() if $ENV{TRACE};
1106              
1107 105         151 my $self = shift;
1108 105         145 my $p = shift;
1109 105         167 my $struct = shift;
1110              
1111 105         190 my $post_proc = $self->{params}{post_proc};
1112              
1113 105         165 my $post_proc_dump = $self->{params}{post_proc_dump};
1114              
1115 105         124 my @post_procs;
1116              
1117 105 100       244 if ($post_proc){
1118              
1119 104         138 my @post_proc_list;
1120              
1121 104 100       329 if (ref $post_proc ne 'ARRAY'){
1122 79         150 push @post_proc_list, $post_proc;
1123             }
1124             else {
1125 25         36 @post_proc_list = @{$post_proc};
  25         82  
1126             }
1127              
1128 104         235 for my $pf (@post_proc_list){
1129              
1130 144         158 my $cref;
1131              
1132 144 100       345 if (ref $pf ne 'CODE'){
1133              
1134 139         355 my $post_proc_module = $self->{namespace} . "::Postprocessor";
1135 139         1140 my $compiler = $post_proc_module->new;
1136              
1137             # post_proc isn't in the dispatch table
1138              
1139 139 100       424 if (! $compiler->exists($pf)){
1140 3         432 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 136         228 eval {
1146 136         443 $cref = $compiler->{post_procs}{$pf}->();
1147             };
1148            
1149 136 100       609 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         98 confess $@;
1156             }
1157             }
1158 140 100       361 if (ref($pf) eq 'CODE'){
1159 5         7 $cref = $pf;
1160             }
1161              
1162 140 100 100     354 if ($post_proc_dump && $post_proc_dump > 1){
1163 1         2 $self->{params}{post_proc_dump}--;
1164 1         2 $post_proc_dump = $self->{params}{post_proc_dump};
1165             }
1166              
1167 140 100 66     354 if ($post_proc_dump && $post_proc_dump == 1){
1168 2         8 my $subs = $cref->($p, $struct);
1169 2         16 print Dumper $subs;
1170 2         1089 exit;
1171             }
1172 138         300 push @post_procs, $cref;
1173             }
1174             }
1175             else {
1176 1         2 return;
1177             }
1178 98         279 return @post_procs;
1179             }
1180             sub _engine {
1181            
1182 214 100   214   1351 trace() if $ENV{TRACE};
1183              
1184 213         329 my $self = shift;
1185 213         262 my $p = shift;
1186 213         270 my $struct = shift;
1187              
1188             my $engine
1189 213 100       734 = defined $p->{engine} ? $p->{engine} : $self->{params}{engine};
1190              
1191 213 100 66     1329 if (not $engine or $engine eq ''){
1192 13         30 return $struct;
1193             }
1194              
1195 200         259 my $cref;
1196              
1197 200 100       525 if (not ref($engine) eq 'CODE'){
1198              
1199             # engine is a name
1200              
1201 199         524 my $engine_module = $self->{namespace} . "::Engine";
1202 199         1985 my $compiler = $engine_module->new;
1203              
1204             # engine isn't in the dispatch table
1205              
1206 199 100       670 if (! $compiler->exists($engine)){
1207 1         90 confess "engine '$engine' is not implemented.\n";
1208             }
1209              
1210 198         303 eval {
1211 198         637 $cref = $compiler->{engines}{$engine}->();
1212             };
1213              
1214             # engine has bad func val in dispatch table, but key is ok
1215              
1216 198 100       879 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         167 confess $@;
1222             }
1223             }
1224              
1225 198 100       513 if (ref($engine) eq 'CODE'){
1226 1         2 $cref = $engine;
1227             }
1228              
1229 198 100       498 if ($self->{params}{engine_dump}){
1230 1         3 my $subs = $cref->($p, $struct);
1231 1         5 print Dumper $subs;
1232 1         183 exit;
1233             }
1234              
1235 197         371 return $cref;
1236             }
1237              
1238             #
1239             # pod
1240             #
1241              
1242 1     1   1940 sub _pod{1;} #vim placeholder
1243             1;
1244             __END__