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