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