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