2 package Moose::Meta::Attribute;
7 use Scalar::Util 'blessed', 'weaken', 'reftype';
10 our $VERSION = '0.10';
11 our $AUTHORITY = 'cpan:STEVAN';
13 use Moose::Meta::Method::Accessor;
14 use Moose::Util::TypeConstraints ();
16 use base 'Class::MOP::Attribute';
18 # options which are not directly used
19 # but we store them for metadata purposes
20 __PACKAGE__->meta->add_attribute('isa' => (reader => '_isa_metadata'));
21 __PACKAGE__->meta->add_attribute('does' => (reader => '_does_metadata'));
22 __PACKAGE__->meta->add_attribute('is' => (reader => '_is_metadata'));
24 # these are actual options for the attrs
25 __PACKAGE__->meta->add_attribute('required' => (reader => 'is_required' ));
26 __PACKAGE__->meta->add_attribute('lazy' => (reader => 'is_lazy' ));
27 __PACKAGE__->meta->add_attribute('coerce' => (reader => 'should_coerce' ));
28 __PACKAGE__->meta->add_attribute('weak_ref' => (reader => 'is_weak_ref' ));
29 __PACKAGE__->meta->add_attribute('auto_deref' => (reader => 'should_auto_deref'));
30 __PACKAGE__->meta->add_attribute('type_constraint' => (
31 reader => 'type_constraint',
32 predicate => 'has_type_constraint',
34 __PACKAGE__->meta->add_attribute('trigger' => (
36 predicate => 'has_trigger',
38 __PACKAGE__->meta->add_attribute('handles' => (
40 predicate => 'has_handles',
44 my ($class, $name, %options) = @_;
45 $class->_process_options($name, \%options);
46 return $class->SUPER::new($name, %options);
49 sub clone_and_inherit_options {
50 my ($self, %options) = @_;
51 # you can change default, required and coerce
53 foreach my $legal_option (qw(default coerce required)) {
54 if (exists $options{$legal_option}) {
55 $actual_options{$legal_option} = $options{$legal_option};
56 delete $options{$legal_option};
59 # isa can be changed, but only if the
60 # new type is a subtype
63 if (blessed($options{isa}) && $options{isa}->isa('Moose::Meta::TypeConstraint')) {
64 $type_constraint = $options{isa};
67 $type_constraint = Moose::Util::TypeConstraints::find_type_constraint($options{isa});
68 (defined $type_constraint)
69 || confess "Could not find the type constraint '" . $options{isa} . "'";
72 # check here to see if the new type
73 # is a subtype of the old one
74 ($type_constraint->is_subtype_of($self->type_constraint->name))
75 || confess "New type constraint setting must be a subtype of inherited one"
76 # iff we have a type constraint that is ...
77 if $self->has_type_constraint;
79 $actual_options{type_constraint} = $type_constraint;
82 (scalar keys %options == 0)
83 || confess "Illegal inherited options => (" . (join ', ' => keys %options) . ")";
84 $self->clone(%actual_options);
87 sub _process_options {
88 my ($class, $name, $options) = @_;
90 if (exists $options->{is}) {
91 if ($options->{is} eq 'ro') {
92 $options->{reader} = $name;
93 (!exists $options->{trigger})
94 || confess "Cannot have a trigger on a read-only attribute";
96 elsif ($options->{is} eq 'rw') {
97 $options->{accessor} = $name;
98 ((reftype($options->{trigger}) || '') eq 'CODE')
99 || confess "Trigger must be a CODE ref"
100 if exists $options->{trigger};
103 confess "I do not understand this option (is => " . $options->{is} . ")"
107 if (exists $options->{isa}) {
109 if (exists $options->{does}) {
110 if (eval { $options->{isa}->can('does') }) {
111 ($options->{isa}->does($options->{does}))
112 || confess "Cannot have an isa option and a does option if the isa does not do the does";
115 confess "Cannot have an isa option which cannot ->does()";
119 # allow for anon-subtypes here ...
120 if (blessed($options->{isa}) && $options->{isa}->isa('Moose::Meta::TypeConstraint')) {
121 $options->{type_constraint} = $options->{isa};
125 if ($options->{isa} =~ /\|/) {
126 my @type_constraints = split /\s*\|\s*/ => $options->{isa};
127 $options->{type_constraint} = Moose::Util::TypeConstraints::create_type_constraint_union(
132 # otherwise assume it is a constraint
133 my $constraint = Moose::Util::TypeConstraints::find_type_constraint($options->{isa});
134 # if the constraing it not found ....
135 unless (defined $constraint) {
136 # assume it is a foreign class, and make
137 # an anon constraint for it
138 $constraint = Moose::Util::TypeConstraints::subtype(
140 Moose::Util::TypeConstraints::where { $_->isa($options->{isa}) }
143 $options->{type_constraint} = $constraint;
147 elsif (exists $options->{does}) {
148 # allow for anon-subtypes here ...
149 if (blessed($options->{does}) && $options->{does}->isa('Moose::Meta::TypeConstraint')) {
150 $options->{type_constraint} = $options->{isa};
153 # otherwise assume it is a constraint
154 my $constraint = Moose::Util::TypeConstraints::find_type_constraint($options->{does});
155 # if the constraing it not found ....
156 unless (defined $constraint) {
157 # assume it is a foreign class, and make
158 # an anon constraint for it
159 $constraint = Moose::Util::TypeConstraints::subtype(
161 Moose::Util::TypeConstraints::where { $_->does($options->{does}) }
164 $options->{type_constraint} = $constraint;
168 if (exists $options->{coerce} && $options->{coerce}) {
169 (exists $options->{type_constraint})
170 || confess "You cannot have coercion without specifying a type constraint";
171 confess "You cannot have a weak reference to a coerced value"
172 if $options->{weak_ref};
175 if (exists $options->{auto_deref} && $options->{auto_deref}) {
176 (exists $options->{type_constraint})
177 || confess "You cannot auto-dereference without specifying a type constraint";
178 ($options->{type_constraint}->is_a_type_of('ArrayRef') ||
179 $options->{type_constraint}->is_a_type_of('HashRef'))
180 || confess "You cannot auto-dereference anything other than a ArrayRef or HashRef";
183 if (exists $options->{lazy} && $options->{lazy}) {
184 (exists $options->{default})
185 || confess "You cannot have lazy attribute without specifying a default value for it";
189 sub initialize_instance_slot {
190 my ($self, $meta_instance, $instance, $params) = @_;
191 my $init_arg = $self->init_arg();
192 # try to fetch the init arg from the %params ...
195 if (exists $params->{$init_arg}) {
196 $val = $params->{$init_arg};
199 # skip it if it's lazy
200 return if $self->is_lazy;
201 # and die if it's required and doesn't have a default value
202 confess "Attribute (" . $self->name . ") is required"
203 if $self->is_required && !$self->has_default;
206 # if nothing was in the %params, we can use the
207 # attribute's default value (if it has one)
208 if (!defined $val && $self->has_default) {
209 $val = $self->default($instance);
212 if ($self->has_type_constraint) {
213 my $type_constraint = $self->type_constraint;
214 if ($self->should_coerce && $type_constraint->has_coercion) {
215 $val = $type_constraint->coerce($val);
217 (defined($type_constraint->check($val)))
218 || confess "Attribute (" .
220 ") does not pass the type constraint (" .
221 $type_constraint->name .
226 $meta_instance->set_slot_value($instance, $self->name, $val);
227 $meta_instance->weaken_slot_value($instance, $self->name)
228 if ref $val && $self->is_weak_ref;
234 my ($self, $instance, $value) = @_;
236 my $attr_name = $self->name;
238 if ($self->is_required) {
240 || confess "Attribute ($attr_name) is required, so cannot be set to undef";
243 if ($self->has_type_constraint) {
245 my $type_constraint = $self->type_constraint;
247 if ($self->should_coerce) {
248 $value = $type_constraint->coerce($value);
250 defined($type_constraint->_compiled_type_constraint->($value))
251 || confess "Attribute ($attr_name) does not pass the type constraint ("
252 . $type_constraint->name . ") with " . (defined($value) ? ("'" . $value . "'") : "undef")
256 my $meta_instance = Class::MOP::Class->initialize(blessed($instance))
259 $meta_instance->set_slot_value($instance, $attr_name, $value);
261 if (ref $value && $self->is_weak_ref) {
262 $meta_instance->weaken_slot_value($instance, $attr_name);
265 if ($self->has_trigger) {
266 $self->trigger->($instance, $value, $self);
271 my ($self, $instance) = @_;
273 if ($self->is_lazy) {
274 unless ($self->has_value($instance)) {
275 if ($self->has_default) {
276 my $default = $self->default($instance);
277 $self->set_value($instance, $default);
280 $self->set_value($instance, undef);
285 if ($self->should_auto_deref) {
287 my $type_constraint = $self->type_constraint;
289 if ($type_constraint->is_a_type_of('ArrayRef')) {
290 my $rv = $self->SUPER::get_value($instance);
291 return unless defined $rv;
292 return wantarray ? @{ $rv } : $rv;
294 elsif ($type_constraint->is_a_type_of('HashRef')) {
295 my $rv = $self->SUPER::get_value($instance);
296 return unless defined $rv;
297 return wantarray ? %{ $rv } : $rv;
300 confess "Can not auto de-reference the type constraint '" . $type_constraint->name . "'";
306 return $self->SUPER::get_value($instance);
310 ## installing accessors
312 sub accessor_metaclass { 'Moose::Meta::Method::Accessor' }
314 sub install_accessors {
316 $self->SUPER::install_accessors(@_);
318 if ($self->has_handles) {
321 # Here we canonicalize the 'handles' option
322 # this will sort out any details and always
323 # return an hash of methods which we want
324 # to delagate to, see that method for details
325 my %handles = $self->_canonicalize_handles();
327 # find the name of the accessor for this attribute
328 my $accessor_name = $self->reader || $self->accessor;
329 (defined $accessor_name)
330 || confess "You cannot install delegation without a reader or accessor for the attribute";
332 # make sure we handle HASH accessors correctly
333 ($accessor_name) = keys %{$accessor_name}
334 if ref($accessor_name) eq 'HASH';
336 # install the delegation ...
337 my $associated_class = $self->associated_class;
338 foreach my $handle (keys %handles) {
339 my $method_to_call = $handles{$handle};
341 (!$associated_class->has_method($handle))
342 || confess "You cannot overwrite a locally defined method ($handle) with a delegation";
345 # handles is not allowed to delegate
346 # any of these methods, as they will
347 # override the ones in your class, which
348 # is almost certainly not what you want.
349 next if $handle =~ /^BUILD|DEMOLISH$/ || Moose::Object->can($handle);
351 if ((reftype($method_to_call) || '') eq 'CODE') {
352 $associated_class->add_method($handle => $method_to_call);
355 $associated_class->add_method($handle => sub {
357 # we should check for lack of
358 # a callable return value from
360 ((shift)->$accessor_name())->$method_to_call(@_);
369 # private methods to help delegation ...
371 sub _canonicalize_handles {
373 my $handles = $self->handles;
374 if (ref($handles) eq 'HASH') {
377 elsif (ref($handles) eq 'ARRAY') {
378 return map { $_ => $_ } @{$handles};
380 elsif (ref($handles) eq 'Regexp') {
381 ($self->has_type_constraint)
382 || confess "Cannot delegate methods based on a RegExpr without a type constraint (isa)";
383 return map { ($_ => $_) }
384 grep { /$handles/ } $self->_get_delegate_method_list;
386 elsif (ref($handles) eq 'CODE') {
387 return $handles->($self, $self->_find_delegate_metaclass);
390 confess "Unable to canonicalize the 'handles' option with $handles";
394 sub _find_delegate_metaclass {
396 if (my $class = $self->_isa_metadata) {
397 # if the class does have
398 # a meta method, use it
399 return $class->meta if $class->can('meta');
400 # otherwise we might be
401 # dealing with a non-Moose
402 # class, and need to make
404 return Moose::Meta::Class->initialize($class);
406 elsif (my $role = $self->_does_metadata) {
407 # our role will always have
412 confess "Cannot find delegate metaclass for attribute " . $self->name;
416 sub _get_delegate_method_list {
418 my $meta = $self->_find_delegate_metaclass;
419 if ($meta->isa('Class::MOP::Class')) {
420 return map { $_->{name} } # NOTE: !never! delegate &meta
421 grep { $_->{class} ne 'Moose::Object' && $_->{name} ne 'meta' }
422 $meta->compute_all_applicable_methods;
424 elsif ($meta->isa('Moose::Meta::Role')) {
425 return $meta->get_method_list;
428 confess "Unable to recognize the delegate metaclass '$meta'";
440 Moose::Meta::Attribute - The Moose attribute metaclass
444 This is a subclass of L<Class::MOP::Attribute> with Moose specific
447 For the most part, the only time you will ever encounter an
448 instance of this class is if you are doing some serious deep
449 introspection. To really understand this class, you need to refer
450 to the L<Class::MOP::Attribute> documentation.
454 =head2 Overridden methods
456 These methods override methods in L<Class::MOP::Attribute> and add
457 Moose specific features. You can safely assume though that they
458 will behave just as L<Class::MOP::Attribute> does.
464 =item B<initialize_instance_slot>
466 =item B<install_accessors>
468 =item B<accessor_metaclass>
476 =head2 Additional Moose features
478 Moose attributes support type-constraint checking, weak reference
479 creation and type coercion.
483 =item B<clone_and_inherit_options>
485 This is to support the C<has '+foo'> feature, it clones an attribute
486 from a superclass and allows a very specific set of changes to be made
489 =item B<has_type_constraint>
491 Returns true if this meta-attribute has a type constraint.
493 =item B<type_constraint>
495 A read-only accessor for this meta-attribute's type constraint. For
496 more information on what you can do with this, see the documentation
497 for L<Moose::Meta::TypeConstraint>.
501 Returns true if this meta-attribute performs delegation.
505 This returns the value which was passed into the handles option.
509 Returns true if this meta-attribute produces a weak reference.
513 Returns true if this meta-attribute is required to have a value.
517 Returns true if this meta-attribute should be initialized lazily.
519 NOTE: lazy attributes, B<must> have a C<default> field set.
521 =item B<should_coerce>
523 Returns true if this meta-attribute should perform type coercion.
525 =item B<should_auto_deref>
527 Returns true if this meta-attribute should perform automatic
530 NOTE: This can only be done for attributes whose type constraint is
531 either I<ArrayRef> or I<HashRef>.
535 Returns true if this meta-attribute has a trigger set.
539 This is a CODE reference which will be executed every time the
540 value of an attribute is assigned. The CODE ref will get two values,
541 the invocant and the new value. This can be used to handle I<basic>
542 bi-directional relations.
548 All complex software has bugs lurking in it, and this module is no
549 exception. If you find a bug please either email me, or add the bug
554 Stevan Little E<lt>stevan@iinteractive.comE<gt>
556 Yuval Kogman E<lt>nothingmuch@woobling.comE<gt>
558 =head1 COPYRIGHT AND LICENSE
560 Copyright 2006, 2007 by Infinity Interactive, Inc.
562 L<http://www.iinteractive.com>
564 This library is free software; you can redistribute it and/or modify
565 it under the same terms as Perl itself.