Question

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.

Était-ce utile?

La solution 2

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 );
}

Autres conseils

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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top