Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I loop over all the methods of a class in Perl?

How do you loop over all the methods of a class in Perl? Are there any good online references to Perl introspection or reflection?

like image 326
BuddyJoe Avatar asked Jun 20 '09 14:06

BuddyJoe


3 Answers

The recommendation Todd Gardner gave to use Moose is a good one, but the example code he chose isn't very helpful.

If you're inspecting a non-Moose using class, you'd do something like this:

use Some::Class;
use Class::MOP;

my $meta = Class::MOP::Class->initialize('Some::Class');

for my $meth ( $meta->get_all_methods ) {
    print $meth->fully_qualified_name, "\n";
}

See the Class::MOP::Class docs for more details on how to do introspection.

You'll also note that I used Class::MOP instead of Moose. Class::MOP (MOP = Meta-Object Protocol) is the base on which Moose builds. If you're working with non-Moose classes, using Moose to introspect doesn't gain you anything.

If you wanted, you could use Moose () and Moose::Meta::Class->initialize instead of CMOP.

like image 148
Dave Rolsky Avatar answered Sep 24 '22 06:09

Dave Rolsky


You can easily get a list of the defined methods of a class using the answers already provided. However, Perl is a dynamic language, which means more methods may be defined later. There really isn't a way to get a list of all of the methods to which any particular class will handle. For a lot more detail on this sort of stuff, I have a few chapters in Mastering Perl.

People are giving you (and upvoting) answers without telling you about the limitations.

Adam mentions his Class::Inspector, but it doesn't really work because it's trying to do something a dynamic language doesn't do (and that's be static :) For instance, here's a snippet where Class::Inspector returns no methods, but I can still call the VERSION method (as well as isa and can):

    BEGIN {

package Foo;

our $VERSION = '1.23'
}

use Class::Inspector;

my $methods = Class::Inspector->methods( 'Foo' );

print "Methods are [@$methods]\n"; # reports nothing

print Foo->VERSION, "\n";

Here's another case where I can call any method I like, but Class::Inspector only returns AUTOLOAD (and still missing VERSION, isa, and can):

BEGIN {

package Foo;

our $VERSION = '1.23';

my $object = bless {}, __PACKAGE__;

sub AUTOLOAD { $object }

}

use Class::Inspector;

my $methods = Class::Inspector->methods( 'Foo' );

print "Methods are [@$methods]\n"; # reports only "AUTOLOAD"

print Foo->dog->cat->bird, "\n";

Curiously, everyone seems to ignore UNIVERSAL, probably because they don't explicitly handle it since it's only virtually in @ISA. I can add a debug method to every class, and Class::Inspector still misses it even though it's a defined method:

BEGIN {

sub UNIVERSAL::debug { "Hello debugger!\n" }    
package Foo;
}

use Class::Inspector;

my $methods = Class::Inspector->methods( 'Foo' );

print "Methods are [@$methods]\n"; # still reports nothing

print Foo->debug, "\n";

Class::MOP has the same limitations.

Not every module is going to use AUTOLOAD, but it's not an obscure or rare feature either. If you don't mind that you are going to miss some of the methods then Class::Inspector or Class::MOP might be okay. It's just not going to give you a list of every method you can call on a class or an object in every case.

If you have a class or an object and you want to know if you can call a particular method, use can(). Wrap it in an eval block so can can call can() on things that aren't even objects to still get back false, instead of death, in those cases:

if( eval { $object->can( 'method_name' ) } )
    {
    $object->( @args );
    }
like image 41
brian d foy Avatar answered Sep 22 '22 06:09

brian d foy


In the general case, you'll have to inspect the symbol table (unless you use Moose). For example, to list the methods defined in the IO::File package:

use IO::File;
no strict 'refs';
print join ', ', grep { defined &{"IO::File::$_"} } keys %{IO::File::};

The hash %{IO::File::} is the symbol table of the IO::File package, and the grep filters out non-subroutine entries (e.g. package variables).

To extend this to include inherited methods, you have to recursively search the symbol tables of the parent classes (@IO::File::ISA).

Here is a complete example:

sub list_methods_for_class {
    my $class = shift;
    eval "require $class";
    no strict 'refs';
    my @methods = grep { defined &{$class . "::$_"} } keys %{$class . "::"};
    push @methods, list_methods_for_class($_) foreach @{$class . "::ISA"};
    return @methods;
}

For more info on packages and symbol tables, see the perlmod man page.

like image 40
trendels Avatar answered Sep 24 '22 06:09

trendels