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