doc updates to use the ArrayRef[Type] syntax
[gitmo/Moose.git] / lib / Moose / Meta / Attribute.pm
CommitLineData
c0e30cf5 1
2package Moose::Meta::Attribute;
3
4use strict;
5use warnings;
6
78cd1d3b 7use Scalar::Util 'blessed', 'weaken', 'reftype';
a15dff8d 8use Carp 'confess';
a909a4df 9use overload ();
a15dff8d 10
18ea2bcc 11our $VERSION = '0.12';
d44714be 12our $AUTHORITY = 'cpan:STEVAN';
78cd1d3b 13
8ee73eeb 14use Moose::Meta::Method::Accessor;
a3c7e2fe 15use Moose::Util::TypeConstraints ();
bc1e29b5 16
c0e30cf5 17use base 'Class::MOP::Attribute';
18
452bac1b 19# options which are not directly used
20# but we store them for metadata purposes
98aae381 21__PACKAGE__->meta->add_attribute('isa' => (reader => '_isa_metadata'));
22__PACKAGE__->meta->add_attribute('does' => (reader => '_does_metadata'));
23__PACKAGE__->meta->add_attribute('is' => (reader => '_is_metadata'));
452bac1b 24
25# these are actual options for the attrs
1a563243 26__PACKAGE__->meta->add_attribute('required' => (reader => 'is_required' ));
27__PACKAGE__->meta->add_attribute('lazy' => (reader => 'is_lazy' ));
28__PACKAGE__->meta->add_attribute('coerce' => (reader => 'should_coerce' ));
29__PACKAGE__->meta->add_attribute('weak_ref' => (reader => 'is_weak_ref' ));
30__PACKAGE__->meta->add_attribute('auto_deref' => (reader => 'should_auto_deref'));
82168dbb 31__PACKAGE__->meta->add_attribute('type_constraint' => (
32 reader => 'type_constraint',
33 predicate => 'has_type_constraint',
34));
8c9d74e7 35__PACKAGE__->meta->add_attribute('trigger' => (
36 reader => 'trigger',
37 predicate => 'has_trigger',
38));
452bac1b 39__PACKAGE__->meta->add_attribute('handles' => (
40 reader => 'handles',
41 predicate => 'has_handles',
42));
ddbdc0cb 43__PACKAGE__->meta->add_attribute('documentation' => (
44 reader => 'documentation',
45 predicate => 'has_documentation',
46));
82168dbb 47
78cd1d3b 48sub new {
49 my ($class, $name, %options) = @_;
1d768fb1 50 $class->_process_options($name, \%options);
98aae381 51 return $class->SUPER::new($name, %options);
1d768fb1 52}
53
ce0e8d63 54sub clone_and_inherit_options {
55 my ($self, %options) = @_;
83cc9094 56 # you can change default, required, coerce, documentation and lazy
ce0e8d63 57 my %actual_options;
83cc9094 58 foreach my $legal_option (qw(default coerce required documentation lazy)) {
ce0e8d63 59 if (exists $options{$legal_option}) {
60 $actual_options{$legal_option} = $options{$legal_option};
61 delete $options{$legal_option};
62 }
63 }
83cc9094 64
65 # handles can only be added, not changed
66 if ($options{handles}) {
67 confess "You can only add the 'handles' option, you cannot change it"
68 if $self->has_handles;
69 $actual_options{handles} = $options{handles};
70 delete $options{handles};
71 }
72
fcb7afc2 73 # isa can be changed, but only if the
74 # new type is a subtype
ce0e8d63 75 if ($options{isa}) {
76 my $type_constraint;
77 if (blessed($options{isa}) && $options{isa}->isa('Moose::Meta::TypeConstraint')) {
78 $type_constraint = $options{isa};
79 }
80 else {
81 $type_constraint = Moose::Util::TypeConstraints::find_type_constraint($options{isa});
82 (defined $type_constraint)
83 || confess "Could not find the type constraint '" . $options{isa} . "'";
84 }
2a0f3bd3 85 # NOTE:
86 # check here to see if the new type
87 # is a subtype of the old one
ce0e8d63 88 ($type_constraint->is_subtype_of($self->type_constraint->name))
89 || confess "New type constraint setting must be a subtype of inherited one"
2a0f3bd3 90 # iff we have a type constraint that is ...
ce0e8d63 91 if $self->has_type_constraint;
2a0f3bd3 92 # then we use it :)
ce0e8d63 93 $actual_options{type_constraint} = $type_constraint;
94 delete $options{isa};
95 }
96 (scalar keys %options == 0)
97 || confess "Illegal inherited options => (" . (join ', ' => keys %options) . ")";
98 $self->clone(%actual_options);
1d768fb1 99}
100
101sub _process_options {
102 my ($class, $name, $options) = @_;
452bac1b 103
1d768fb1 104 if (exists $options->{is}) {
105 if ($options->{is} eq 'ro') {
df492bba 106 $options->{reader} ||= $name;
1d768fb1 107 (!exists $options->{trigger})
8c9d74e7 108 || confess "Cannot have a trigger on a read-only attribute";
78cd1d3b 109 }
1d768fb1 110 elsif ($options->{is} eq 'rw') {
452bac1b 111 $options->{accessor} = $name;
98aae381 112 ((reftype($options->{trigger}) || '') eq 'CODE')
113 || confess "Trigger must be a CODE ref"
114 if exists $options->{trigger};
452bac1b 115 }
116 else {
117 confess "I do not understand this option (is => " . $options->{is} . ")"
78cd1d3b 118 }
119 }
120
1d768fb1 121 if (exists $options->{isa}) {
02a0fb52 122
1d768fb1 123 if (exists $options->{does}) {
124 if (eval { $options->{isa}->can('does') }) {
125 ($options->{isa}->does($options->{does}))
02a0fb52 126 || confess "Cannot have an isa option and a does option if the isa does not do the does";
127 }
7eaef7ad 128 else {
129 confess "Cannot have an isa option which cannot ->does()";
130 }
02a0fb52 131 }
132
78cd1d3b 133 # allow for anon-subtypes here ...
1d768fb1 134 if (blessed($options->{isa}) && $options->{isa}->isa('Moose::Meta::TypeConstraint')) {
135 $options->{type_constraint} = $options->{isa};
78cd1d3b 136 }
137 else {
d9b40005 138 $options->{type_constraint} = Moose::Util::TypeConstraints::find_or_create_type_constraint(
139 $options->{isa},
140 {
141 parent => Moose::Util::TypeConstraints::find_type_constraint('Object'),
142 constraint => sub { $_[0]->isa($options->{isa}) }
143 }
144 );
78cd1d3b 145 }
146 }
1d768fb1 147 elsif (exists $options->{does}) {
02a0fb52 148 # allow for anon-subtypes here ...
1d768fb1 149 if (blessed($options->{does}) && $options->{does}->isa('Moose::Meta::TypeConstraint')) {
150 $options->{type_constraint} = $options->{isa};
02a0fb52 151 }
152 else {
d9b40005 153 $options->{type_constraint} = Moose::Util::TypeConstraints::find_or_create_type_constraint(
154 $options->{does},
155 {
156 parent => Moose::Util::TypeConstraints::find_type_constraint('Role'),
157 constraint => sub { $_[0]->does($options->{does}) }
158 }
159 );
02a0fb52 160 }
161 }
78cd1d3b 162
1d768fb1 163 if (exists $options->{coerce} && $options->{coerce}) {
164 (exists $options->{type_constraint})
3ec7b7a3 165 || confess "You cannot have coercion without specifying a type constraint";
4b598ea3 166 confess "You cannot have a weak reference to a coerced value"
1d768fb1 167 if $options->{weak_ref};
ca01a97b 168 }
78cd1d3b 169
536f0b17 170 if (exists $options->{auto_deref} && $options->{auto_deref}) {
171 (exists $options->{type_constraint})
172 || confess "You cannot auto-dereference without specifying a type constraint";
94b8bbb8 173 ($options->{type_constraint}->is_a_type_of('ArrayRef') ||
174 $options->{type_constraint}->is_a_type_of('HashRef'))
536f0b17 175 || confess "You cannot auto-dereference anything other than a ArrayRef or HashRef";
176 }
177
1d768fb1 178 if (exists $options->{lazy} && $options->{lazy}) {
179 (exists $options->{default})
ca01a97b 180 || confess "You cannot have lazy attribute without specifying a default value for it";
1d768fb1 181 }
78cd1d3b 182}
c0e30cf5 183
d500266f 184sub initialize_instance_slot {
ddd0ec20 185 my ($self, $meta_instance, $instance, $params) = @_;
d500266f 186 my $init_arg = $self->init_arg();
187 # try to fetch the init arg from the %params ...
ddd0ec20 188
d500266f 189 my $val;
190 if (exists $params->{$init_arg}) {
191 $val = $params->{$init_arg};
ab859145 192
193 if (!defined $val && $self->is_required) {
194 confess "Attribute (" . $self->name . ") is required and cannot be undef";
195 }
d500266f 196 }
197 else {
198 # skip it if it's lazy
199 return if $self->is_lazy;
200 # and die if it's required and doesn't have a default value
201 confess "Attribute (" . $self->name . ") is required"
202 if $self->is_required && !$self->has_default;
203 }
ddd0ec20 204
d500266f 205 # if nothing was in the %params, we can use the
206 # attribute's default value (if it has one)
207 if (!defined $val && $self->has_default) {
208 $val = $self->default($instance);
ab859145 209 }
d7611a4a 210
7a5ebc40 211 if (defined $val || $self->has_default) {
d500266f 212 if ($self->has_type_constraint) {
c07af9d2 213 my $type_constraint = $self->type_constraint;
214 if ($self->should_coerce && $type_constraint->has_coercion) {
0a5bd159 215 $val = $type_constraint->coerce($val);
d500266f 216 }
c07af9d2 217 (defined($type_constraint->check($val)))
218 || confess "Attribute (" .
219 $self->name .
8449e6e7 220 ") does not pass the type constraint (" .
c07af9d2 221 $type_constraint->name .
a909a4df 222 ") with '" .
223 (defined $val
8ac5969a 224 ? (blessed($val) && overload::Overloaded($val)
a909a4df 225 ? overload::StrVal($val)
226 : $val)
227 : 'undef') .
228 "'";
d500266f 229 }
230 }
ddd0ec20 231
ac1ef2f9 232 $meta_instance->set_slot_value($instance, $self->name, $val);
233 $meta_instance->weaken_slot_value($instance, $self->name)
234 if ref $val && $self->is_weak_ref;
d500266f 235}
236
d617b644 237## Slot management
9e93dd19 238
946289d1 239sub set_value {
240 my ($self, $instance, $value) = @_;
241
242 my $attr_name = $self->name;
243
244 if ($self->is_required) {
245 defined($value)
246 || confess "Attribute ($attr_name) is required, so cannot be set to undef";
247 }
248
249 if ($self->has_type_constraint) {
250
251 my $type_constraint = $self->type_constraint;
252
253 if ($self->should_coerce) {
254 $value = $type_constraint->coerce($value);
255 }
256 defined($type_constraint->_compiled_type_constraint->($value))
257 || confess "Attribute ($attr_name) does not pass the type constraint ("
a909a4df 258 . $type_constraint->name
259 . ") with "
260 . (defined($value)
8ac5969a 261 ? ("'" .
262 (blessed($value) && overload::Overloaded($value)
263 ? overload::StrVal($value)
264 : $value)
265 . "'")
a909a4df 266 : "undef")
946289d1 267 if defined($value);
268 }
269
270 my $meta_instance = Class::MOP::Class->initialize(blessed($instance))
271 ->get_meta_instance;
272
273 $meta_instance->set_slot_value($instance, $attr_name, $value);
274
275 if (ref $value && $self->is_weak_ref) {
276 $meta_instance->weaken_slot_value($instance, $attr_name);
277 }
278
279 if ($self->has_trigger) {
280 $self->trigger->($instance, $value, $self);
281 }
282}
283
284sub get_value {
285 my ($self, $instance) = @_;
286
287 if ($self->is_lazy) {
288 unless ($self->has_value($instance)) {
289 if ($self->has_default) {
290 my $default = $self->default($instance);
291 $self->set_value($instance, $default);
292 }
293 else {
294 $self->set_value($instance, undef);
295 }
296 }
297 }
298
299 if ($self->should_auto_deref) {
300
301 my $type_constraint = $self->type_constraint;
302
303 if ($type_constraint->is_a_type_of('ArrayRef')) {
304 my $rv = $self->SUPER::get_value($instance);
305 return unless defined $rv;
306 return wantarray ? @{ $rv } : $rv;
307 }
308 elsif ($type_constraint->is_a_type_of('HashRef')) {
309 my $rv = $self->SUPER::get_value($instance);
310 return unless defined $rv;
311 return wantarray ? %{ $rv } : $rv;
312 }
313 else {
314 confess "Can not auto de-reference the type constraint '" . $type_constraint->name . "'";
315 }
316
317 }
318 else {
319
320 return $self->SUPER::get_value($instance);
321 }
322}
a15dff8d 323
d617b644 324## installing accessors
c0e30cf5 325
d617b644 326sub accessor_metaclass { 'Moose::Meta::Method::Accessor' }
d7f17ebb 327
452bac1b 328sub install_accessors {
329 my $self = shift;
330 $self->SUPER::install_accessors(@_);
331
332 if ($self->has_handles) {
333
334 # NOTE:
335 # Here we canonicalize the 'handles' option
336 # this will sort out any details and always
337 # return an hash of methods which we want
338 # to delagate to, see that method for details
339 my %handles = $self->_canonicalize_handles();
340
341 # find the name of the accessor for this attribute
342 my $accessor_name = $self->reader || $self->accessor;
343 (defined $accessor_name)
344 || confess "You cannot install delegation without a reader or accessor for the attribute";
345
346 # make sure we handle HASH accessors correctly
347 ($accessor_name) = keys %{$accessor_name}
348 if ref($accessor_name) eq 'HASH';
349
350 # install the delegation ...
351 my $associated_class = $self->associated_class;
352 foreach my $handle (keys %handles) {
353 my $method_to_call = $handles{$handle};
354
355 (!$associated_class->has_method($handle))
356 || confess "You cannot overwrite a locally defined method ($handle) with a delegation";
357
d022f632 358 # NOTE:
359 # handles is not allowed to delegate
360 # any of these methods, as they will
361 # override the ones in your class, which
362 # is almost certainly not what you want.
363 next if $handle =~ /^BUILD|DEMOLISH$/ || Moose::Object->can($handle);
364
452bac1b 365 if ((reftype($method_to_call) || '') eq 'CODE') {
366 $associated_class->add_method($handle => $method_to_call);
367 }
368 else {
369 $associated_class->add_method($handle => sub {
b805c70c 370 # FIXME
371 # we should check for lack of
372 # a callable return value from
373 # the accessor here
cbe25729 374 my $proxy = (shift)->$accessor_name();
375 @_ = ($proxy, @_);
376 goto &{ $proxy->can($method_to_call)};
452bac1b 377 });
378 }
379 }
380 }
381
382 return;
383}
384
98aae381 385# private methods to help delegation ...
386
452bac1b 387sub _canonicalize_handles {
388 my $self = shift;
389 my $handles = $self->handles;
c84f324f 390 if (my $handle_type = ref($handles)) {
391 if ($handle_type eq 'HASH') {
392 return %{$handles};
393 }
394 elsif ($handle_type eq 'ARRAY') {
395 return map { $_ => $_ } @{$handles};
396 }
397 elsif ($handle_type eq 'Regexp') {
398 ($self->has_type_constraint)
399 || confess "Cannot delegate methods based on a RegExpr without a type constraint (isa)";
400 return map { ($_ => $_) }
401 grep { /$handles/ } $self->_get_delegate_method_list;
402 }
403 elsif ($handle_type eq 'CODE') {
404 return $handles->($self, $self->_find_delegate_metaclass);
405 }
406 else {
407 confess "Unable to canonicalize the 'handles' option with $handles";
408 }
452bac1b 409 }
410 else {
c84f324f 411 my $role_meta = eval { $handles->meta };
412 if ($@) {
413 confess "Unable to canonicalize the 'handles' option with $handles because : $@";
414 }
415
416 (blessed $role_meta && $role_meta->isa('Moose::Meta::Role'))
417 || confess "Unable to canonicalize the 'handles' option with $handles because ->meta is not a Moose::Meta::Role";
418
419 return map { $_ => $_ } (
420 $role_meta->get_method_list,
421 $role_meta->get_required_method_list
422 );
452bac1b 423 }
424}
425
426sub _find_delegate_metaclass {
427 my $self = shift;
98aae381 428 if (my $class = $self->_isa_metadata) {
452bac1b 429 # if the class does have
430 # a meta method, use it
431 return $class->meta if $class->can('meta');
432 # otherwise we might be
433 # dealing with a non-Moose
434 # class, and need to make
435 # our own metaclass
436 return Moose::Meta::Class->initialize($class);
437 }
98aae381 438 elsif (my $role = $self->_does_metadata) {
452bac1b 439 # our role will always have
440 # a meta method
98aae381 441 return $role->meta;
452bac1b 442 }
443 else {
444 confess "Cannot find delegate metaclass for attribute " . $self->name;
445 }
446}
447
448sub _get_delegate_method_list {
449 my $self = shift;
450 my $meta = $self->_find_delegate_metaclass;
451 if ($meta->isa('Class::MOP::Class')) {
093b12c2 452 return map { $_->{name} } # NOTE: !never! delegate &meta
453 grep { $_->{class} ne 'Moose::Object' && $_->{name} ne 'meta' }
452bac1b 454 $meta->compute_all_applicable_methods;
455 }
456 elsif ($meta->isa('Moose::Meta::Role')) {
457 return $meta->get_method_list;
458 }
459 else {
460 confess "Unable to recognize the delegate metaclass '$meta'";
461 }
462}
463
c0e30cf5 4641;
465
466__END__
467
468=pod
469
470=head1 NAME
471
6ba6d68c 472Moose::Meta::Attribute - The Moose attribute metaclass
c0e30cf5 473
474=head1 DESCRIPTION
475
e522431d 476This is a subclass of L<Class::MOP::Attribute> with Moose specific
6ba6d68c 477extensions.
478
479For the most part, the only time you will ever encounter an
480instance of this class is if you are doing some serious deep
481introspection. To really understand this class, you need to refer
482to the L<Class::MOP::Attribute> documentation.
e522431d 483
c0e30cf5 484=head1 METHODS
485
6ba6d68c 486=head2 Overridden methods
487
488These methods override methods in L<Class::MOP::Attribute> and add
489Moose specific features. You can safely assume though that they
490will behave just as L<Class::MOP::Attribute> does.
491
c0e30cf5 492=over 4
493
494=item B<new>
495
d500266f 496=item B<initialize_instance_slot>
497
452bac1b 498=item B<install_accessors>
499
39b3bc94 500=item B<accessor_metaclass>
501
946289d1 502=item B<get_value>
503
504=item B<set_value>
505
bcbaa845 506 eval { $point->meta->get_attribute('x')->set_value($point, 'fourty-two') };
507 if($@) {
508 print "Oops: $@\n";
509 }
510
511I<Attribute (x) does not pass the type constraint (Int) with 'fourty-two'>
512
513Before setting the value, a check is made on the type constraint of
514the attribute, if it has one, to see if the value passes it. If the
515value fails to pass, the set operation dies with a L<Carp/confess>.
516
517Any coercion to convert values is done before checking the type constraint.
518
519To check a value against a type constraint before setting it, fetch the
520attribute instance using L<Moose::Meta::Attribute/find_attribute_by_name>,
521fetch the type_constraint from the attribute using L<Moose::Meta::Attribute/type_constraint>
522and call L<Moose::Meta::TypeConstraint/check>. See L<Moose::Cookbook::RecipeX>
523for an example.
524
a15dff8d 525=back
526
6ba6d68c 527=head2 Additional Moose features
528
8449e6e7 529Moose attributes support type-constraint checking, weak reference
6ba6d68c 530creation and type coercion.
531
a15dff8d 532=over 4
533
9e93dd19 534=item B<clone_and_inherit_options>
535
536This is to support the C<has '+foo'> feature, it clones an attribute
537from a superclass and allows a very specific set of changes to be made
538to the attribute.
539
a15dff8d 540=item B<has_type_constraint>
541
6ba6d68c 542Returns true if this meta-attribute has a type constraint.
543
a15dff8d 544=item B<type_constraint>
545
6ba6d68c 546A read-only accessor for this meta-attribute's type constraint. For
547more information on what you can do with this, see the documentation
548for L<Moose::Meta::TypeConstraint>.
a15dff8d 549
452bac1b 550=item B<has_handles>
551
552Returns true if this meta-attribute performs delegation.
553
554=item B<handles>
555
556This returns the value which was passed into the handles option.
557
6ba6d68c 558=item B<is_weak_ref>
a15dff8d 559
02a0fb52 560Returns true if this meta-attribute produces a weak reference.
4b598ea3 561
ca01a97b 562=item B<is_required>
563
02a0fb52 564Returns true if this meta-attribute is required to have a value.
ca01a97b 565
566=item B<is_lazy>
567
02a0fb52 568Returns true if this meta-attribute should be initialized lazily.
ca01a97b 569
570NOTE: lazy attributes, B<must> have a C<default> field set.
571
34a66aa3 572=item B<should_coerce>
4b598ea3 573
02a0fb52 574Returns true if this meta-attribute should perform type coercion.
6ba6d68c 575
536f0b17 576=item B<should_auto_deref>
577
578Returns true if this meta-attribute should perform automatic
579auto-dereferencing.
580
581NOTE: This can only be done for attributes whose type constraint is
582either I<ArrayRef> or I<HashRef>.
583
8c9d74e7 584=item B<has_trigger>
585
02a0fb52 586Returns true if this meta-attribute has a trigger set.
587
8c9d74e7 588=item B<trigger>
589
02a0fb52 590This is a CODE reference which will be executed every time the
591value of an attribute is assigned. The CODE ref will get two values,
592the invocant and the new value. This can be used to handle I<basic>
593bi-directional relations.
594
ddbdc0cb 595=item B<documentation>
596
597This is a string which contains the documentation for this attribute.
598It serves no direct purpose right now, but it might in the future
599in some kind of automated documentation system perhaps.
600
601=item B<has_documentation>
602
603Returns true if this meta-attribute has any documentation.
604
c0e30cf5 605=back
606
607=head1 BUGS
608
609All complex software has bugs lurking in it, and this module is no
610exception. If you find a bug please either email me, or add the bug
611to cpan-RT.
612
c0e30cf5 613=head1 AUTHOR
614
615Stevan Little E<lt>stevan@iinteractive.comE<gt>
616
98aae381 617Yuval Kogman E<lt>nothingmuch@woobling.comE<gt>
618
c0e30cf5 619=head1 COPYRIGHT AND LICENSE
620
b77fdbed 621Copyright 2006, 2007 by Infinity Interactive, Inc.
c0e30cf5 622
623L<http://www.iinteractive.com>
624
625This library is free software; you can redistribute it and/or modify
626it under the same terms as Perl itself.
627
8a7a9c53 628=cut