Moose::Meta::Method::Accessor:
[gitmo/Moose.git] / lib / Moose / Meta / Class.pm
CommitLineData
c0e30cf5 1
2package Moose::Meta::Class;
3
4use strict;
5use warnings;
6
648e79ae 7use Class::MOP;
8
6ba6d68c 9use Carp 'confess';
54b1cdf0 10use Scalar::Util 'weaken', 'blessed', 'reftype';
a15dff8d 11
5cb193ed 12our $VERSION = '0.13';
d44714be 13our $AUTHORITY = 'cpan:STEVAN';
bc1e29b5 14
8ee73eeb 15use Moose::Meta::Method::Overriden;
16
c0e30cf5 17use base 'Class::MOP::Class';
18
598340d5 19__PACKAGE__->meta->add_attribute('roles' => (
ef333f17 20 reader => 'roles',
21 default => sub { [] }
22));
23
590868a3 24sub initialize {
25 my $class = shift;
26 my $pkg = shift;
27 $class->SUPER::initialize($pkg,
5cf3dbcf 28 'attribute_metaclass' => 'Moose::Meta::Attribute',
29 'method_metaclass' => 'Moose::Meta::Method',
30 'instance_metaclass' => 'Moose::Meta::Instance',
590868a3 31 @_);
1341f10c 32}
590868a3 33
ef333f17 34sub add_role {
35 my ($self, $role) = @_;
36 (blessed($role) && $role->isa('Moose::Meta::Role'))
37 || confess "Roles must be instances of Moose::Meta::Role";
38 push @{$self->roles} => $role;
39}
40
b8aeb4dc 41sub calculate_all_roles {
42 my $self = shift;
43 my %seen;
44 grep { !$seen{$_->name}++ } map { $_->calculate_all_roles } @{ $self->roles };
45}
46
ef333f17 47sub does_role {
48 my ($self, $role_name) = @_;
49 (defined $role_name)
50 || confess "You must supply a role name to look for";
9c429218 51 foreach my $class ($self->class_precedence_list) {
74f6d830 52 next unless $class->can('meta');
9c429218 53 foreach my $role (@{$class->meta->roles}) {
54 return 1 if $role->does_role($role_name);
55 }
ef333f17 56 }
57 return 0;
58}
59
d79e62fd 60sub excludes_role {
61 my ($self, $role_name) = @_;
62 (defined $role_name)
63 || confess "You must supply a role name to look for";
74f6d830 64 foreach my $class ($self->class_precedence_list) {
65 next unless $class->can('meta');
5cb193ed 66 # NOTE:
67 # in the pretty rare instance when a Moose metaclass
68 # is itself extended with a role, this check needs to
69 # be done since some items in the class_precedence_list
70 # might in fact be Class::MOP based still.
71 next unless $class->meta->can('roles');
9c429218 72 foreach my $role (@{$class->meta->roles}) {
73 return 1 if $role->excludes_role($role_name);
74 }
d79e62fd 75 }
76 return 0;
77}
78
8c9d74e7 79sub new_object {
80 my ($class, %params) = @_;
81 my $self = $class->SUPER::new_object(%params);
82 foreach my $attr ($class->compute_all_applicable_attributes()) {
715ea0b7 83 # FIXME:
84 # this does not accept undefined
85 # values, nor does it accept false
86 # values to be passed into the init-arg
5faf11bb 87 next unless $params{$attr->init_arg} && $attr->can('has_trigger') && $attr->has_trigger;
88 $attr->trigger->($self, $params{$attr->init_arg}, $attr);
8c9d74e7 89 }
90 return $self;
91}
92
a15dff8d 93sub construct_instance {
94 my ($class, %params) = @_;
ddd0ec20 95 my $meta_instance = $class->get_meta_instance;
575db57d 96 # FIXME:
97 # the code below is almost certainly incorrect
98 # but this is foreign inheritence, so we might
99 # have to kludge it in the end.
ddd0ec20 100 my $instance = $params{'__INSTANCE__'} || $meta_instance->create_instance();
74f6d830 101 foreach my $attr ($class->compute_all_applicable_attributes()) {
ddd0ec20 102 $attr->initialize_instance_slot($meta_instance, $instance, \%params)
a15dff8d 103 }
104 return $instance;
105}
106
a7d0cd00 107
093b12c2 108# FIXME:
109# This is ugly
110sub get_method_map {
111 my $self = shift;
5cf3dbcf 112 my $map = $self->{'%!methods'};
a7d0cd00 113
093b12c2 114 my $class_name = $self->name;
115 my $method_metaclass = $self->method_metaclass;
116
117 foreach my $symbol ($self->list_all_package_symbols('CODE')) {
118
119 my $code = $self->get_package_symbol('&' . $symbol);
120
121 next if exists $map->{$symbol} &&
122 defined $map->{$symbol} &&
123 $map->{$symbol}->body == $code;
124
125 my $gv = B::svref_2object($code)->GV;
126
127 my $pkg = $gv->STASH->NAME;
37ee30c9 128 if ($pkg->can('meta') && $pkg->meta && $pkg->meta->isa('Moose::Meta::Role')) {
093b12c2 129 #my $role = $pkg->meta->name;
130 #next unless $self->does_role($role);
131 }
132 else {
133 next if ($gv->STASH->NAME || '') ne $class_name &&
134 ($gv->NAME || '') ne '__ANON__';
135 }
136
137 $map->{$symbol} = $method_metaclass->wrap($code);
138 }
139
140 return $map;
a7d0cd00 141}
142
093b12c2 143### ---------------------------------------------
144
a2eec5e7 145sub add_attribute {
146 my $self = shift;
147 my $name = shift;
148 if (scalar @_ == 1 && ref($_[0]) eq 'HASH') {
149 # NOTE:
150 # if it is a HASH ref, we de-ref it.
151 # this will usually mean that it is
152 # coming from a role
153 $self->SUPER::add_attribute($name => %{$_[0]});
154 }
155 else {
156 # otherwise we just pass the args
157 $self->SUPER::add_attribute($name => @_);
158 }
159}
160
78cd1d3b 161sub add_override_method_modifier {
162 my ($self, $name, $method, $_super_package) = @_;
d05cd563 163 (!$self->has_method($name))
164 || confess "Cannot add an override method if a local method is already present";
78cd1d3b 165 # need this for roles ...
166 $_super_package ||= $self->name;
167 my $super = $self->find_next_method_by_name($name);
168 (defined $super)
169 || confess "You cannot override '$name' because it has no super method";
093b12c2 170 $self->add_method($name => Moose::Meta::Method::Overriden->wrap(sub {
78cd1d3b 171 my @args = @_;
172 no strict 'refs';
173 no warnings 'redefine';
174 local *{$_super_package . '::super'} = sub { $super->(@args) };
175 return $method->(@args);
093b12c2 176 }));
78cd1d3b 177}
178
179sub add_augment_method_modifier {
05d9eaf6 180 my ($self, $name, $method) = @_;
d05cd563 181 (!$self->has_method($name))
182 || confess "Cannot add an augment method if a local method is already present";
78cd1d3b 183 my $super = $self->find_next_method_by_name($name);
184 (defined $super)
05d9eaf6 185 || confess "You cannot augment '$name' because it has no super method";
186 my $_super_package = $super->package_name;
187 # BUT!,... if this is an overriden method ....
188 if ($super->isa('Moose::Meta::Method::Overriden')) {
189 # we need to be sure that we actually
190 # find the next method, which is not
191 # an 'override' method, the reason is
192 # that an 'override' method will not
193 # be the one calling inner()
194 my $real_super = $self->_find_next_method_by_name_which_is_not_overridden($name);
195 $_super_package = $real_super->package_name;
196 }
78cd1d3b 197 $self->add_method($name => sub {
198 my @args = @_;
199 no strict 'refs';
200 no warnings 'redefine';
05d9eaf6 201 local *{$_super_package . '::inner'} = sub { $method->(@args) };
78cd1d3b 202 return $super->(@args);
203 });
204}
205
1341f10c 206## Private Utility methods ...
207
05d9eaf6 208sub _find_next_method_by_name_which_is_not_overridden {
209 my ($self, $name) = @_;
68efb014 210 foreach my $method ($self->find_all_methods_by_name($name)) {
05d9eaf6 211 return $method->{code}
212 if blessed($method->{code}) && !$method->{code}->isa('Moose::Meta::Method::Overriden');
213 }
214 return undef;
215}
216
1341f10c 217sub _fix_metaclass_incompatability {
218 my ($self, @superclasses) = @_;
219 foreach my $super (@superclasses) {
220 # don't bother if it does not have a meta.
221 next unless $super->can('meta');
8ecb1fa0 222 # get the name, make sure we take
223 # immutable classes into account
224 my $super_meta_name = ($super->meta->is_immutable
225 ? $super->meta->get_mutable_metaclass_name
226 : blessed($super->meta));
1341f10c 227 # if it's meta is a vanilla Moose,
8ecb1fa0 228 # then we can safely ignore it.
229 next if $super_meta_name eq 'Moose::Meta::Class';
1341f10c 230 # but if we have anything else,
231 # we need to check it out ...
232 unless (# see if of our metaclass is incompatible
8ecb1fa0 233 ($self->isa($super_meta_name) &&
1341f10c 234 # and see if our instance metaclass is incompatible
235 $self->instance_metaclass->isa($super->meta->instance_metaclass)) &&
236 # ... and if we are just a vanilla Moose
237 $self->isa('Moose::Meta::Class')) {
238 # re-initialize the meta ...
239 my $super_meta = $super->meta;
240 # NOTE:
241 # We might want to consider actually
242 # transfering any attributes from the
243 # original meta into this one, but in
244 # general you should not have any there
245 # at this point anyway, so it's very
246 # much an obscure edge case anyway
247 $self = $super_meta->reinitialize($self->name => (
5cf3dbcf 248 'attribute_metaclass' => $super_meta->attribute_metaclass,
249 'method_metaclass' => $super_meta->method_metaclass,
250 'instance_metaclass' => $super_meta->instance_metaclass,
1341f10c 251 ));
252 }
253 }
254 return $self;
255}
256
257sub _apply_all_roles {
258 my ($self, @roles) = @_;
259 ($_->can('meta') && $_->meta->isa('Moose::Meta::Role'))
260 || confess "You can only consume roles, $_ is not a Moose role"
261 foreach @roles;
262 if (scalar @roles == 1) {
263 $roles[0]->meta->apply($self);
264 }
265 else {
68efb014 266 # FIXME
267 # we should make a Moose::Meta::Role::Composite
268 # which is a smaller version of Moose::Meta::Role
269 # which does not use any package stuff
1341f10c 270 Moose::Meta::Role->combine(
271 map { $_->meta } @roles
272 )->apply($self);
273 }
274}
275
276sub _process_attribute {
277 my ($self, $name, %options) = @_;
278 if ($name =~ /^\+(.*)/) {
279 my $new_attr = $self->_process_inherited_attribute($1, %options);
280 $self->add_attribute($new_attr);
281 }
282 else {
283 if ($options{metaclass}) {
c1935ade 284 my $metaclass_name = $options{metaclass};
285 eval {
286 my $possible_full_name = 'Moose::Meta::Attribute::Custom::' . $metaclass_name;
287 Class::MOP::load_class($possible_full_name);
288 $metaclass_name = $possible_full_name->can('register_implementation')
289 ? $possible_full_name->register_implementation
290 : $possible_full_name;
291 };
292 if ($@) {
293 Class::MOP::load_class($metaclass_name);
294 }
295 $self->add_attribute($metaclass_name->new($name, %options));
1341f10c 296 }
297 else {
298 $self->add_attribute($name, %options);
299 }
300 }
301}
302
303sub _process_inherited_attribute {
304 my ($self, $attr_name, %options) = @_;
305 my $inherited_attr = $self->find_attribute_by_name($attr_name);
306 (defined $inherited_attr)
307 || confess "Could not find an attribute by the name of '$attr_name' to inherit from";
308 my $new_attr;
309 if ($inherited_attr->isa('Moose::Meta::Attribute')) {
310 $new_attr = $inherited_attr->clone_and_inherit_options(%options);
311 }
312 else {
313 # NOTE:
314 # kind of a kludge to handle Class::MOP::Attributes
315 $new_attr = Moose::Meta::Attribute::clone_and_inherit_options(
316 $inherited_attr, %options
317 );
318 }
319 return $new_attr;
320}
321
5cf3dbcf 322## -------------------------------------------------
323
324use Moose::Meta::Method::Constructor;
1f779926 325use Moose::Meta::Method::Destructor;
5cf3dbcf 326
327{
328 # NOTE:
329 # the immutable version of a
330 # particular metaclass is
331 # really class-level data so
332 # we don't want to regenerate
333 # it any more than we need to
334 my $IMMUTABLE_METACLASS;
335 sub make_immutable {
336 my $self = shift;
337
338 $IMMUTABLE_METACLASS ||= Class::MOP::Immutable->new($self, {
339 read_only => [qw/superclasses/],
340 cannot_call => [qw/
341 add_method
342 alias_method
343 remove_method
344 add_attribute
345 remove_attribute
346 add_package_symbol
347 remove_package_symbol
348 add_role
349 /],
350 memoize => {
351 class_precedence_list => 'ARRAY',
352 compute_all_applicable_attributes => 'ARRAY',
353 get_meta_instance => 'SCALAR',
354 get_method_map => 'SCALAR',
355 # maybe ....
356 calculate_all_roles => 'ARRAY',
357 }
358 });
359
360 $IMMUTABLE_METACLASS->make_metaclass_immutable(
361 $self,
362 constructor_class => 'Moose::Meta::Method::Constructor',
1f779926 363 destructor_class => 'Moose::Meta::Method::Destructor',
364 inline_destructor => 1,
365 # NOTE:
366 # no need to do this,
367 # Moose always does it
5cf3dbcf 368 inline_accessors => 0,
369 @_,
370 )
371 }
372}
373
c0e30cf5 3741;
375
376__END__
377
378=pod
379
380=head1 NAME
381
e522431d 382Moose::Meta::Class - The Moose metaclass
c0e30cf5 383
c0e30cf5 384=head1 DESCRIPTION
385
e522431d 386This is a subclass of L<Class::MOP::Class> with Moose specific
387extensions.
388
6ba6d68c 389For the most part, the only time you will ever encounter an
390instance of this class is if you are doing some serious deep
391introspection. To really understand this class, you need to refer
392to the L<Class::MOP::Class> documentation.
393
c0e30cf5 394=head1 METHODS
395
396=over 4
397
590868a3 398=item B<initialize>
399
5cf3dbcf 400=item B<make_immutable>
401
8c9d74e7 402=item B<new_object>
403
02a0fb52 404We override this method to support the C<trigger> attribute option.
405
a15dff8d 406=item B<construct_instance>
407
6ba6d68c 408This provides some Moose specific extensions to this method, you
409almost never call this method directly unless you really know what
410you are doing.
411
412This method makes sure to handle the moose weak-ref, type-constraint
413and type coercion features.
ef1d5f4b 414
093b12c2 415=item B<get_method_map>
e9ec68d6 416
68efb014 417This accommodates Moose::Meta::Role::Method instances, which are
e9ec68d6 418aliased, instead of added, but still need to be counted as valid
419methods.
420
78cd1d3b 421=item B<add_override_method_modifier ($name, $method)>
422
02a0fb52 423This will create an C<override> method modifier for you, and install
424it in the package.
425
78cd1d3b 426=item B<add_augment_method_modifier ($name, $method)>
427
02a0fb52 428This will create an C<augment> method modifier for you, and install
429it in the package.
430
2b14ac61 431=item B<calculate_all_roles>
432
ef333f17 433=item B<roles>
434
02a0fb52 435This will return an array of C<Moose::Meta::Role> instances which are
436attached to this class.
437
ef333f17 438=item B<add_role ($role)>
439
02a0fb52 440This takes an instance of C<Moose::Meta::Role> in C<$role>, and adds it
441to the list of associated roles.
442
ef333f17 443=item B<does_role ($role_name)>
444
02a0fb52 445This will test if this class C<does> a given C<$role_name>. It will
446not only check it's local roles, but ask them as well in order to
447cascade down the role hierarchy.
448
d79e62fd 449=item B<excludes_role ($role_name)>
450
451This will test if this class C<excludes> a given C<$role_name>. It will
452not only check it's local roles, but ask them as well in order to
453cascade down the role hierarchy.
454
9e93dd19 455=item B<add_attribute ($attr_name, %params|$params)>
4e848edb 456
9e93dd19 457This method does the same thing as L<Class::MOP::Class::add_attribute>, but adds
458support for taking the C<$params> as a HASH ref.
ac1ef2f9 459
c0e30cf5 460=back
461
462=head1 BUGS
463
464All complex software has bugs lurking in it, and this module is no
465exception. If you find a bug please either email me, or add the bug
466to cpan-RT.
467
c0e30cf5 468=head1 AUTHOR
469
470Stevan Little E<lt>stevan@iinteractive.comE<gt>
471
472=head1 COPYRIGHT AND LICENSE
473
b77fdbed 474Copyright 2006, 2007 by Infinity Interactive, Inc.
c0e30cf5 475
476L<http://www.iinteractive.com>
477
478This library is free software; you can redistribute it and/or modify
479it under the same terms as Perl itself.
480
8a7a9c53 481=cut
1a563243 482