获取给定Perl类或模块中的所有方法和/或属性

Héc*_*eja 5 methods perl uml

我正在处理一个明显的简单问题.

我正在编写一个类似于UML :: Class :: Simple的模块,但有一些改进.总而言之,我们的想法是为给定源中的每个模块检索记录卡,其中包含有关方法,属性,依赖关系和子项的信息.我目前的问题是获取每个模块的方法和属性.让我们看看我已经写过的代码:

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

为给定的包调用此函数,我得到的方法比我预期的多.原因是Class :: Inspector返回所有继承的方法,如果模块是Moose :: Object,则返回访问器.我想过滤所有这些方法,以获得在给定包中定义的方法,而不是在其父类中.

任何人都可以提供一种优雅的方式来按我建议的方式过滤方法列表吗?

提前致谢.

tob*_*ink 5

如果某个类是Moose类,请不要使用Class :: Inspector进行检查。Moose提供了自己非常广泛的自省API。它可以为您提供方法,属性等的列表。

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;
Run Code Online (Sandbox Code Playgroud)

可悲的是,所有这些文档都分散在许多不同的页面上。Moose :: Meta :: Class不是一个不错的起点。

鼠标提供了几乎但不完全相同的自省API。

Moo不提供自己的自省API,但是如果加载Moose会挂接到Moose的API中,以便您可以使用检索有关Moo类的信息Moose::Util::find_meta


Héc*_*eja 3

感谢@Oesor,他向我介绍了模块 Data::Printer ,该模块在其源代码中包含了我的问题的解决方案;感谢 @tobyink,他给了我解析 Moose 类的关键,我想出了以下解决方案:

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 );
}
Run Code Online (Sandbox Code Playgroud)