same as C<$method_name>), the name of the class in which the method
lives and a CODE reference for the actual method.
+The list of methods produced is a distinct list, meaning there are no
+duplicates in it. This is especially useful for things like object
+initialization and destruction where you only want the method called
+once, and in the correct order.
+
=back
=head3 Attributes
my ($self, $method_name, $method) = @_;
(defined $method_name && $method_name)
|| confess "You must define a method name";
+ # use reftype here to allow for blessed subs ...
(reftype($method) && reftype($method) eq 'CODE')
|| confess "Your code block must be a CODE reference";
my $full_method_name = ($self->name . '::' . $method_name);
sub get_method_list {
my $self = shift;
no strict 'refs';
- grep {
- defined &{$self->name . '::' . $_} && $self->has_method($_)
- } %{$self->name . '::'};
+ grep { $self->has_method($_) } %{$self->name . '::'};
+}
+
+sub compute_all_applicable_methods {
+ my $self = shift;
+ my @methods;
+ # keep a record of what we have seen
+ # here, this will handle all the
+ # inheritence issues because we are
+ # using the &class_precedence_list
+ my (%seen_class, %seen_method);
+ foreach my $class ($self->class_precedence_list()) {
+ next if $seen_class{$class};
+ $seen_class{$class}++;
+ # fetch the meta-class ...
+ my $meta = $self->initialize($class);
+ foreach my $method_name ($meta->get_method_list()) {
+ next if exists $seen_method{$method_name};
+ $seen_method{$method_name}++;
+ push @methods => {
+ name => $method_name,
+ class => $class,
+ code => $meta->get_method($method_name)
+ };
+ }
+ }
+ return @methods;
+}
+
+## Recursive Version of compute_all_applicable_methods
+# sub compute_all_applicable_methods {
+# my ($self, $seen) = @_;
+# $seen ||= {};
+# (
+# (map {
+# if (exists $seen->{$_}) {
+# ();
+# }
+# else {
+# $seen->{$_}++;
+# {
+# name => $_,
+# class => $self->name,
+# code => $self->get_method($_)
+# };
+# }
+# } $self->get_method_list()),
+# map {
+# $self->initialize($_)->compute_all_applicable_methods($seen)
+# } $self->superclasses()
+# );
+# }
+
+sub find_all_methods_by_name {
+ my ($self, $method_name) = @_;
+ (defined $method_name && $method_name)
+ || confess "You must define a method name to find";
+ my @methods;
+ # keep a record of what we have seen
+ # here, this will handle all the
+ # inheritence issues because we are
+ # using the &class_precedence_list
+ my %seen_class;
+ foreach my $class ($self->class_precedence_list()) {
+ next if $seen_class{$class};
+ $seen_class{$class}++;
+ # fetch the meta-class ...
+ my $meta = $self->initialize($class);
+ push @methods => {
+ name => $method_name,
+ class => $class,
+ code => $meta->get_method($method_name)
+ } if $meta->has_method($method_name);
+ }
+ return @methods;
+
}
1;
use Test::More no_plan => 1;
use Test::Exception;
-use Test::Deep;
BEGIN {
use_ok('Class::MOP');
[ qw(FOO_CONSTANT bang bar baz blah bling evaled_foo floob foo) ],
'... got the right method list for Foo');
+is_deeply(
+ [ sort { $a->{name} cmp $b->{name} } $Foo->compute_all_applicable_methods() ],
+ [
+ map {
+ {
+ name => $_,
+ class => 'Foo',
+ code => $Foo->get_method($_)
+ }
+ } qw(
+ FOO_CONSTANT
+ bang
+ bar
+ baz
+ blah
+ bling
+ evaled_foo
+ floob
+ foo
+ )
+ ],
+ '... got the right list of applicable methods for Foo');
+
is($Foo->remove_method('foo'), $foo, '... removed the foo method');
ok(!$Foo->has_method('foo'), '... !Foo->has_method(foo) we just removed it');
dies_ok { Foo->foo } '... cannot call Foo->foo because it is not there';
is_deeply(
[ sort $Bar->get_method_list ],
[ qw(bar foo) ],
- '... got the right method list for Bar');
+ '... got the right method list for Bar');
+
+is_deeply(
+ [ sort { $a->{name} cmp $b->{name} } $Bar->compute_all_applicable_methods() ],
+ [
+ {
+ name => 'bang',
+ class => 'Foo',
+ code => $Foo->get_method('bang')
+ },
+ {
+ name => 'bar',
+ class => 'Bar',
+ code => $Bar->get_method('bar')
+ },
+ (map {
+ {
+ name => $_,
+ class => 'Foo',
+ code => $Foo->get_method($_)
+ }
+ } qw(
+ baz
+ blah
+ bling
+ evaled_foo
+ floob
+ )),
+ {
+ name => 'foo',
+ class => 'Bar',
+ code => $Bar->get_method('foo')
+ },
+ ],
+ '... got the right list of applicable methods for Bar');
+
+
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More no_plan => 1;
+use Test::Exception;
+
+BEGIN {
+ use_ok('Class::MOP');
+ use_ok('Class::MOP::Class');
+}
+
+=pod
+
+The following class hierarhcy is very contrived
+and totally horrid (it won't work under C3 even),
+but it tests a number of aspect of this module.
+
+A more real-world example would be a nice addition :)
+
+=cut
+
+{
+ package Foo;
+
+ sub BUILD { 'Foo::BUILD' }
+ sub foo { 'Foo::foo' }
+
+ package Bar;
+ our @ISA = ('Foo');
+
+ sub BUILD { 'Bar::BUILD' }
+ sub bar { 'Bar::bar' }
+
+ package Baz;
+ our @ISA = ('Bar');
+
+ sub BUILD { 'Baz::BUILD' }
+ sub baz { 'Baz::baz' }
+ sub foo { 'Baz::foo' }
+
+ package Foo::Bar;
+ our @ISA = ('Foo', 'Bar');
+
+ sub BUILD { 'Foo::Bar::BUILD' }
+ sub foobar { 'Foo::Bar::foobar' }
+
+ package Foo::Bar::Baz;
+ our @ISA = ('Foo', 'Bar', 'Baz');
+
+ sub BUILD { 'Foo::Bar::Baz::BUILD' }
+ sub bar { 'Foo::Bar::Baz::bar' }
+ sub foobarbaz { 'Foo::Bar::Baz::foobarbaz' }
+}
+
+is_deeply(
+ [ sort { $a->{name} cmp $b->{name} } Class::MOP::Class->initialize('Foo')->compute_all_applicable_methods() ],
+ [
+ {
+ name => 'BUILD',
+ class => 'Foo',
+ code => \&Foo::BUILD
+ },
+ {
+ name => 'foo',
+ class => 'Foo',
+ code => \&Foo::foo
+ },
+ ],
+ '... got the right list of applicable methods for Foo');
+
+is_deeply(
+ [ sort { $a->{name} cmp $b->{name} } Class::MOP::Class->initialize('Bar')->compute_all_applicable_methods() ],
+ [
+ {
+ name => 'BUILD',
+ class => 'Bar',
+ code => \&Bar::BUILD
+ },
+ {
+ name => 'bar',
+ class => 'Bar',
+ code => \&Bar::bar
+ },
+ {
+ name => 'foo',
+ class => 'Foo',
+ code => \&Foo::foo
+ },
+ ],
+ '... got the right list of applicable methods for Bar');
+
+
+is_deeply(
+ [ sort { $a->{name} cmp $b->{name} } Class::MOP::Class->initialize('Baz')->compute_all_applicable_methods() ],
+ [
+ {
+ name => 'BUILD',
+ class => 'Baz',
+ code => \&Baz::BUILD
+ },
+ {
+ name => 'bar',
+ class => 'Bar',
+ code => \&Bar::bar
+ },
+ {
+ name => 'baz',
+ class => 'Baz',
+ code => \&Baz::baz
+ },
+ {
+ name => 'foo',
+ class => 'Baz',
+ code => \&Baz::foo
+ },
+ ],
+ '... got the right list of applicable methods for Baz');
+
+is_deeply(
+ [ sort { $a->{name} cmp $b->{name} } Class::MOP::Class->initialize('Foo::Bar')->compute_all_applicable_methods() ],
+ [
+ {
+ name => 'BUILD',
+ class => 'Foo::Bar',
+ code => \&Foo::Bar::BUILD
+ },
+ {
+ name => 'bar',
+ class => 'Bar',
+ code => \&Bar::bar
+ },
+ {
+ name => 'foo',
+ class => 'Foo',
+ code => \&Foo::foo
+ },
+ {
+ name => 'foobar',
+ class => 'Foo::Bar',
+ code => \&Foo::Bar::foobar
+ },
+ ],
+ '... got the right list of applicable methods for Foo::Bar');
+
+is_deeply(
+ [ sort { $a->{name} cmp $b->{name} } Class::MOP::Class->initialize('Foo::Bar::Baz')->compute_all_applicable_methods() ],
+ [
+ {
+ name => 'BUILD',
+ class => 'Foo::Bar::Baz',
+ code => \&Foo::Bar::Baz::BUILD
+ },
+ {
+ name => 'bar',
+ class => 'Foo::Bar::Baz',
+ code => \&Foo::Bar::Baz::bar
+ },
+ {
+ name => 'baz',
+ class => 'Baz',
+ code => \&Baz::baz
+ },
+ {
+ name => 'foo',
+ class => 'Foo',
+ code => \&Foo::foo
+ },
+ {
+ name => 'foobarbaz',
+ class => 'Foo::Bar::Baz',
+ code => \&Foo::Bar::Baz::foobarbaz
+ },
+ ],
+ '... got the right list of applicable methods for Foo::Bar::Baz');
+
+## find_all_methods_by_name
+
+is_deeply(
+ [ Class::MOP::Class->initialize('Foo::Bar')->find_all_methods_by_name('BUILD') ],
+ [
+ {
+ name => 'BUILD',
+ class => 'Foo::Bar',
+ code => \&Foo::Bar::BUILD
+ },
+ {
+ name => 'BUILD',
+ class => 'Foo',
+ code => \&Foo::BUILD
+ },
+ {
+ name => 'BUILD',
+ class => 'Bar',
+ code => \&Bar::BUILD
+ }
+ ],
+ '... got the right list of BUILD methods for Foo::Bar');
+
+is_deeply(
+ [ Class::MOP::Class->initialize('Foo::Bar::Baz')->find_all_methods_by_name('BUILD') ],
+ [
+ {
+ name => 'BUILD',
+ class => 'Foo::Bar::Baz',
+ code => \&Foo::Bar::Baz::BUILD
+ },
+ {
+ name => 'BUILD',
+ class => 'Foo',
+ code => \&Foo::BUILD
+ },
+ {
+ name => 'BUILD',
+ class => 'Bar',
+ code => \&Bar::BUILD
+ },
+ {
+ name => 'BUILD',
+ class => 'Baz',
+ code => \&Baz::BUILD
+ },
+ ],
+ '... got the right list of BUILD methods for Foo::Bar::Baz');
\ No newline at end of file
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More no_plan => 1;
+use Test::Exception;
+
+BEGIN {
+ use_ok('Class::MOP::Class');
+}
+
+
+my $meta = Class::MOP::Class->initialize('Class::MOP::Class');
+isa_ok($meta, 'Class::MOP::Class');
+
+foreach my $method_name (qw(
+ initialize create
+ name version
+ superclasses class_precedence_list
+ has_method get_method add_method remove_method
+ get_method_list compute_all_applicable_methods find_all_methods_by_name
+ )) {
+ ok($meta->has_method($method_name), '... Class::MOP::Class->has_method(' . $method_name . ')');
+ {
+ no strict 'refs';
+ is($meta->get_method($method_name),
+ \&{'Class::MOP::Class::' . $method_name},
+ '... Class::MOP::Class->get_method(' . $method_name . ') == &Class::MOP::Class::' . $method_name);
+ }
+}
+
+is($meta->name, 'Class::MOP::Class', '... Class::MOP::Class->name');
+is($meta->version, $Class::MOP::Class::VERSION, '... Class::MOP::Class->version');
+
+is_deeply(
+ [ $meta->superclasses ],
+ [],
+ '... Class::MOP::Class->superclasses == []');
+
+is_deeply(
+ [ $meta->class_precedence_list ],
+ [ 'Class::MOP::Class' ],
+ '... Class::MOP::Class->class_precedence_list == []');
+