Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get all methods and/or properties in a given Perl class or module

Tags:

methods

perl

uml

I'm dealing with an apparent simple issue.

I'm writing a module similar to UML::Class::Simple but with some improvements. Summarizing, the idea is to retrieve a record card for each module in a given source, containing information about the Methods, Properties, Dependencies and Children. My current problem is getting Methods and Properties for each module. Let's see the code I've already written:

use Class::Inspector;
use Data::Dumper;
sub _load_methods{
  my $pkg = shift;
  my $methods = Class::Inspector->methods( $pkg, 'expanded' );
  print Dumper $methods;
  return 1;
}

Calling this function for a given package, I get more methods than I expect. The reason is Class::Inspector returns all inherited methods and also the accessors if the module is a Moose::Object. I would like to filter all of this methods to get just those defined in the given package, not in its parents.

Can anyone provide an elegant way to filter the list of methods in the way I suggest?

Thanks in advance.

like image 736
Héctor Valverde Pareja Avatar asked May 13 '14 15:05

Héctor Valverde Pareja


2 Answers

If a class is a Moose class, don't inspect it with Class::Inspector. Moose provides its own very extensive introspection API. It can give you lists of methods, attributes, etc.

my $meta = Moose::Util::find_meta($class_name);

my @isa    = $meta->superclasses;
my @does   = $meta->calculate_all_roles;
my @can    = $meta->get_method_list;
my @has    = $meta->get_attribute_list;

The documentation for all this is sadly split across a lot of different pages. Moose::Meta::Class is not a bad place to start.

Mouse provides an almost, but not quite identical introspection API.

Moo does not provide an introspection API of its own, but if Moose is loaded will hook into Moose's API, so that you can retrieve information about Moo classes using Moose::Util::find_meta.

like image 181
tobyink Avatar answered Nov 04 '22 21:11

tobyink


Thanks to @Oesor, who introduced to me the module Data::Printer, which contains a solution for my problem in its source code, and to @tobyink, who give me the key to parse Moose classes, I came up with the following solution:

sub _load_methods_for_one_pkg {
  # Inspired in Data::Printer::_show_methods
  # Thanks to Oesor
  my $pkg     = shift;
  my $string  = '';
  my $methods = {
    public  => [],
    private => [],
  };
  my $inherited = 'none';
  require B;
  my $methods_of = sub {
    my ($name) = @_;
    map {
      my $m;
      if (  $_
        and $m = B::svref_2object($_)
        and $m->isa('B::CV')
        and not $m->GV->isa('B::Special') )
      {
        [ $m->GV->STASH->NAME, $m->GV->NAME ];
      }
      else {
        ();
      }
    } values %{ Package::Stash->new($name)->get_all_symbols('CODE') };
  };
  my %seen_method_name;
METHOD:
  foreach my $method ( map $methods_of->($_), @{ mro::get_linear_isa($pkg) } ) {
    my ( $package_string, $method_string ) = @$method;
    next METHOD if $seen_method_name{$method_string}++;
    my $type = substr( $method_string, 0, 1 ) eq '_' ? 'private' : 'public';
    if ( $package_string ne $pkg ) {
      next METHOD
        unless $inherited ne 'none'
        and ( $inherited eq 'all' or $type eq $inherited );
      $method_string .= ' (' . $package_string . ')';
    }
    push @{ $methods->{$type} }, $method_string;
  }

# If is a Moose object, we have more things to do!
  if( grep 'Moose', @{ $self->dependencies->{ $pkg } }){
    my ($roles, $this_methods, $properties) = _parse_moose_class($pkg);
    push @{ $methods->{properties} }, @$properties;
    push @{ $methods->{roles} }, @$roles;
  }
  return $methods;
}

=head2 _parse_moose_class

=cut

sub _parse_moose_class{
  my $pkg = shift;
  my $meta = Moose::Util::find_meta($pkg);
  my @does = $meta->calculate_all_roles;
  my @can = $meta->get_method_list;
  my @has = $meta->get_attribute_list;
  return ( \@does, \@can, \@has );
}
like image 43
Héctor Valverde Pareja Avatar answered Nov 04 '22 22:11

Héctor Valverde Pareja