2 package Moose::Meta::Attribute;
7 use Scalar::Util 'blessed', 'weaken', 'reftype';
10 our $VERSION = '0.05';
12 use Moose::Util::TypeConstraints ();
14 use base 'Class::MOP::Attribute';
16 __PACKAGE__->meta->add_attribute('required' => (reader => 'is_required' ));
17 __PACKAGE__->meta->add_attribute('lazy' => (reader => 'is_lazy' ));
18 __PACKAGE__->meta->add_attribute('coerce' => (reader => 'should_coerce' ));
19 __PACKAGE__->meta->add_attribute('weak_ref' => (reader => 'is_weak_ref' ));
20 __PACKAGE__->meta->add_attribute('auto_deref' => (reader => 'should_auto_deref'));
21 __PACKAGE__->meta->add_attribute('type_constraint' => (
22 reader => 'type_constraint',
23 predicate => 'has_type_constraint',
25 __PACKAGE__->meta->add_attribute('trigger' => (
27 predicate => 'has_trigger',
31 my ($class, $name, %options) = @_;
32 $class->_process_options($name, \%options);
33 $class->SUPER::new($name, %options);
36 sub clone_and_inherit_options {
37 my ($self, %options) = @_;
38 # you can change default, required and coerce
40 foreach my $legal_option (qw(default coerce required)) {
41 if (exists $options{$legal_option}) {
42 $actual_options{$legal_option} = $options{$legal_option};
43 delete $options{$legal_option};
46 # isa can be changed, but only if the
47 # new type is a subtype
50 if (blessed($options{isa}) && $options{isa}->isa('Moose::Meta::TypeConstraint')) {
51 $type_constraint = $options{isa};
54 $type_constraint = Moose::Util::TypeConstraints::find_type_constraint($options{isa});
55 (defined $type_constraint)
56 || confess "Could not find the type constraint '" . $options{isa} . "'";
58 ($type_constraint->is_subtype_of($self->type_constraint->name))
59 || confess "New type constraint setting must be a subtype of inherited one"
60 if $self->has_type_constraint;
61 $actual_options{type_constraint} = $type_constraint;
64 (scalar keys %options == 0)
65 || confess "Illegal inherited options => (" . (join ', ' => keys %options) . ")";
66 $self->clone(%actual_options);
69 sub _process_options {
70 my ($class, $name, $options) = @_;
71 if (exists $options->{is}) {
72 if ($options->{is} eq 'ro') {
73 $options->{reader} = $name;
74 (!exists $options->{trigger})
75 || confess "Cannot have a trigger on a read-only attribute";
77 elsif ($options->{is} eq 'rw') {
78 $options->{accessor} = $name;
79 ((reftype($options->{trigger}) || '') eq 'CODE')
80 || confess "A trigger must be a CODE reference"
81 if exists $options->{trigger};
85 if (exists $options->{isa}) {
87 if (exists $options->{does}) {
88 if (eval { $options->{isa}->can('does') }) {
89 ($options->{isa}->does($options->{does}))
90 || confess "Cannot have an isa option and a does option if the isa does not do the does";
93 confess "Cannot have an isa option which cannot ->does()";
97 # allow for anon-subtypes here ...
98 if (blessed($options->{isa}) && $options->{isa}->isa('Moose::Meta::TypeConstraint')) {
99 $options->{type_constraint} = $options->{isa};
103 if ($options->{isa} =~ /\|/) {
104 my @type_constraints = split /\s*\|\s*/ => $options->{isa};
105 $options->{type_constraint} = Moose::Util::TypeConstraints::create_type_constraint_union(
110 # otherwise assume it is a constraint
111 my $constraint = Moose::Util::TypeConstraints::find_type_constraint($options->{isa});
112 # if the constraing it not found ....
113 unless (defined $constraint) {
114 # assume it is a foreign class, and make
115 # an anon constraint for it
116 $constraint = Moose::Util::TypeConstraints::subtype(
118 Moose::Util::TypeConstraints::where { $_->isa($options->{isa}) }
121 $options->{type_constraint} = $constraint;
125 elsif (exists $options->{does}) {
126 # allow for anon-subtypes here ...
127 if (blessed($options->{does}) && $options->{does}->isa('Moose::Meta::TypeConstraint')) {
128 $options->{type_constraint} = $options->{isa};
131 # otherwise assume it is a constraint
132 my $constraint = Moose::Util::TypeConstraints::find_type_constraint($options->{does});
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(
139 Moose::Util::TypeConstraints::where { $_->does($options->{does}) }
142 $options->{type_constraint} = $constraint;
146 if (exists $options->{coerce} && $options->{coerce}) {
147 (exists $options->{type_constraint})
148 || confess "You cannot have coercion without specifying a type constraint";
149 (!$options->{type_constraint}->isa('Moose::Meta::TypeConstraint::Union'))
150 || confess "You cannot have coercion with a type constraint union";
151 confess "You cannot have a weak reference to a coerced value"
152 if $options->{weak_ref};
155 if (exists $options->{auto_deref} && $options->{auto_deref}) {
156 (exists $options->{type_constraint})
157 || confess "You cannot auto-dereference without specifying a type constraint";
158 ($options->{type_constraint}->name =~ /^ArrayRef|HashRef$/)
159 || confess "You cannot auto-dereference anything other than a ArrayRef or HashRef";
162 if (exists $options->{lazy} && $options->{lazy}) {
163 (exists $options->{default})
164 || confess "You cannot have lazy attribute without specifying a default value for it";
168 sub initialize_instance_slot {
169 my ($self, $meta_instance, $instance, $params) = @_;
170 my $init_arg = $self->init_arg();
171 # try to fetch the init arg from the %params ...
174 if (exists $params->{$init_arg}) {
175 $val = $params->{$init_arg};
178 # skip it if it's lazy
179 return if $self->is_lazy;
180 # and die if it's required and doesn't have a default value
181 confess "Attribute (" . $self->name . ") is required"
182 if $self->is_required && !$self->has_default;
185 # if nothing was in the %params, we can use the
186 # attribute's default value (if it has one)
187 if (!defined $val && $self->has_default) {
188 $val = $self->default($instance);
191 if ($self->has_type_constraint) {
192 my $type_constraint = $self->type_constraint;
193 if ($self->should_coerce && $type_constraint->has_coercion) {
194 $val = $type_constraint->coercion->coerce($val);
196 (defined($type_constraint->check($val)))
197 || confess "Attribute (" .
199 ") does not pass the type contraint (" .
200 $type_constraint->name .
205 $meta_instance->set_slot_value($instance, $self->name, $val);
206 $meta_instance->weaken_slot_value($instance, $self->name)
207 if ref $val && $self->is_weak_ref;
210 sub _inline_check_constraint {
211 my ($self, $value) = @_;
212 return '' unless $self->has_type_constraint;
214 # FIXME - remove 'unless defined($value) - constraint Undef
215 return sprintf <<'EOF', $value, $value, $value, $value
216 defined($attr->type_constraint->check(%s))
217 || confess "Attribute (" . $attr->name . ") does not pass the type contraint ("
218 . $attr->type_constraint->name . ") with " . (defined(%s) ? "'%s'" : "undef")
224 my ($self, $instance, $value) = @_;
226 my $mi = $self->associated_class->get_meta_instance;
227 my $slot_name = sprintf "'%s'", $self->slots;
229 my $code = $mi->inline_set_slot_value($instance, $slot_name, $value) . ";";
230 $code .= $mi->inline_weaken_slot_value($instance, $slot_name, $value) . ";"
231 if $self->is_weak_ref;
235 sub _inline_trigger {
236 my ($self, $instance, $value) = @_;
237 return '' unless $self->has_trigger;
238 return sprintf('$attr->trigger->(%s, %s, $attr);', $instance, $value);
242 my ($self, $instance) = @_;
244 my $mi = $self->associated_class->get_meta_instance;
245 my $slot_name = sprintf "'%s'", $self->slots;
247 return $mi->inline_get_slot_value($instance, $slot_name);
250 sub _inline_auto_deref {
251 my ( $self, $ref_value ) = @_;
253 return $ref_value unless $self->should_auto_deref;
255 my $type = $self->type_constraint->name;
258 if ($type eq "ArrayRef") {
261 elsif ($type eq 'HashRef') {
265 confess "Can not auto de-reference the type constraint '$type'";
268 "(wantarray() ? $sigil\{ ( $ref_value ) || return } : ( $ref_value ) )";
271 sub generate_accessor_method {
272 my ($attr, $attr_name) = @_;
273 my $value_name = $attr->should_coerce ? '$val' : '$_[1]';
274 my $mi = $attr->associated_class->get_meta_instance;
275 my $slot_name = sprintf "'%s'", $attr->slots;
278 . 'if (scalar(@_) == 2) {'
279 . ($attr->is_required ?
280 'defined($_[1]) || confess "Attribute ($attr_name) is required, so cannot be set to undef";'
282 . ($attr->should_coerce ?
283 'my $val = $attr->type_constraint->coercion->coerce($_[1]);'
285 . $attr->_inline_check_constraint($value_name)
286 . $attr->_inline_store($inv, $value_name)
287 . $attr->_inline_trigger($inv, $value_name)
290 '$_[0]->{$attr_name} = ($attr->has_default ? $attr->default($_[0]) : undef)'
291 . 'unless exists $_[0]->{$attr_name};'
293 . 'return ' . $attr->_inline_auto_deref($attr->_inline_get($inv))
295 my $sub = eval $code;
296 warn "Could not create accessor for '$attr_name' because $@ \n code: $code" if $@;
297 confess "Could not create accessor for '$attr_name' because $@ \n code: $code" if $@;
301 sub generate_writer_method {
302 my ($attr, $attr_name) = @_;
303 my $value_name = $attr->should_coerce ? '$val' : '$_[1]';
306 . ($attr->is_required ?
307 'defined($_[1]) || confess "Attribute ($attr_name) is required, so cannot be set to undef";'
309 . ($attr->should_coerce ?
310 'my $val = $attr->type_constraint->coercion->coerce($_[1]);'
312 . $attr->_inline_check_constraint($value_name)
313 . $attr->_inline_store($inv, $value_name)
314 . $attr->_inline_trigger($inv, $value_name)
316 my $sub = eval $code;
317 confess "Could not create writer for '$attr_name' because $@ \n code: $code" if $@;
321 sub generate_reader_method {
323 my $attr_name = $self->slots;
325 . 'confess "Cannot assign a value to a read-only accessor" if @_ > 1;'
327 '$_[0]->{$attr_name} = ($self->has_default ? $self->default($_[0]) : undef)'
328 . 'unless exists $_[0]->{$attr_name};'
330 . 'return ' . $self->_inline_auto_deref( '$_[0]->{$attr_name}' ) . ';'
332 my $sub = eval $code;
333 confess "Could not create reader for '$attr_name' because $@ \n code: $code" if $@;
345 Moose::Meta::Attribute - The Moose attribute metaclass
349 This is a subclass of L<Class::MOP::Attribute> with Moose specific
352 For the most part, the only time you will ever encounter an
353 instance of this class is if you are doing some serious deep
354 introspection. To really understand this class, you need to refer
355 to the L<Class::MOP::Attribute> documentation.
359 =head2 Overridden methods
361 These methods override methods in L<Class::MOP::Attribute> and add
362 Moose specific features. You can safely assume though that they
363 will behave just as L<Class::MOP::Attribute> does.
369 =item B<clone_and_inherit_options>
371 =item B<initialize_instance_slot>
373 =item B<generate_accessor_method>
375 =item B<generate_writer_method>
377 =item B<generate_reader_method>
381 =head2 Additional Moose features
383 Moose attributes support type-contstraint checking, weak reference
384 creation and type coercion.
388 =item B<has_type_constraint>
390 Returns true if this meta-attribute has a type constraint.
392 =item B<type_constraint>
394 A read-only accessor for this meta-attribute's type constraint. For
395 more information on what you can do with this, see the documentation
396 for L<Moose::Meta::TypeConstraint>.
400 Returns true if this meta-attribute produces a weak reference.
404 Returns true if this meta-attribute is required to have a value.
408 Returns true if this meta-attribute should be initialized lazily.
410 NOTE: lazy attributes, B<must> have a C<default> field set.
412 =item B<should_coerce>
414 Returns true if this meta-attribute should perform type coercion.
416 =item B<should_auto_deref>
418 Returns true if this meta-attribute should perform automatic
421 NOTE: This can only be done for attributes whose type constraint is
422 either I<ArrayRef> or I<HashRef>.
426 Returns true if this meta-attribute has a trigger set.
430 This is a CODE reference which will be executed every time the
431 value of an attribute is assigned. The CODE ref will get two values,
432 the invocant and the new value. This can be used to handle I<basic>
433 bi-directional relations.
439 All complex software has bugs lurking in it, and this module is no
440 exception. If you find a bug please either email me, or add the bug
445 Stevan Little E<lt>stevan@iinteractive.comE<gt>
447 =head1 COPYRIGHT AND LICENSE
449 Copyright 2006 by Infinity Interactive, Inc.
451 L<http://www.iinteractive.com>
453 This library is free software; you can redistribute it and/or modify
454 it under the same terms as Perl itself.