Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

What package is the method in?

by Sixtease (Friar)
on May 14, 2010 at 13:50 UTC ( #839996=perlquestion: print w/ replies, xml ) Need Help??
Sixtease has asked for the wisdom of the Perl Monks concerning the following question:

Friends,

how do I check the package from which a (possibly inherited) method will be called? Like if I see $foo->do_stuff(), I'd like to know which package the do_stuff method is located in.

Thanks in advance!

use strict; use warnings; print "Just Another Perl Hacker\n";

Comment on What package is the method in?
Select or Download Code
Re: What package is the method in?
by JavaFan (Canon) on May 14, 2010 at 14:01 UTC
    Untested:
    my @classes = ref $foo; my $method = "do_stuff"; while (@classes) { my $class = shift @classes; no strict 'refs'; if (defined &{"${class}::${method}"}) { say $class; exit; } unshift @classes, @{"${class}::ISA"}; } say "Not found";
    This doesn't catch cases where a method is dealt with by an AUTOLOAD function.

      You're making bad assumptions about the mro.

      And defined should be exists. sub f; will "handle" the method call, but you look pass it.

Re: What package is the method in?
by ikegami (Pope) on May 14, 2010 at 14:14 UTC

    I didn't find any modules that provided that functionality (although I didn't look very hard), but it's easy to code:

    use mro; sub find_implementor { my ($class, $method) = @_; for my $parent (@{ mro::get_linear_isa($class) }) { no strict 'refs'; return $parent if exists( &{"${parent}::$method"} ); } return 'UNIVERSAL' if UNIVERSAL->can($method); return find_implementor($class, 'AUTOLOAD') if $method ne 'AUTOLOAD' && $class->can($method); return undef; }
    use strict; use warnings; {package Foo; sub m1; sub m2 {} our @a; } {package Bar; our @ISA = 'Foo'; } print(find_implementor('Bar', 'm1' ) // "[undef]", "\n"); # Foo print(find_implementor('Bar', 'm2' ) // "[undef]", "\n"); # Foo print(find_implementor('Bar', 'a' ) // "[undef]", "\n"); # [undef] print(find_implementor('Bar', 'isa') // "[undef]", "\n"); # UNIVERSAL

    Update: Added support for AUTOLOAD. Fixed strict error in test.
    Update: Fixed AUTOLOAD.

      This doesn't look right. Perl's dispatcher will search through the entire @INC plus UNIVERSAL for the method. If it can't find it, it repeats the search from the beginning for AUTOLOAD. So if the first parent defines AUTOLOAD and the second parent defines sub foo {}, the second parent's foo {} is called before the first parent's AUTOLOAD.
        Thanks, Fixed. Since one should override can when one has an AUTOLOAD, I only search for AUTOLOAD if the class reports it has such a method.
Re: What package is the method in?
by moritz (Cardinal) on May 14, 2010 at 16:07 UTC
    ikegami's solution is pretty good, but in the spirit of TIMTOWTDI...

    With UNIVERSAL::can you can obtain a code ref to the module, and Sub::Identify tells you where it's from:

    use Sub::Identify qw(stash_name); print stash_name($obj->can('method_name'));

    Note that this might not be reliable, because sub names can be changed with Sub::Name.

    Perl 6 - links to (nearly) everything that is Perl 6.
      That was my first thought, but it doesn't work given my understanding of the question. It provides the package where the method was compiled, and that's not necessarily the class/package that provides the method.
      $ perl -le' { package Role; use Moose::Role; sub m {} } { package Class; use Moose; with "Role"; } use Sub::Identify qw( stash_name ); print stash_name( Class->new->can("m") ); ' Role

      Your method returns Role even though Class->new->m() would call Class::m. find_implementor returns Class.

Re: What package is the method in?
by bart (Canon) on May 16, 2010 at 14:43 UTC
    $foo->can will return a reference to a sub. All you still have to do is get the fully qualified name for the sub from that coderef. Sub::Identify should do the trick (though I haven't test it)...

    What's more: I think it should be possible to extract both the file name and the line number for the source file.
    Update: Oh yes it is possible, using B::svref_2object() just like the "Pure Perl" version of the above module, and you can find how in: Track the filename/line number of an anonymous coderef.

Re: What package is the method in?
by Sixtease (Friar) on May 17, 2010 at 11:14 UTC

    I thank you, my fellows.

    I was trying to go the bart's way but didn't know how to get the info from the coderef. My debugging and tracing hell has now been eased.

    use strict; use warnings; print "Just Another Perl Hacker\n";
Re: What package is the method in?
by Anonymous Monk on May 17, 2010 at 16:02 UTC
    from reading the code you can determine which package the method is used from :- perl looks in the list of classes (packages) in the @ISA array (in the order used in the @ISA array)and uses the first one it finds. Subpackages of the 1st entry are checked before the 2nd package.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://839996]
Approved by ikegami
Front-paged by MidLifeXis
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (7)
As of 2014-08-30 20:39 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (293 votes), past polls