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