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