# # Copyright (c) 2004-2020 Christian Jaeger, copying@christianjaeger.ch # # This is free software, offered under either the same terms as perl 5 # or the terms of the Artistic License version 2 or the terms of the # MIT License (Expat version). See the file COPYING.md that came # bundled with this file. # =head1 NAME Chj::Class::methodnames =head1 SYNOPSIS use FP::Equal 'is_equal'; use Chj::Class::methodnames; { package Foo123; sub foo { }; sub bar { }; } is_equal [ methodnames bless {}, Foo123 ], [ "bar", "foo" ]; =head1 DESCRIPTION Return all names of methods, except for those in a stoplist; also recurses into parent packages following @ISA. =head1 FUNCTIONS set_stoplist(list of methodnames not to return) set another list than BEGIN and Dumper. (It sets a new hashref at $Chj::Class::methodnames::stop.) =head1 NOTE This is alpha software! Read the status section in the package README or on the L. =cut package Chj::Class::methodnames; use strict; use warnings; use warnings FATAL => 'uninitialized'; use Exporter "import"; our @EXPORT = qw(methodnames); use FP::Carp; our $stop = {}; sub set_stoplist { #my $class = shift; eh we are not a class here my $newstop = {}; for (@_) { $newstop->{$_} = undef } $stop = $newstop; } set_stoplist( qw( BEGIN Dumper ) ); sub methods_of_class { my ($class, $maybe_ignore_codes) = @_; if ($class eq 'Chj::Class::Array') { # since I have such a mess there, I exclude that one, and # return a list of only some of it's methods. return qw(clone ) } no strict 'refs'; my $class_array_namehash = do { if (defined *{ $class . "::_CLASS_ARRAY_COUNTER" }{SCALAR}) { +{ map { $_ => 1 } ( @{ $class . "::_CLASS_ARRAY_PUBLIC_FIELDS" }, @{ $class . "::_CLASS_ARRAY_PUBLICA_FIELDS" }, @{ $class . "::_CLASS_ARRAY_PROTECTED_FIELDS" }, @{ $class . "::_CLASS_ARRAY_PRIVATE_FIELDS" } ) } } else { undef } }; my $ignore_codes = defined $maybe_ignore_codes ? $maybe_ignore_codes : +{ map { $_ => 1 } ( (*Data::Dumper::Dumper{CODE} || ()), (*Carp::croak{CODE} || ()), (*Carp::carp{CODE} || ()), (*Carp::confess{CODE} || ()), (*Carp::cluck{CODE} || ()) ) }; if (my $hash = *{ $class . "::" }{HASH}) { my $code; # (ugly?) ( ( grep { ( not(exists $stop->{$_}) and not do { # constant name of class array based class. # or: constant at all?. how to find out if it's a constant? $class_array_namehash and exists $class_array_namehash->{$_} } and $code = *{ $class . "::" . $_ }{CODE} and not do { # exclude carp/croak, Dumper etc. $ignore_codes->{$code} } ) } keys %$hash ), do { if (my $isa = *{ $class . "::ISA" }{ARRAY}) { map { methods_of_class($_, $ignore_codes) } @$isa } else { () } } ) } else { () # or exception? } } sub methodnames { @_ == 1 or fp_croak_arity 1; my ($obj_or_class) = @_; my $class = ref($obj_or_class) || $obj_or_class; methods_of_class $class; } *Chj::Class::methodnames = \&methodnames; 1