d568e5a89f227f6663d52cb887dfd640777d7a74
[gitmo/Class-MOP.git] / lib / Class / MOP / Mixin / HasMethods.pm
1 package Class::MOP::Mixin::HasMethods;
2
3 use strict;
4 use warnings;
5
6 our $VERSION   = '1.04';
7 $VERSION = eval $VERSION;
8 our $AUTHORITY = 'cpan:STEVAN';
9
10 use Scalar::Util 'blessed';
11 use Carp         'confess';
12 use Sub::Name    'subname';
13
14 use base 'Class::MOP::Mixin';
15
16 sub method_metaclass         { $_[0]->{'method_metaclass'}            }
17 sub wrapped_method_metaclass { $_[0]->{'wrapped_method_metaclass'}    }
18
19 # This doesn't always get initialized in a constructor because there is a
20 # weird object construction path for subclasses of Class::MOP::Class. At one
21 # point, this always got initialized by calling into the XS code first, but
22 # that is no longer guaranteed to happen.
23 sub _method_map { $_[0]->{'methods'} ||= {} }
24
25 sub wrap_method_body {
26     my ( $self, %args ) = @_;
27
28     ( 'CODE' eq ref $args{body} )
29         || confess "Your code block must be a CODE reference";
30
31     $self->method_metaclass->wrap(
32         package_name => $self->name,
33         %args,
34     );
35 }
36
37 sub add_method {
38     my ( $self, $method_name, $method ) = @_;
39     ( defined $method_name && length $method_name )
40         || confess "You must define a method name";
41
42     my $package_name = $self->name;
43
44     my $body;
45     if ( blessed($method) ) {
46         $body = $method->body;
47         if ( $method->package_name ne $package_name ) {
48             $method = $method->clone(
49                 package_name => $package_name,
50                 name         => $method_name,
51             ) if $method->can('clone');
52         }
53
54         $method->attach_to_class($self);
55     }
56     else {
57         # If a raw code reference is supplied, its method object is not created.
58         # The method object won't be created until required.
59         $body = $method;
60     }
61
62     $self->_method_map->{$method_name} = $method;
63
64     my ( $current_package, $current_name ) = Class::MOP::get_code_info($body);
65
66     if ( !defined $current_name || $current_name =~ /^__ANON__/ ) {
67         my $full_method_name = ( $package_name . '::' . $method_name );
68         subname( $full_method_name => $body );
69     }
70
71     $self->add_package_symbol(
72         { sigil => '&', type => 'CODE', name => $method_name },
73         $body,
74     );
75 }
76
77 sub _code_is_mine {
78     my ( $self, $code ) = @_;
79
80     my ( $code_package, $code_name ) = Class::MOP::get_code_info($code);
81
82     return $code_package && $code_package eq $self->name
83         || ( $code_package eq 'constant' && $code_name eq '__ANON__' );
84 }
85
86 sub has_method {
87     my ( $self, $method_name ) = @_;
88
89     ( defined $method_name && length $method_name )
90         || confess "You must define a method name";
91
92     return defined( $self->get_method($method_name) );
93 }
94
95 sub get_method {
96     my ( $self, $method_name ) = @_;
97
98     ( defined $method_name && length $method_name )
99         || confess "You must define a method name";
100
101     my $method_map = $self->_method_map;
102     my $map_entry  = $method_map->{$method_name};
103     my $code       = $self->get_package_symbol(
104         {
105             name  => $method_name,
106             sigil => '&',
107             type  => 'CODE',
108         }
109     );
110
111     # The !$code case seems to happen in some weird cases where methods
112     # modifiers are added via roles or some other such bizareness. Honestly, I
113     # don't totally understand this, but returning the entry works, and keeps
114     # various MX modules from blowing up. - DR
115     return $map_entry
116         if blessed $map_entry && ( !$code || $map_entry->body == $code );
117
118     unless ($map_entry) {
119         return unless $code && $self->_code_is_mine($code);
120     }
121
122     $code ||= $map_entry;
123
124     return $method_map->{$method_name} = $self->wrap_method_body(
125         body                 => $code,
126         name                 => $method_name,
127         associated_metaclass => $self,
128     );
129 }
130
131 sub remove_method {
132     my ( $self, $method_name ) = @_;
133     ( defined $method_name && length $method_name )
134         || confess "You must define a method name";
135
136     my $removed_method = delete $self->_full_method_map->{$method_name};
137
138     $self->remove_package_symbol(
139         { sigil => '&', type => 'CODE', name => $method_name } );
140
141     $removed_method->detach_from_class
142         if $removed_method && blessed $removed_method;
143
144     # still valid, since we just removed the method from the map
145     $self->update_package_cache_flag;
146
147     return $removed_method;
148 }
149
150 sub get_method_list {
151     my $self = shift;
152
153     my $namespace = $self->namespace;
154
155     # Constants will show up as some sort of reference in the namespace hash
156     # ref.
157     return grep {
158                ! ref $namespace->{$_}
159             && *{ $namespace->{$_} }{CODE}
160             && $self->has_method($_)
161         }
162         keys %{$namespace};
163 }
164
165 # This should probably be what get_method_list actually does, instead of just
166 # returning names. This was created as a much faster alternative to
167 # $meta->get_method($_) for $meta->get_method_list
168 sub _get_local_methods {
169     my $self = shift;
170
171     my $namespace = $self->namespace;
172
173     return map { $self->get_method($_) }
174         grep { ! ref $namespace->{$_} && *{ $namespace->{$_} }{CODE} }
175         keys %{$namespace};
176 }
177
178 1;
179
180 __END__
181
182 =pod
183
184 =head1 NAME
185
186 Class::MOP::Mixin::HasMethods - Methods for metaclasses which have methods
187
188 =head1 DESCRIPTION
189
190 This class implements methods for metaclasses which have methods
191 (L<Class::MOP::Package> and L<Moose::Meta::Role>). See L<Class::MOP::Package>
192 for API details.
193
194 =head1 AUTHORS
195
196 Dave Rolsky E<lt>autarch@urth.orgE<gt>
197
198 =head1 COPYRIGHT AND LICENSE
199
200 Copyright 2006-2010 by Infinity Interactive, Inc.
201
202 L<http://www.iinteractive.com>
203
204 This library is free software; you can redistribute it and/or modify
205 it under the same terms as Perl itself.
206
207 =cut