bah
[gitmo/Moose.git] / lib / Moose / Meta / Attribute.pm
CommitLineData
c0e30cf5 1
2package Moose::Meta::Attribute;
3
4use strict;
5use warnings;
6
21f1e231 7use Scalar::Util 'blessed', 'weaken';
a909a4df 8use overload ();
a15dff8d 9
eaa35e6e 10our $VERSION = '0.50';
d44714be 11our $AUTHORITY = 'cpan:STEVAN';
78cd1d3b 12
8ee73eeb 13use Moose::Meta::Method::Accessor;
d5c30e52 14use Moose::Util ();
a3c7e2fe 15use Moose::Util::TypeConstraints ();
bc1e29b5 16
c0e30cf5 17use base 'Class::MOP::Attribute';
18
452bac1b 19# options which are not directly used
20# but we store them for metadata purposes
98aae381 21__PACKAGE__->meta->add_attribute('isa' => (reader => '_isa_metadata'));
22__PACKAGE__->meta->add_attribute('does' => (reader => '_does_metadata'));
23__PACKAGE__->meta->add_attribute('is' => (reader => '_is_metadata'));
452bac1b 24
25# these are actual options for the attrs
1a563243 26__PACKAGE__->meta->add_attribute('required' => (reader => 'is_required' ));
27__PACKAGE__->meta->add_attribute('lazy' => (reader => 'is_lazy' ));
26fbace8 28__PACKAGE__->meta->add_attribute('lazy_build' => (reader => 'is_lazy_build' ));
1a563243 29__PACKAGE__->meta->add_attribute('coerce' => (reader => 'should_coerce' ));
30__PACKAGE__->meta->add_attribute('weak_ref' => (reader => 'is_weak_ref' ));
31__PACKAGE__->meta->add_attribute('auto_deref' => (reader => 'should_auto_deref'));
82168dbb 32__PACKAGE__->meta->add_attribute('type_constraint' => (
33 reader => 'type_constraint',
34 predicate => 'has_type_constraint',
35));
8c9d74e7 36__PACKAGE__->meta->add_attribute('trigger' => (
37 reader => 'trigger',
38 predicate => 'has_trigger',
39));
452bac1b 40__PACKAGE__->meta->add_attribute('handles' => (
41 reader => 'handles',
42 predicate => 'has_handles',
43));
ddbdc0cb 44__PACKAGE__->meta->add_attribute('documentation' => (
45 reader => 'documentation',
46 predicate => 'has_documentation',
47));
82a5b1a7 48__PACKAGE__->meta->add_attribute('traits' => (
49 reader => 'applied_traits',
50 predicate => 'has_applied_traits',
51));
82168dbb 52
587e457d 53# we need to have a ->does method in here to
54# more easily support traits, and the introspection
0db4f1d7 55# of those traits. We extend the does check to look
56# for metatrait aliases.
57sub does {
58 my ($self, $role_name) = @_;
59 my $name = eval {
60 Moose::Util::resolve_metatrait_alias(Attribute => $role_name)
61 };
62 return 0 if !defined($name); # failed to load class
63 return Moose::Object::does($self, $name);
64}
587e457d 65
be05faea 66sub throw_error {
67 my $self = shift;
68 my $class = ( ref $self && $self->associated_class ) || "Moose::Meta::Class";
69 unshift @_, "message" if @_ % 2 == 1;
70 unshift @_, attr => $self if ref $self;
71 unshift @_, $class;
72 goto $class->can("throw_error"); # to avoid incrementing depth by 1
73}
74
78cd1d3b 75sub new {
f3c4e20e 76 my ($class, $name, %options) = @_;
c32c2c61 77 $class->_process_options($name, \%options) unless $options{__hack_no_process_options}; # used from clone()... YECHKKK FIXME ICKY YUCK GROSS
f3c4e20e 78 return $class->SUPER::new($name, %options);
1d768fb1 79}
80
d5c30e52 81sub interpolate_class_and_new {
82 my ($class, $name, @args) = @_;
83
c32c2c61 84 my ( $new_class, @traits ) = $class->interpolate_class(@args);
85
86 $new_class->new($name, @args, ( scalar(@traits) ? ( traits => \@traits ) : () ) );
d5c30e52 87}
88
89sub interpolate_class {
90 my ($class, %options) = @_;
91
c32c2c61 92 $class = ref($class) || $class;
93
94 if ( my $metaclass_name = delete $options{metaclass} ) {
95 my $new_class = Moose::Util::resolve_metaclass_alias( Attribute => $metaclass_name );
96
97 if ( $class ne $new_class ) {
98 if ( $new_class->can("interpolate_class") ) {
99 return $new_class->interpolate_class(%options);
100 } else {
101 $class = $new_class;
102 }
103 }
d5c30e52 104 }
105
c32c2c61 106 my @traits;
107
d5c30e52 108 if (my $traits = $options{traits}) {
c32c2c61 109 if ( @traits = grep { not $class->does($_) } map {
d5c30e52 110 Moose::Util::resolve_metatrait_alias( Attribute => $_ )
111 or
112 $_
c32c2c61 113 } @$traits ) {
114 my $anon_class = Moose::Meta::Class->create_anon_class(
115 superclasses => [ $class ],
116 roles => [ @traits ],
117 cache => 1,
118 );
119
120 $class = $anon_class->name;
121 }
d5c30e52 122 }
c32c2c61 123
124 return ( wantarray ? ( $class, @traits ) : $class );
d5c30e52 125}
126
ce0e8d63 127sub clone_and_inherit_options {
128 my ($self, %options) = @_;
c32c2c61 129 my %copy = %options;
130 # you can change default, required, coerce, documentation, lazy, handles, builder, type_constraint (explicitly or using isa/does), metaclass and traits
ce0e8d63 131 my %actual_options;
c32c2c61 132 foreach my $legal_option (qw(default coerce required documentation lazy handles builder type_constraint)) {
ce0e8d63 133 if (exists $options{$legal_option}) {
134 $actual_options{$legal_option} = $options{$legal_option};
135 delete $options{$legal_option};
136 }
137 }
26fbace8 138
ce0e8d63 139 if ($options{isa}) {
140 my $type_constraint;
8de73ff1 141 if (blessed($options{isa}) && $options{isa}->isa('Moose::Meta::TypeConstraint')) {
142 $type_constraint = $options{isa};
143 }
144 else {
d40ce9d5 145 $type_constraint = Moose::Util::TypeConstraints::find_or_create_isa_type_constraint($options{isa});
8de73ff1 146 (defined $type_constraint)
be05faea 147 || $self->throw_error("Could not find the type constraint '" . $options{isa} . "'", data => $options{isa});
8de73ff1 148 }
5e98d2b6 149
8de73ff1 150 $actual_options{type_constraint} = $type_constraint;
ce0e8d63 151 delete $options{isa};
152 }
2ea379cb 153
154 if ($options{does}) {
155 my $type_constraint;
156 if (blessed($options{does}) && $options{does}->isa('Moose::Meta::TypeConstraint')) {
157 $type_constraint = $options{does};
158 }
159 else {
d40ce9d5 160 $type_constraint = Moose::Util::TypeConstraints::find_or_create_does_type_constraint($options{does});
2ea379cb 161 (defined $type_constraint)
be05faea 162 || $self->throw_error("Could not find the type constraint '" . $options{does} . "'", data => $options{does});
2ea379cb 163 }
164
165 $actual_options{type_constraint} = $type_constraint;
166 delete $options{does};
167 }
c32c2c61 168
cbd141ca 169 # NOTE:
170 # this doesn't apply to Class::MOP::Attributes,
171 # so we can ignore it for them.
172 # - SL
173 if ($self->can('interpolate_class')) {
174 ( $actual_options{metaclass}, my @traits ) = $self->interpolate_class(%options);
c32c2c61 175
cbd141ca 176 my %seen;
177 my @all_traits = grep { $seen{$_}++ } @{ $self->applied_traits || [] }, @traits;
178 $actual_options{traits} = \@all_traits if @all_traits;
c32c2c61 179
cbd141ca 180 delete @options{qw(metaclass traits)};
181 }
c32c2c61 182
26fbace8 183 (scalar keys %options == 0)
be05faea 184 || $self->throw_error("Illegal inherited options => (" . (join ', ' => keys %options) . ")", data => \%options);
c32c2c61 185
186
ce0e8d63 187 $self->clone(%actual_options);
1d768fb1 188}
189
c32c2c61 190sub clone {
191 my ( $self, %params ) = @_;
192
193 my $class = $params{metaclass} || ref $self;
194
195 if ( 0 and $class eq ref $self ) {
196 return $self->SUPER::clone(%params);
197 } else {
198 my ( @init, @non_init );
199
200 foreach my $attr ( grep { $_->has_value($self) } $self->meta->compute_all_applicable_attributes ) {
201 push @{ $attr->has_init_arg ? \@init : \@non_init }, $attr;
202 }
203
204 my %new_params = ( ( map { $_->init_arg => $_->get_value($self) } @init ), %params );
205
206 my $name = delete $new_params{name};
207
208 my $clone = $class->new($name, %new_params, __hack_no_process_options => 1 );
209
210 foreach my $attr ( @non_init ) {
211 $attr->set_value($clone, $attr->get_value($self));
212 }
213
214
215 return $clone;
216 }
217}
218
1d768fb1 219sub _process_options {
220 my ($class, $name, $options) = @_;
8de73ff1 221
f3c4e20e 222 if (exists $options->{is}) {
21f1e231 223
012fcbd1 224 ### -------------------------
225 ## is => ro, writer => _foo # turns into (reader => foo, writer => _foo) as before
226 ## is => rw, writer => _foo # turns into (reader => foo, writer => _foo)
227 ## is => rw, accessor => _foo # turns into (accessor => _foo)
228 ## is => ro, accessor => _foo # error, accesor is rw
229 ### -------------------------
21f1e231 230
8de73ff1 231 if ($options->{is} eq 'ro') {
be05faea 232 $class->throw_error("Cannot define an accessor name on a read-only attribute, accessors are read/write", data => $options)
21f1e231 233 if exists $options->{accessor};
8de73ff1 234 $options->{reader} ||= $name;
8de73ff1 235 }
236 elsif ($options->{is} eq 'rw') {
21f1e231 237 if ($options->{writer}) {
238 $options->{reader} ||= $name;
239 }
240 else {
241 $options->{accessor} ||= $name;
242 }
8de73ff1 243 }
244 else {
be05faea 245 $class->throw_error("I do not understand this option (is => " . $options->{is} . ") on attribute $name", data => $options->{is});
8de73ff1 246 }
f3c4e20e 247 }
8de73ff1 248
f3c4e20e 249 if (exists $options->{isa}) {
f3c4e20e 250 if (exists $options->{does}) {
251 if (eval { $options->{isa}->can('does') }) {
252 ($options->{isa}->does($options->{does}))
be05faea 253 || $class->throw_error("Cannot have an isa option and a does option if the isa does not do the does on attribute $name", data => $options);
f3c4e20e 254 }
255 else {
be05faea 256 $class->throw_error("Cannot have an isa option which cannot ->does() on attribute $name", data => $options);
26fbace8 257 }
26fbace8 258 }
8de73ff1 259
f3c4e20e 260 # allow for anon-subtypes here ...
261 if (blessed($options->{isa}) && $options->{isa}->isa('Moose::Meta::TypeConstraint')) {
8de73ff1 262 $options->{type_constraint} = $options->{isa};
263 }
264 else {
620db045 265 $options->{type_constraint} = Moose::Util::TypeConstraints::find_or_create_isa_type_constraint($options->{isa});
8de73ff1 266 }
f3c4e20e 267 }
268 elsif (exists $options->{does}) {
269 # allow for anon-subtypes here ...
270 if (blessed($options->{does}) && $options->{does}->isa('Moose::Meta::TypeConstraint')) {
238b424d 271 $options->{type_constraint} = $options->{does};
8de73ff1 272 }
273 else {
620db045 274 $options->{type_constraint} = Moose::Util::TypeConstraints::find_or_create_does_type_constraint($options->{does});
8de73ff1 275 }
f3c4e20e 276 }
8de73ff1 277
f3c4e20e 278 if (exists $options->{coerce} && $options->{coerce}) {
279 (exists $options->{type_constraint})
be05faea 280 || $class->throw_error("You cannot have coercion without specifying a type constraint on attribute $name", data => $options);
281 $class->throw_error("You cannot have a weak reference to a coerced value on attribute $name", data => $options)
8de73ff1 282 if $options->{weak_ref};
f3c4e20e 283 }
8de73ff1 284
0b7df53c 285 if (exists $options->{trigger}) {
21f1e231 286 ('CODE' eq ref $options->{trigger})
be05faea 287 || $class->throw_error("Trigger must be a CODE ref", data => $options->{trigger});
0b7df53c 288 }
289
f3c4e20e 290 if (exists $options->{auto_deref} && $options->{auto_deref}) {
291 (exists $options->{type_constraint})
be05faea 292 || $class->throw_error("You cannot auto-dereference without specifying a type constraint on attribute $name", data => $options);
f3c4e20e 293 ($options->{type_constraint}->is_a_type_of('ArrayRef') ||
8de73ff1 294 $options->{type_constraint}->is_a_type_of('HashRef'))
be05faea 295 || $class->throw_error("You cannot auto-dereference anything other than a ArrayRef or HashRef on attribute $name", data => $options);
f3c4e20e 296 }
8de73ff1 297
f3c4e20e 298 if (exists $options->{lazy_build} && $options->{lazy_build} == 1) {
be05faea 299 $class->throw_error("You can not use lazy_build and default for the same attribute $name", data => $options)
8de73ff1 300 if exists $options->{default};
a6c84c69 301 $options->{lazy} = 1;
302 $options->{required} = 1;
303 $options->{builder} ||= "_build_${name}";
304 if ($name =~ /^_/) {
f3c4e20e 305 $options->{clearer} ||= "_clear${name}";
306 $options->{predicate} ||= "_has${name}";
a6c84c69 307 }
308 else {
f3c4e20e 309 $options->{clearer} ||= "clear_${name}";
310 $options->{predicate} ||= "has_${name}";
26fbace8 311 }
f3c4e20e 312 }
8de73ff1 313
f3c4e20e 314 if (exists $options->{lazy} && $options->{lazy}) {
9edba990 315 (exists $options->{default} || defined $options->{builder} )
be05faea 316 || $class->throw_error("You cannot have lazy attribute ($name) without specifying a default value for it", data => $options);
f3c4e20e 317 }
26fbace8 318
9edba990 319 if ( $options->{required} && !( ( !exists $options->{init_arg} || defined $options->{init_arg} ) || exists $options->{default} || defined $options->{builder} ) ) {
be05faea 320 $class->throw_error("You cannot have a required attribute ($name) without a default, builder, or an init_arg", data => $options);
9edba990 321 }
322
78cd1d3b 323}
c0e30cf5 324
d500266f 325sub initialize_instance_slot {
ddd0ec20 326 my ($self, $meta_instance, $instance, $params) = @_;
d500266f 327 my $init_arg = $self->init_arg();
328 # try to fetch the init arg from the %params ...
ddd0ec20 329
26fbace8 330 my $val;
1ed0b94f 331 my $value_is_set;
625d571f 332 if ( defined($init_arg) and exists $params->{$init_arg}) {
d500266f 333 $val = $params->{$init_arg};
2c78d811 334 $value_is_set = 1;
d500266f 335 }
336 else {
337 # skip it if it's lazy
338 return if $self->is_lazy;
339 # and die if it's required and doesn't have a default value
be05faea 340 $self->throw_error("Attribute (" . $self->name . ") is required", object => $instance, data => $params)
26fbace8 341 if $self->is_required && !$self->has_default && !$self->has_builder;
ddd0ec20 342
1ed0b94f 343 # if nothing was in the %params, we can use the
344 # attribute's default value (if it has one)
345 if ($self->has_default) {
346 $val = $self->default($instance);
347 $value_is_set = 1;
a6c84c69 348 }
349 elsif ($self->has_builder) {
350 if (my $builder = $instance->can($self->builder)){
1ed0b94f 351 $val = $instance->$builder;
352 $value_is_set = 1;
a6c84c69 353 }
354 else {
be05faea 355 $self->throw_error(blessed($instance)." does not support builder method '".$self->builder."' for attribute '" . $self->name . "'", object => $instance, data => $params);
1ed0b94f 356 }
a0748c37 357 }
26fbace8 358 }
359
1ed0b94f 360 return unless $value_is_set;
361
362 if ($self->has_type_constraint) {
363 my $type_constraint = $self->type_constraint;
364 if ($self->should_coerce && $type_constraint->has_coercion) {
365 $val = $type_constraint->coerce($val);
d500266f 366 }
ab76842e 367 $type_constraint->check($val)
be05faea 368 || $self->throw_error("Attribute ("
688fcdda 369 . $self->name
370 . ") does not pass the type constraint because: "
be05faea 371 . $type_constraint->get_message($val), data => $val, object => $instance);
1ed0b94f 372 }
ddd0ec20 373
759e4e8f 374 $self->set_initial_value($instance, $val);
26fbace8 375 $meta_instance->weaken_slot_value($instance, $self->name)
a6c84c69 376 if ref $val && $self->is_weak_ref;
d500266f 377}
378
d617b644 379## Slot management
9e93dd19 380
8abe9636 381# FIXME:
382# this duplicates too much code from
383# Class::MOP::Attribute, we need to
384# refactor these bits eventually.
385# - SL
386sub _set_initial_slot_value {
387 my ($self, $meta_instance, $instance, $value) = @_;
388
389 my $slot_name = $self->name;
390
391 return $meta_instance->set_slot_value($instance, $slot_name, $value)
392 unless $self->has_initializer;
393
394 my ($type_constraint, $can_coerce);
395 if ($self->has_type_constraint) {
396 $type_constraint = $self->type_constraint;
397 $can_coerce = ($self->should_coerce && $type_constraint->has_coercion);
398 }
399
400 my $callback = sub {
401 my $val = shift;
402 if ($type_constraint) {
403 $val = $type_constraint->coerce($val)
404 if $can_coerce;
405 $type_constraint->check($val)
be05faea 406 || $self->throw_error("Attribute ("
8abe9636 407 . $slot_name
408 . ") does not pass the type constraint because: "
be05faea 409 . $type_constraint->get_message($val), data => $val, object => $instance);
8abe9636 410 }
411 $meta_instance->set_slot_value($instance, $slot_name, $val);
412 };
413
414 my $initializer = $self->initializer;
415
416 # most things will just want to set a value, so make it first arg
417 $instance->$initializer($value, $callback, $self);
418}
419
946289d1 420sub set_value {
b6af66f8 421 my ($self, $instance, @args) = @_;
422 my $value = $args[0];
26fbace8 423
946289d1 424 my $attr_name = $self->name;
26fbace8 425
b6af66f8 426 if ($self->is_required and not @args) {
be05faea 427 $self->throw_error("Attribute ($attr_name) is required", object => $instance);
946289d1 428 }
26fbace8 429
946289d1 430 if ($self->has_type_constraint) {
26fbace8 431
946289d1 432 my $type_constraint = $self->type_constraint;
26fbace8 433
946289d1 434 if ($self->should_coerce) {
26fbace8 435 $value = $type_constraint->coerce($value);
688fcdda 436 }
42bc21a4 437 $type_constraint->_compiled_type_constraint->($value)
be05faea 438 || $self->throw_error("Attribute ("
688fcdda 439 . $self->name
440 . ") does not pass the type constraint because "
be05faea 441 . $type_constraint->get_message($value), object => $instance, data => $value);
946289d1 442 }
26fbace8 443
946289d1 444 my $meta_instance = Class::MOP::Class->initialize(blessed($instance))
445 ->get_meta_instance;
26fbace8 446
447 $meta_instance->set_slot_value($instance, $attr_name, $value);
448
946289d1 449 if (ref $value && $self->is_weak_ref) {
26fbace8 450 $meta_instance->weaken_slot_value($instance, $attr_name);
946289d1 451 }
26fbace8 452
946289d1 453 if ($self->has_trigger) {
454 $self->trigger->($instance, $value, $self);
455 }
456}
457
458sub get_value {
459 my ($self, $instance) = @_;
26fbace8 460
946289d1 461 if ($self->is_lazy) {
8de73ff1 462 unless ($self->has_value($instance)) {
463 if ($self->has_default) {
464 my $default = $self->default($instance);
759e4e8f 465 $self->set_initial_value($instance, $default);
3f11800d 466 } elsif ( $self->has_builder ) {
a6c84c69 467 if (my $builder = $instance->can($self->builder)){
759e4e8f 468 $self->set_initial_value($instance, $instance->$builder);
3f11800d 469 }
a6c84c69 470 else {
be05faea 471 $self->throw_error(blessed($instance)
a6c84c69 472 . " does not support builder method '"
473 . $self->builder
474 . "' for attribute '"
475 . $self->name
be05faea 476 . "'", object => $instance);
26fbace8 477 }
a6c84c69 478 }
479 else {
759e4e8f 480 $self->set_initial_value($instance, undef);
26fbace8 481 }
8de73ff1 482 }
946289d1 483 }
26fbace8 484
946289d1 485 if ($self->should_auto_deref) {
26fbace8 486
946289d1 487 my $type_constraint = $self->type_constraint;
488
489 if ($type_constraint->is_a_type_of('ArrayRef')) {
490 my $rv = $self->SUPER::get_value($instance);
491 return unless defined $rv;
492 return wantarray ? @{ $rv } : $rv;
26fbace8 493 }
946289d1 494 elsif ($type_constraint->is_a_type_of('HashRef')) {
495 my $rv = $self->SUPER::get_value($instance);
496 return unless defined $rv;
497 return wantarray ? %{ $rv } : $rv;
26fbace8 498 }
946289d1 499 else {
be05faea 500 $self->throw_error("Can not auto de-reference the type constraint '" . $type_constraint->name . "'", object => $instance, data => $type_constraint);
946289d1 501 }
26fbace8 502
946289d1 503 }
504 else {
26fbace8 505
946289d1 506 return $self->SUPER::get_value($instance);
26fbace8 507 }
946289d1 508}
a15dff8d 509
26fbace8 510## installing accessors
c0e30cf5 511
d617b644 512sub accessor_metaclass { 'Moose::Meta::Method::Accessor' }
d7f17ebb 513
452bac1b 514sub install_accessors {
515 my $self = shift;
26fbace8 516 $self->SUPER::install_accessors(@_);
517
452bac1b 518 if ($self->has_handles) {
26fbace8 519
452bac1b 520 # NOTE:
521 # Here we canonicalize the 'handles' option
26fbace8 522 # this will sort out any details and always
523 # return an hash of methods which we want
452bac1b 524 # to delagate to, see that method for details
525 my %handles = $self->_canonicalize_handles();
26fbace8 526
f3c4e20e 527 # find the accessor method for this attribute
528 my $accessor = $self->get_read_method_ref;
529 # then unpack it if we need too ...
530 $accessor = $accessor->body if blessed $accessor;
26fbace8 531
452bac1b 532 # install the delegation ...
533 my $associated_class = $self->associated_class;
534 foreach my $handle (keys %handles) {
535 my $method_to_call = $handles{$handle};
7dbbdcab 536 my $class_name = $associated_class->name;
537 my $name = "${class_name}::${handle}";
26fbace8 538
452bac1b 539 (!$associated_class->has_method($handle))
cee532a1 540 || $self->throw_error("You cannot overwrite a locally defined method ($handle) with a delegation", method_name => $handle);
26fbace8 541
d022f632 542 # NOTE:
543 # handles is not allowed to delegate
26fbace8 544 # any of these methods, as they will
545 # override the ones in your class, which
d022f632 546 # is almost certainly not what you want.
4fe78472 547
548 # FIXME warn when $handle was explicitly specified, but not if the source is a regex or something
549 #cluck("Not delegating method '$handle' because it is a core method") and
550 next if $class_name->isa("Moose::Object") and $handle =~ /^BUILD|DEMOLISH$/ || Moose::Object->can($handle);
26fbace8 551
21f1e231 552 if ('CODE' eq ref($method_to_call)) {
1b2aea39 553 $associated_class->add_method($handle => Class::MOP::subname($name, $method_to_call));
452bac1b 554 }
555 else {
140001f0 556 # NOTE:
557 # we used to do a goto here, but the
558 # goto didn't handle failure correctly
559 # (it just returned nothing), so I took
560 # that out. However, the more I thought
561 # about it, the less I liked it doing
562 # the goto, and I prefered the act of
563 # delegation being actually represented
564 # in the stack trace.
565 # - SL
1b2aea39 566 $associated_class->add_method($handle => Class::MOP::subname($name, sub {
be05faea 567 my $instance = shift;
568 my $proxy = $instance->$accessor();
ccf49e80 569 (defined $proxy)
be05faea 570 || $self->throw_error("Cannot delegate $handle to $method_to_call because " .
cee532a1 571 "the value of " . $self->name . " is not defined", method_name => $method_to_call, object => $instance);
f4f3e701 572 $proxy->$method_to_call(@_);
1b2aea39 573 }));
452bac1b 574 }
575 }
576 }
26fbace8 577
452bac1b 578 return;
579}
580
98aae381 581# private methods to help delegation ...
582
452bac1b 583sub _canonicalize_handles {
584 my $self = shift;
585 my $handles = $self->handles;
c84f324f 586 if (my $handle_type = ref($handles)) {
587 if ($handle_type eq 'HASH') {
588 return %{$handles};
589 }
590 elsif ($handle_type eq 'ARRAY') {
591 return map { $_ => $_ } @{$handles};
592 }
593 elsif ($handle_type eq 'Regexp') {
594 ($self->has_type_constraint)
be05faea 595 || $self->throw_error("Cannot delegate methods based on a RegExpr without a type constraint (isa)", data => $handles);
26fbace8 596 return map { ($_ => $_) }
c84f324f 597 grep { /$handles/ } $self->_get_delegate_method_list;
598 }
599 elsif ($handle_type eq 'CODE') {
600 return $handles->($self, $self->_find_delegate_metaclass);
601 }
602 else {
be05faea 603 $self->throw_error("Unable to canonicalize the 'handles' option with $handles", data => $handles);
c84f324f 604 }
452bac1b 605 }
606 else {
c84f324f 607 my $role_meta = eval { $handles->meta };
608 if ($@) {
be05faea 609 $self->throw_error("Unable to canonicalize the 'handles' option with $handles because : $@", data => $handles, error => $@);
c84f324f 610 }
611
612 (blessed $role_meta && $role_meta->isa('Moose::Meta::Role'))
be05faea 613 || $self->throw_error("Unable to canonicalize the 'handles' option with $handles because ->meta is not a Moose::Meta::Role", data => $handles);
26fbace8 614
c84f324f 615 return map { $_ => $_ } (
26fbace8 616 $role_meta->get_method_list,
c84f324f 617 $role_meta->get_required_method_list
618 );
452bac1b 619 }
620}
621
622sub _find_delegate_metaclass {
623 my $self = shift;
98aae381 624 if (my $class = $self->_isa_metadata) {
26fbace8 625 # if the class does have
452bac1b 626 # a meta method, use it
627 return $class->meta if $class->can('meta');
26fbace8 628 # otherwise we might be
452bac1b 629 # dealing with a non-Moose
26fbace8 630 # class, and need to make
452bac1b 631 # our own metaclass
632 return Moose::Meta::Class->initialize($class);
633 }
98aae381 634 elsif (my $role = $self->_does_metadata) {
26fbace8 635 # our role will always have
452bac1b 636 # a meta method
98aae381 637 return $role->meta;
452bac1b 638 }
639 else {
be05faea 640 $self->throw_error("Cannot find delegate metaclass for attribute " . $self->name);
452bac1b 641 }
642}
643
644sub _get_delegate_method_list {
645 my $self = shift;
646 my $meta = $self->_find_delegate_metaclass;
647 if ($meta->isa('Class::MOP::Class')) {
093b12c2 648 return map { $_->{name} } # NOTE: !never! delegate &meta
26fbace8 649 grep { $_->{class} ne 'Moose::Object' && $_->{name} ne 'meta' }
452bac1b 650 $meta->compute_all_applicable_methods;
651 }
652 elsif ($meta->isa('Moose::Meta::Role')) {
26fbace8 653 return $meta->get_method_list;
452bac1b 654 }
655 else {
be05faea 656 $self->throw_error("Unable to recognize the delegate metaclass '$meta'", data => $meta);
452bac1b 657 }
658}
659
21f1e231 660package Moose::Meta::Attribute::Custom::Moose;
661sub register_implementation { 'Moose::Meta::Attribute' }
662
c0e30cf5 6631;
664
665__END__
666
667=pod
668
669=head1 NAME
670
6ba6d68c 671Moose::Meta::Attribute - The Moose attribute metaclass
c0e30cf5 672
673=head1 DESCRIPTION
674
26fbace8 675This is a subclass of L<Class::MOP::Attribute> with Moose specific
676extensions.
6ba6d68c 677
26fbace8 678For the most part, the only time you will ever encounter an
679instance of this class is if you are doing some serious deep
680introspection. To really understand this class, you need to refer
6ba6d68c 681to the L<Class::MOP::Attribute> documentation.
e522431d 682
c0e30cf5 683=head1 METHODS
684
6ba6d68c 685=head2 Overridden methods
686
26fbace8 687These methods override methods in L<Class::MOP::Attribute> and add
688Moose specific features. You can safely assume though that they
6ba6d68c 689will behave just as L<Class::MOP::Attribute> does.
690
c0e30cf5 691=over 4
692
693=item B<new>
694
c32c2c61 695=item B<clone>
696
6e2840b7 697=item B<does>
698
d500266f 699=item B<initialize_instance_slot>
700
452bac1b 701=item B<install_accessors>
702
39b3bc94 703=item B<accessor_metaclass>
704
946289d1 705=item B<get_value>
706
707=item B<set_value>
708
bcbaa845 709 eval { $point->meta->get_attribute('x')->set_value($point, 'fourty-two') };
710 if($@) {
711 print "Oops: $@\n";
712 }
713
714I<Attribute (x) does not pass the type constraint (Int) with 'fourty-two'>
715
716Before setting the value, a check is made on the type constraint of
717the attribute, if it has one, to see if the value passes it. If the
718value fails to pass, the set operation dies with a L<Carp/confess>.
719
720Any coercion to convert values is done before checking the type constraint.
721
722To check a value against a type constraint before setting it, fetch the
ec00fa75 723attribute instance using L<Class::MOP::Class/find_attribute_by_name>,
bcbaa845 724fetch the type_constraint from the attribute using L<Moose::Meta::Attribute/type_constraint>
725and call L<Moose::Meta::TypeConstraint/check>. See L<Moose::Cookbook::RecipeX>
726for an example.
727
a15dff8d 728=back
729
6ba6d68c 730=head2 Additional Moose features
731
26fbace8 732Moose attributes support type-constraint checking, weak reference
733creation and type coercion.
6ba6d68c 734
a15dff8d 735=over 4
736
be05faea 737=item B<throw_error>
738
739Delegates to C<associated_class> or C<Moose::Meta::Class> if there is none.
740
d5c30e52 741=item B<interpolate_class_and_new>
742
743=item B<interpolate_class>
744
745When called as a class method causes interpretation of the C<metaclass> and
746C<traits> options.
747
9e93dd19 748=item B<clone_and_inherit_options>
749
26fbace8 750This is to support the C<has '+foo'> feature, it clones an attribute
751from a superclass and allows a very specific set of changes to be made
9e93dd19 752to the attribute.
753
a15dff8d 754=item B<has_type_constraint>
755
6ba6d68c 756Returns true if this meta-attribute has a type constraint.
757
a15dff8d 758=item B<type_constraint>
759
26fbace8 760A read-only accessor for this meta-attribute's type constraint. For
761more information on what you can do with this, see the documentation
6ba6d68c 762for L<Moose::Meta::TypeConstraint>.
a15dff8d 763
452bac1b 764=item B<has_handles>
765
766Returns true if this meta-attribute performs delegation.
767
768=item B<handles>
769
770This returns the value which was passed into the handles option.
771
6ba6d68c 772=item B<is_weak_ref>
a15dff8d 773
02a0fb52 774Returns true if this meta-attribute produces a weak reference.
4b598ea3 775
ca01a97b 776=item B<is_required>
777
02a0fb52 778Returns true if this meta-attribute is required to have a value.
ca01a97b 779
780=item B<is_lazy>
781
02a0fb52 782Returns true if this meta-attribute should be initialized lazily.
ca01a97b 783
26fbace8 784NOTE: lazy attributes, B<must> have a C<default> or C<builder> field set.
785
786=item B<is_lazy_build>
787
788Returns true if this meta-attribute should be initialized lazily through
789the builder generated by lazy_build. Using C<lazy_build =E<gt> 1> will
790make your attribute required and lazy. In addition it will set the builder, clearer
791and predicate options for you using the following convention.
792
793 #If your attribute name starts with an underscore:
794 has '_foo' => (lazy_build => 1);
795 #is the same as
58f85113 796 has '_foo' => (lazy => 1, required => 1, predicate => '_has_foo', clearer => '_clear_foo', builder => '_build__foo);
26fbace8 797 # or
58f85113 798 has '_foo' => (lazy => 1, required => 1, predicate => '_has_foo', clearer => '_clear_foo', default => sub{shift->_build__foo});
26fbace8 799
800 #If your attribute name does not start with an underscore:
58f85113 801 has 'foo' => (lazy_build => 1);
802 #is the same as
803 has 'foo' => (lazy => 1, required => 1, predicate => 'has_foo', clearer => 'clear_foo', builder => '_build_foo);
26fbace8 804 # or
58f85113 805 has 'foo' => (lazy => 1, required => 1, predicate => 'has_foo', clearer => 'clear_foo', default => sub{shift->_build_foo});
806
807The reason for the different naming of the C<builder> is that the C<builder>
808method is a private method while the C<clearer> and C<predicate> methods
809are public methods.
26fbace8 810
811NOTE: This means your class should provide a method whose name matches the value
58f85113 812of the builder part, in this case _build__foo or _build_foo.
ca01a97b 813
34a66aa3 814=item B<should_coerce>
4b598ea3 815
02a0fb52 816Returns true if this meta-attribute should perform type coercion.
6ba6d68c 817
536f0b17 818=item B<should_auto_deref>
819
26fbace8 820Returns true if this meta-attribute should perform automatic
821auto-dereferencing.
536f0b17 822
26fbace8 823NOTE: This can only be done for attributes whose type constraint is
536f0b17 824either I<ArrayRef> or I<HashRef>.
825
8c9d74e7 826=item B<has_trigger>
827
02a0fb52 828Returns true if this meta-attribute has a trigger set.
829
8c9d74e7 830=item B<trigger>
831
26fbace8 832This is a CODE reference which will be executed every time the
833value of an attribute is assigned. The CODE ref will get two values,
834the invocant and the new value. This can be used to handle I<basic>
02a0fb52 835bi-directional relations.
836
ddbdc0cb 837=item B<documentation>
838
26fbace8 839This is a string which contains the documentation for this attribute.
ddbdc0cb 840It serves no direct purpose right now, but it might in the future
841in some kind of automated documentation system perhaps.
842
843=item B<has_documentation>
844
845Returns true if this meta-attribute has any documentation.
846
88f23977 847=item B<applied_traits>
848
849This will return the ARRAY ref of all the traits applied to this
850attribute, or if no traits have been applied, it returns C<undef>.
851
852=item B<has_applied_traits>
853
854Returns true if this meta-attribute has any traits applied.
855
c0e30cf5 856=back
857
858=head1 BUGS
859
26fbace8 860All complex software has bugs lurking in it, and this module is no
c0e30cf5 861exception. If you find a bug please either email me, or add the bug
862to cpan-RT.
863
c0e30cf5 864=head1 AUTHOR
865
866Stevan Little E<lt>stevan@iinteractive.comE<gt>
867
98aae381 868Yuval Kogman E<lt>nothingmuch@woobling.comE<gt>
869
c0e30cf5 870=head1 COPYRIGHT AND LICENSE
871
778db3ac 872Copyright 2006-2008 by Infinity Interactive, Inc.
c0e30cf5 873
874L<http://www.iinteractive.com>
875
876This library is free software; you can redistribute it and/or modify
26fbace8 877it under the same terms as Perl itself.
c0e30cf5 878
8a7a9c53 879=cut