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