I've updated my Devel::Examine::Subs module, and I thought I'd give a bit of a demo of a few of the tasks it can now perform.

EDIT: I just noticed a significant issue with cache. Please don't use it while this notice is posted.

Initialize a DES object, setting some global parameters...

use Devel::Examine::Subs; my $dir = '/home/steve/devel/repos/project'; my $des = Devel::Examine::Subs->new({ file => $dir, regex => 1, });

Notes: The cache parameter should always be used in the new() call, except for doing write operations which we're doing below.

Search for 'template.tpl' in all subs in .pm files and replace it with 'new_template.pl', except in any subs named one or three. By default, it processes .pm and .pl files...

$des->search_replace({ exclude => ['one', 'three'], search => '(\W)template.tpl(\W)', replace => 'new_template.tpl', extensions => ['pm'], });

Notes: exclude and extensions need to be set back to some form of non-true value for further calls under the same object, if they are not needed any further. In the next release, I'll have added the ability to change this behaviour.

Look for any variant of $self = shift in all subs of all .pm and .pl files, and inject new code after it (like all other methods, this one obeys include and exclude).

my @code = <DATA>; $des->inject_after({ search => '\$self\s+=\s+shift', code => \@code, }); __DATA__ $self->{thing} = some_function(); my $debug = 1 if $self->{thing}; if ($debug){ print Dumper $self; exit; }

Notes: copy => 'some_file.ext; can be sent in, and it'll make all the changes to that file in the local directory for review before removing it and editing the live file. The injects param informs when to stop searching and injecting. The default is stop after the first search term (1). DES honours the indenting found on the line the search term was on. Set no_indent if you don't want this behaviour.

Let's create a new object for read-only operations, and set up caching.

my $des = Devel::Examine::Subs->new({ file => 'file.pm', cache => 1, });

Get all subs from the file and put them into objects...

my $objects_aref = $des->objects();

Print out some info...

for my $sub (@$objects_aref){ say "name: " . $sub->name; say "first line num: " . $sub->start; say "last line num: " . $sub->end; say "line count: " . $sub->line_count; say "sub code: "; say "\t$_" for @{$sub->code}; print "\n"; if ($sub->lines->[0]){ say "Lines that match: "; for my $line (@{$sub->lines}){ say "\t$line"; } print "\n"; } }

Notes: $sub->lines() contains an array ref of strings that contain the line number and text (separated by a :) of the lines that contain a search term, if a search term was passed in.

End notes: Everything above performed on a single file can be run in directory mode as well. To present output after a call, wrap it in one level deeper:

# dir search returns an href. key is the filename of # the file the subs were found in, and the values are the # same return you'd get from the same call on an individual # file my $files = $des->objects(); for my $file (keys %$files){ for my $sub (@{$files->{$file}}){ say $sub->name; ... } }

Get all sub names in a file...

my $aref = $des->all();

All subs that contain or don't contain a search term...

my $aref = $des->has({search => 'this'}); my $aref = $des->missing({search => 'this'});

All subs in all files...

my $files = $des->all({file => 'dir'}); for my $file (keys %$files){ say $file; for my $sub (@{$files->{$file}}){ say "\t$sub"; } print "\n"; }

It can do much more than this, so please feel free to read through the documentation specified in the README and play around. I urge you to provide feedback if any bugs are found or to see if something is available (or if its on the roadmap) and/or if you have any suggestions whatsoever.

Thanks for reading!

-stevieb

ps.

The long story behind this module is that years ago, I was writing a multi-module ISP accounting/billing/tracking system, and wanted a way for every single method in every single sub to call out to a tracing function in order to store all stack information. This module is how I envisioned at the time injecting such code.

After I add a few more methods to this module add_sub(), add_use() etc, I'll be rewriting my Devel::Trace::Method to do just this.

Next additions (above and beyond those stated in my last comment), is to allow editing any live module file by specifying the module name as in Data::Dumper (already half implemented in the add_functionality() method, add the creating and storing of diffs with the ability to apply them back if something breaks, clean up the configuration parameter infrastructure and a few other small tasks, such as adding POD for the sub-modules.