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