Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine where Moose attributes and methods were inherited from?

I often work on a huge, not-very-well-documented, object-oriented Perl repo at my place of employment. While maintaining the code, I frequently need to trace things that are inherited from other classes so that I can understand what they're doing. For example, I need to figure out what $self->mystery is and what it's doing:

package Foo::Bar;
use Moose;
use Method::Signatures;
use Foo::Bar::Element;
use Foo::Bar::Function;
use base qw (Baz::Foo::Bar);

method do_stuff ($some_arg) {
    # mystery is not defined in Foo::Bar
    my $mystery = $self->mystery;
    $mystery->another_mystery($some_arg);
}

I usually find myself spending way too much time tracing through parent classes. So my question is, is there an easy way for me to figure out where $self->mystery comes from? Or in other words, I need to find where mystery is declared.

And by "easy way", I don't mean using ack or grep to string search through files. I'm hoping there's some sort of debugging module I can install and use which could help give me some insight.

Thank you.

like image 916
Dan Chrostowski Avatar asked Mar 06 '15 00:03

Dan Chrostowski


2 Answers

Thanks to Standard Perl . . . the comes_from Method!

You don’t need to download any special tool or module this, let alone some giant IDE because your undocumented class structure has gotten too complicated for mere humans ever to understand without a hulking IDE.

Why not? Simple: Standard Perl contains everything you need to get the answer you’re looking for. The easy way to find out where something comes from is to use the very useful comes_from method:

$origin        = $self->comes_from("mystery");
$secret_origin = $self->comes_from("another_mystery");
$birthplace    = Some::Class->comes_from("method_name");

That will return the original name of the subroutine which that method would resolve to. As you see, comes_from works as both an object method and a class method, just like can and isa.

Note that when I say the name of the subroutine it resolves to, I mean where that subroutine was originally created, back before any importing or inheritance. For example, this code:

use v5.10.1;
use Path::Router;
my($what, $method) = qw(Path::Router dump);
say "$what->$method is really ", $what->comes_from($method);

prints out:

Path::Router->dump is really Moose::Object::dump

Similar calls would also reveal things like:

Net::SMTP->mail     is really Net::SMTP::mail
Net::SMTP->status   is really Net::Cmd::status
Net::SMTP->error    is really IO::Handle::error

It works just fine on plain ole subroutines, too:

SQL::Translator::Parser::Storable->normalize_name 
 is really SQL::Translator::Utils::normalize_name

The lovely comes_from method isn’t quite built in though it requires nothing outside of Standard Perl. To make it accessible to you and all your classes and objects and more, just add this bit of code somewhere — anywhere you please really :)

sub UNIVERSAL::comes_from($$) {
    require B;
    my($invocant, $invoke) = @_;
    my $coderef  = $invocant->can($invoke) || return;
    my $cv       = B::svref_2object($coderef);
    return unless $cv->isa("B::CV");            
    my $gv       = $cv->GV;
    return if $gv->isa("B::SPECIAL");
    my $subname  = $gv->NAME;
    my $packname = $gv->STASH->NAME;
    return $packname . "::" . $subname;
}

By declaring that as a UNIVERSAL sub, now everybody who’s anybody gets to play with it, just like they do with can and isa. Enjoy!

like image 107
tchrist Avatar answered Nov 15 '22 09:11

tchrist


Are you sure you don't want an IDE? It seems to be what you are asking about. Padre, Eclipse EPIC, Emacs , and vim and many other editors offer some variation on the features you mention - probably simpler than you seem to want. If you have big project to navigate ctags can help - it's usually easy to integrate into an editor and you are allowed to hack on your configuration file (with regexes BTW) to get it to recognize bits of a complicated set of source files.

There is a related PERL FAQ entry about IDEs and a SO question: What's a good development environment for Perl?. There are also a host of CPAN modules you will want to use when developing that let you look into your code programmatically:

  • Devel::Kit
  • Devel::Peek
  • SUPER
  • Devel::Trepan
  • MooseX::amine / mex
  • ...

You can see an example of a script that looks for methods in classes in the SO node: Get all methods and/or properties in a given Perl class or module.

You might be able to get tools like these to help you hop around in your source in a way you find useful from a shell or from inside the debugger. Trepan has a good short summary of debugging tools as part of its documentation. Generally though you can be very productive combining Data::Dumper the B:: modules (B::Xref , B::Deparse, etc., etc.) with the debugger and ack.

like image 2
G. Cito Avatar answered Nov 15 '22 09:11

G. Cito