FileHandle also accepts IO::Handle objects
[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
25374f01 12our $VERSION = '0.17';
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,
ac2dc464 28 'attribute_metaclass' => 'Moose::Meta::Attribute',
5cf3dbcf 29 'method_metaclass' => 'Moose::Meta::Method',
ac2dc464 30 'instance_metaclass' => 'Moose::Meta::Instance',
590868a3 31 @_);
ac2dc464 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) {
ac2dc464 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";
ac2dc464 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
ac2dc464 68 # is itself extended with a role, this check needs to
5cb193ed 69 # be done since some items in the class_precedence_list
ac2dc464 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
ac2dc464 85 # values, nor does it accept false
715ea0b7 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 }
ac2dc464 90 return $self;
8c9d74e7 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
ac2dc464 99 # have to kludge it in the end.
ddd0ec20 100 my $instance = $params{'__INSTANCE__'} || $meta_instance->create_instance();
ac2dc464 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
093b12c2 107# FIXME:
108# This is ugly
ac2dc464 109sub get_method_map {
093b12c2 110 my $self = shift;
53dd42d8 111
112 if (defined $self->{'$!_package_cache_flag'} &&
66e08a8a 113 $self->{'$!_package_cache_flag'} == Class::MOP::check_package_cache_flag($self->meta->name)) {
53dd42d8 114 return $self->{'%!methods'};
115 }
116
ac2dc464 117 my $map = $self->{'%!methods'};
118
093b12c2 119 my $class_name = $self->name;
120 my $method_metaclass = $self->method_metaclass;
ac2dc464 121
093b12c2 122 foreach my $symbol ($self->list_all_package_symbols('CODE')) {
ac2dc464 123
093b12c2 124 my $code = $self->get_package_symbol('&' . $symbol);
ac2dc464 125
126 next if exists $map->{$symbol} &&
127 defined $map->{$symbol} &&
128 $map->{$symbol}->body == $code;
129
53dd42d8 130 my ($pkg, $name) = Class::MOP::get_code_info($code);
ac2dc464 131
53dd42d8 132 if ($pkg->can('meta')
4f8f3aab 133 # NOTE:
134 # we don't know what ->meta we are calling
53dd42d8 135 # here, so we need to be careful cause it
136 # just might blow up at us, or just complain
137 # loudly (in the case of Curses.pm) so we
4f8f3aab 138 # just be a little overly cautious here.
139 # - SL
140 && eval { no warnings; blessed($pkg->meta) }
141 && $pkg->meta->isa('Moose::Meta::Role')) {
093b12c2 142 #my $role = $pkg->meta->name;
143 #next unless $self->does_role($role);
144 }
145 else {
53dd42d8 146 next if ($pkg || '') ne $class_name &&
147 ($name || '') ne '__ANON__';
148
093b12c2 149 }
ac2dc464 150
093b12c2 151 $map->{$symbol} = $method_metaclass->wrap($code);
152 }
ac2dc464 153
093b12c2 154 return $map;
a7d0cd00 155}
156
093b12c2 157### ---------------------------------------------
158
a2eec5e7 159sub add_attribute {
160 my $self = shift;
d9bb6c63 161 $self->SUPER::add_attribute($self->_process_attribute(@_));
a2eec5e7 162}
163
78cd1d3b 164sub add_override_method_modifier {
165 my ($self, $name, $method, $_super_package) = @_;
d05cd563 166 (!$self->has_method($name))
167 || confess "Cannot add an override method if a local method is already present";
78cd1d3b 168 # need this for roles ...
169 $_super_package ||= $self->name;
170 my $super = $self->find_next_method_by_name($name);
171 (defined $super)
ac2dc464 172 || confess "You cannot override '$name' because it has no super method";
093b12c2 173 $self->add_method($name => Moose::Meta::Method::Overriden->wrap(sub {
78cd1d3b 174 my @args = @_;
78cd1d3b 175 no warnings 'redefine';
52c7c330 176 if ($Moose::SUPER_SLOT{$_super_package}) {
b644e331 177 local *{$Moose::SUPER_SLOT{$_super_package}} = sub { $super->body->(@args) };
b468a3d3 178 return $method->(@args);
52c7c330 179 } else {
b468a3d3 180 confess "Trying to call override modifier'd method without super()";
52c7c330 181 }
093b12c2 182 }));
78cd1d3b 183}
184
185sub add_augment_method_modifier {
ac2dc464 186 my ($self, $name, $method) = @_;
d05cd563 187 (!$self->has_method($name))
ac2dc464 188 || confess "Cannot add an augment method if a local method is already present";
78cd1d3b 189 my $super = $self->find_next_method_by_name($name);
190 (defined $super)
ac2dc464 191 || confess "You cannot augment '$name' because it has no super method";
192 my $_super_package = $super->package_name;
193 # BUT!,... if this is an overriden method ....
05d9eaf6 194 if ($super->isa('Moose::Meta::Method::Overriden')) {
ac2dc464 195 # we need to be sure that we actually
196 # find the next method, which is not
05d9eaf6 197 # an 'override' method, the reason is
ac2dc464 198 # that an 'override' method will not
05d9eaf6 199 # be the one calling inner()
ac2dc464 200 my $real_super = $self->_find_next_method_by_name_which_is_not_overridden($name);
05d9eaf6 201 $_super_package = $real_super->package_name;
ac2dc464 202 }
78cd1d3b 203 $self->add_method($name => sub {
204 my @args = @_;
78cd1d3b 205 no warnings 'redefine';
52c7c330 206 if ($Moose::INNER_SLOT{$_super_package}) {
53dd42d8 207 local *{$Moose::INNER_SLOT{$_super_package}} = sub {
208 local *{$Moose::INNER_SLOT{$_super_package}} = sub {};
b468a3d3 209 $method->(@args);
210 };
b644e331 211 return $super->body->(@args);
53dd42d8 212 }
213 else {
b644e331 214 return $super->body->(@args);
52c7c330 215 }
ac2dc464 216 });
78cd1d3b 217}
218
1341f10c 219## Private Utility methods ...
220
05d9eaf6 221sub _find_next_method_by_name_which_is_not_overridden {
222 my ($self, $name) = @_;
68efb014 223 foreach my $method ($self->find_all_methods_by_name($name)) {
ac2dc464 224 return $method->{code}
05d9eaf6 225 if blessed($method->{code}) && !$method->{code}->isa('Moose::Meta::Method::Overriden');
226 }
227 return undef;
228}
229
1341f10c 230sub _fix_metaclass_incompatability {
231 my ($self, @superclasses) = @_;
232 foreach my $super (@superclasses) {
233 # don't bother if it does not have a meta.
234 next unless $super->can('meta');
ac2dc464 235 # get the name, make sure we take
8ecb1fa0 236 # immutable classes into account
ac2dc464 237 my $super_meta_name = ($super->meta->is_immutable
8ecb1fa0 238 ? $super->meta->get_mutable_metaclass_name
239 : blessed($super->meta));
ac2dc464 240 # if it's meta is a vanilla Moose,
241 # then we can safely ignore it.
8ecb1fa0 242 next if $super_meta_name eq 'Moose::Meta::Class';
ac2dc464 243 # but if we have anything else,
1341f10c 244 # we need to check it out ...
245 unless (# see if of our metaclass is incompatible
8ecb1fa0 246 ($self->isa($super_meta_name) &&
1341f10c 247 # and see if our instance metaclass is incompatible
248 $self->instance_metaclass->isa($super->meta->instance_metaclass)) &&
249 # ... and if we are just a vanilla Moose
250 $self->isa('Moose::Meta::Class')) {
251 # re-initialize the meta ...
252 my $super_meta = $super->meta;
253 # NOTE:
ac2dc464 254 # We might want to consider actually
255 # transfering any attributes from the
256 # original meta into this one, but in
1341f10c 257 # general you should not have any there
ac2dc464 258 # at this point anyway, so it's very
1341f10c 259 # much an obscure edge case anyway
260 $self = $super_meta->reinitialize($self->name => (
ac2dc464 261 'attribute_metaclass' => $super_meta->attribute_metaclass,
5cf3dbcf 262 'method_metaclass' => $super_meta->method_metaclass,
263 'instance_metaclass' => $super_meta->instance_metaclass,
1341f10c 264 ));
265 }
266 }
ac2dc464 267 return $self;
1341f10c 268}
269
d7d8a8c7 270# NOTE:
d9bb6c63 271# this was crap anyway, see
272# Moose::Util::apply_all_roles
d7d8a8c7 273# instead
274sub _apply_all_roles { die "DEPRECATED" }
1341f10c 275
d9bb6c63 276my %ANON_CLASSES;
277
1341f10c 278sub _process_attribute {
d9bb6c63 279 my $self = shift;
280 my $name = shift;
281 my %options = ((scalar @_ == 1 && ref($_[0]) eq 'HASH') ? %{$_[0]} : @_);
282
1341f10c 283 if ($name =~ /^\+(.*)/) {
d7d8a8c7 284 return $self->_process_inherited_attribute($1, %options);
1341f10c 285 }
286 else {
d9bb6c63 287 my $attr_metaclass_name;
1341f10c 288 if ($options{metaclass}) {
c1935ade 289 my $metaclass_name = $options{metaclass};
290 eval {
291 my $possible_full_name = 'Moose::Meta::Attribute::Custom::' . $metaclass_name;
ac2dc464 292 Class::MOP::load_class($possible_full_name);
293 $metaclass_name = $possible_full_name->can('register_implementation')
c1935ade 294 ? $possible_full_name->register_implementation
295 : $possible_full_name;
296 };
297 if ($@) {
298 Class::MOP::load_class($metaclass_name);
299 }
d9bb6c63 300 $attr_metaclass_name = $metaclass_name;
1341f10c 301 }
302 else {
d9bb6c63 303 $attr_metaclass_name = $self->attribute_metaclass;
1341f10c 304 }
d9bb6c63 305
306 if ($options{traits}) {
307
308 my $anon_role_key = join "|" => @{$options{traits}};
309
310 my $class;
311 if (exists $ANON_CLASSES{$anon_role_key} && defined $ANON_CLASSES{$anon_role_key}) {
312 $class = $ANON_CLASSES{$anon_role_key};
313 }
314 else {
315 $class = Moose::Meta::Class->create_anon_class(
316 superclasses => [ $attr_metaclass_name ]
317 );
318 $ANON_CLASSES{$anon_role_key} = $class;
319 Moose::Util::apply_all_roles($class, @{$options{traits}});
320 }
321
322 $attr_metaclass_name = $class->name;
323 }
324
325 return $attr_metaclass_name->new($name, %options);
ac2dc464 326 }
1341f10c 327}
328
329sub _process_inherited_attribute {
330 my ($self, $attr_name, %options) = @_;
331 my $inherited_attr = $self->find_attribute_by_name($attr_name);
332 (defined $inherited_attr)
333 || confess "Could not find an attribute by the name of '$attr_name' to inherit from";
1341f10c 334 if ($inherited_attr->isa('Moose::Meta::Attribute')) {
d7d8a8c7 335 return $inherited_attr->clone_and_inherit_options(%options);
1341f10c 336 }
337 else {
338 # NOTE:
339 # kind of a kludge to handle Class::MOP::Attributes
d7d8a8c7 340 return $inherited_attr->Moose::Meta::Attribute::clone_and_inherit_options(%options);
ac2dc464 341 }
1341f10c 342}
343
5cf3dbcf 344## -------------------------------------------------
345
346use Moose::Meta::Method::Constructor;
1f779926 347use Moose::Meta::Method::Destructor;
5cf3dbcf 348
ac2dc464 349# This could be done by using SUPER and altering ->options
350# I am keeping it this way to make it more explicit.
351sub create_immutable_transformer {
352 my $self = shift;
353 my $class = Class::MOP::Immutable->new($self, {
354 read_only => [qw/superclasses/],
355 cannot_call => [qw/
356 add_method
357 alias_method
358 remove_method
359 add_attribute
360 remove_attribute
361 add_package_symbol
362 remove_package_symbol
363 add_role
364 /],
365 memoize => {
366 class_precedence_list => 'ARRAY',
367 compute_all_applicable_attributes => 'ARRAY',
368 get_meta_instance => 'SCALAR',
369 get_method_map => 'SCALAR',
370 # maybe ....
371 calculate_all_roles => 'ARRAY',
372 }
373 });
374 return $class;
375}
376
377sub make_immutable {
378 my $self = shift;
379 $self->SUPER::make_immutable
380 (
381 constructor_class => 'Moose::Meta::Method::Constructor',
382 destructor_class => 'Moose::Meta::Method::Destructor',
383 inline_destructor => 1,
384 # NOTE:
385 # no need to do this,
386 # Moose always does it
387 inline_accessors => 0,
388 @_,
389 );
5cf3dbcf 390}
391
c0e30cf5 3921;
393
394__END__
395
396=pod
397
398=head1 NAME
399
e522431d 400Moose::Meta::Class - The Moose metaclass
c0e30cf5 401
c0e30cf5 402=head1 DESCRIPTION
403
ac2dc464 404This is a subclass of L<Class::MOP::Class> with Moose specific
e522431d 405extensions.
406
ac2dc464 407For the most part, the only time you will ever encounter an
408instance of this class is if you are doing some serious deep
409introspection. To really understand this class, you need to refer
6ba6d68c 410to the L<Class::MOP::Class> documentation.
411
c0e30cf5 412=head1 METHODS
413
414=over 4
415
590868a3 416=item B<initialize>
417
5cf3dbcf 418=item B<make_immutable>
419
ac2dc464 420Override original to add default options for inlining destructor
421and altering the Constructor metaclass.
422
423=item B<create_immutable_transformer>
424
425Override original to lock C<add_role> and memoize C<calculate_all_roles>
426
8c9d74e7 427=item B<new_object>
428
02a0fb52 429We override this method to support the C<trigger> attribute option.
430
a15dff8d 431=item B<construct_instance>
432
ac2dc464 433This provides some Moose specific extensions to this method, you
434almost never call this method directly unless you really know what
435you are doing.
6ba6d68c 436
437This method makes sure to handle the moose weak-ref, type-constraint
ac2dc464 438and type coercion features.
ef1d5f4b 439
093b12c2 440=item B<get_method_map>
e9ec68d6 441
ac2dc464 442This accommodates Moose::Meta::Role::Method instances, which are
443aliased, instead of added, but still need to be counted as valid
e9ec68d6 444methods.
445
78cd1d3b 446=item B<add_override_method_modifier ($name, $method)>
447
ac2dc464 448This will create an C<override> method modifier for you, and install
02a0fb52 449it in the package.
450
78cd1d3b 451=item B<add_augment_method_modifier ($name, $method)>
452
ac2dc464 453This will create an C<augment> method modifier for you, and install
02a0fb52 454it in the package.
455
2b14ac61 456=item B<calculate_all_roles>
457
ef333f17 458=item B<roles>
459
ac2dc464 460This will return an array of C<Moose::Meta::Role> instances which are
02a0fb52 461attached to this class.
462
ef333f17 463=item B<add_role ($role)>
464
ac2dc464 465This takes an instance of C<Moose::Meta::Role> in C<$role>, and adds it
02a0fb52 466to the list of associated roles.
467
ef333f17 468=item B<does_role ($role_name)>
469
ac2dc464 470This will test if this class C<does> a given C<$role_name>. It will
471not only check it's local roles, but ask them as well in order to
02a0fb52 472cascade down the role hierarchy.
473
d79e62fd 474=item B<excludes_role ($role_name)>
475
ac2dc464 476This will test if this class C<excludes> a given C<$role_name>. It will
477not only check it's local roles, but ask them as well in order to
d79e62fd 478cascade down the role hierarchy.
479
9e93dd19 480=item B<add_attribute ($attr_name, %params|$params)>
4e848edb 481
9e93dd19 482This method does the same thing as L<Class::MOP::Class::add_attribute>, but adds
483support for taking the C<$params> as a HASH ref.
ac1ef2f9 484
c0e30cf5 485=back
486
487=head1 BUGS
488
ac2dc464 489All complex software has bugs lurking in it, and this module is no
c0e30cf5 490exception. If you find a bug please either email me, or add the bug
491to cpan-RT.
492
c0e30cf5 493=head1 AUTHOR
494
495Stevan Little E<lt>stevan@iinteractive.comE<gt>
496
497=head1 COPYRIGHT AND LICENSE
498
778db3ac 499Copyright 2006-2008 by Infinity Interactive, Inc.
c0e30cf5 500
501L<http://www.iinteractive.com>
502
503This library is free software; you can redistribute it and/or modify
ac2dc464 504it under the same terms as Perl itself.
c0e30cf5 505
8a7a9c53 506=cut
1a563243 507