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