Rename HasMethod & HasAttributes as Class::MOP::Mixin::...
[gitmo/Class-MOP.git] / lib / Class / MOP / Mixin / HasMethods.pm
CommitLineData
30bf0c82 1package Class::MOP::Mixin::HasMethods;
e3e651fb 2
3use strict;
4use warnings;
5
6use Scalar::Util 'blessed';
7use Carp 'confess';
8use Sub::Name 'subname';
9
10use base 'Class::MOP::Object';
11
12sub method_metaclass { $_[0]->{'method_metaclass'} }
13sub wrapped_method_metaclass { $_[0]->{'wrapped_method_metaclass'} }
14
15# This doesn't always get initialized in a constructor because there is a
16# weird object construction path for subclasses of Class::MOP::Class. At one
17# point, this always got initialized by calling into the XS code first, but
18# that is no longer guaranteed to happen.
19sub _method_map { $_[0]->{'methods'} ||= {} }
20
21sub wrap_method_body {
22 my ( $self, %args ) = @_;
23
24 ( 'CODE' eq ref $args{body} )
25 || confess "Your code block must be a CODE reference";
26
27 $self->method_metaclass->wrap(
28 package_name => $self->name,
29 %args,
30 );
31}
32
33sub add_method {
34 my ( $self, $method_name, $method ) = @_;
35 ( defined $method_name && length $method_name )
36 || confess "You must define a method name";
37
38 my $body;
39 if ( blessed($method) ) {
40 $body = $method->body;
41 if ( $method->package_name ne $self->name ) {
42 $method = $method->clone(
43 package_name => $self->name,
44 name => $method_name,
45 ) if $method->can('clone');
46 }
47
48 $method->attach_to_class($self);
49 }
50 else {
51 # If a raw code reference is supplied, its method object is not created.
52 # The method object won't be created until required.
53 $body = $method;
54 }
55
56 $self->_method_map->{$method_name} = $method;
57
58 my ( $current_package, $current_name ) = Class::MOP::get_code_info($body);
59
60 if ( !defined $current_name || $current_name =~ /^__ANON__/ ) {
61 my $full_method_name = ( $self->name . '::' . $method_name );
62 subname( $full_method_name => $body );
63 }
64
65 $self->add_package_symbol(
66 { sigil => '&', type => 'CODE', name => $method_name },
67 $body,
68 );
69}
70
71sub _code_is_mine {
72 my ( $self, $code ) = @_;
73
74 my ( $code_package, $code_name ) = Class::MOP::get_code_info($code);
75
76 return $code_package && $code_package eq $self->name
77 || ( $code_package eq 'constant' && $code_name eq '__ANON__' );
78}
79
80sub has_method {
81 my ( $self, $method_name ) = @_;
82
83 ( defined $method_name && length $method_name )
84 || confess "You must define a method name";
85
86 return defined( $self->get_method($method_name) );
87}
88
89sub get_method {
90 my ( $self, $method_name ) = @_;
91
92 ( defined $method_name && length $method_name )
93 || confess "You must define a method name";
94
95 my $method_map = $self->_method_map;
96 my $map_entry = $method_map->{$method_name};
97 my $code = $self->get_package_symbol(
98 {
99 name => $method_name,
100 sigil => '&',
101 type => 'CODE',
102 }
103 );
104
105 # This seems to happen in some weird cases where methods modifiers are
106 # added via roles or some other such bizareness. Honestly, I don't totally
107 # understand this, but returning the entry works, and keeps various MX
108 # modules from blowing up. - DR
109 return $map_entry if blessed $map_entry && !$code;
110
111 return $map_entry if blessed $map_entry && $map_entry->body == $code;
112
113 unless ($map_entry) {
114 return unless $code && $self->_code_is_mine($code);
115 }
116
117 $code ||= $map_entry;
118
119 return $method_map->{$method_name} = $self->wrap_method_body(
120 body => $code,
121 name => $method_name,
122 associated_metaclass => $self,
123 );
124}
125
126sub remove_method {
127 my ( $self, $method_name ) = @_;
128 ( defined $method_name && length $method_name )
129 || confess "You must define a method name";
130
131 my $removed_method = delete $self->_full_method_map->{$method_name};
132
133 $self->remove_package_symbol(
134 { sigil => '&', type => 'CODE', name => $method_name } );
135
136 $removed_method->detach_from_class
137 if $removed_method && blessed $removed_method;
138
139 # still valid, since we just removed the method from the map
140 $self->update_package_cache_flag;
141
142 return $removed_method;
143}
144
145sub get_method_list {
146 my $self = shift;
147 return grep { $self->has_method($_) } keys %{ $self->namespace };
148}
149
1501;