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