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!
mystery
,ack '^sub mystery'
really is the best way to find where that method is defined. – Selfinterests
to step into the method call and see where it takes you. – Druggetmystery
can be an Moose attribute. Or use For this repo, it can either be asub
ormethod
, depending on who wrote it. Or it can be defined as a handler method:has foo => (is => 'ro', handles => [ qw(bar mystery baz) ]
– Shiflettmy $place_to_look = Magic::Module->($mystery)
Not necessarily what I'm looking for, but it would probably be most helpful. – Shiflettmystery
from somewhere. This is what I want to avoid. – Shiflettack '(?:sub|has|method|before|after|around) foo'
. I put the formula in a script to re-use it. – Selfinterest$self->another_mystery($arg)
. So now I need to regex again to findanother_mystery
. This gets tedious and complicated after 3 or 4 iterations, especially ifmystery
is defined in more than one place. I just want a quicker way. Hence my wording "is there a better way?" – Shiflett