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

时间:2014-05-13 15:09:26

标签: perl methods 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;
}

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

有人能提供一种优雅的方式来按我建议的方式过滤方法列表吗?

提前致谢。

2 个答案:

答案 0 :(得分:4)

如果某个班级是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;

所有这些的文档很遗憾地分散在很多不同的页面上。 Moose::Meta::Class不是一个糟糕的起点。

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

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

答案 1 :(得分: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 );
}