mooooooooooose
[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
4c4fbe56 10our $VERSION = '0.05';
78cd1d3b 11
a3c7e2fe 12use Moose::Util::TypeConstraints ();
bc1e29b5 13
c0e30cf5 14use base 'Class::MOP::Attribute';
15
ca01a97b 16__PACKAGE__->meta->add_attribute('required' => (reader => 'is_required' ));
17__PACKAGE__->meta->add_attribute('lazy' => (reader => 'is_lazy' ));
6ba6d68c 18__PACKAGE__->meta->add_attribute('coerce' => (reader => 'should_coerce'));
19__PACKAGE__->meta->add_attribute('weak_ref' => (reader => 'is_weak_ref' ));
82168dbb 20__PACKAGE__->meta->add_attribute('type_constraint' => (
21 reader => 'type_constraint',
22 predicate => 'has_type_constraint',
23));
8c9d74e7 24__PACKAGE__->meta->add_attribute('trigger' => (
25 reader => 'trigger',
26 predicate => 'has_trigger',
27));
82168dbb 28
78cd1d3b 29sub new {
30 my ($class, $name, %options) = @_;
1d768fb1 31 $class->_process_options($name, \%options);
32 $class->SUPER::new($name, %options);
33}
34
35sub clone {
36 my ($self, %options) = @_;
37 $self->_process_options($self->name, \%options);
38 $self->SUPER::clone(%options);
39}
40
41sub _process_options {
42 my ($class, $name, $options) = @_;
43 if (exists $options->{is}) {
44 if ($options->{is} eq 'ro') {
45 $options->{reader} = $name;
46 (!exists $options->{trigger})
8c9d74e7 47 || confess "Cannot have a trigger on a read-only attribute";
78cd1d3b 48 }
1d768fb1 49 elsif ($options->{is} eq 'rw') {
50 $options->{accessor} = $name;
51 ((reftype($options->{trigger}) || '') eq 'CODE')
8c9d74e7 52 || confess "A trigger must be a CODE reference"
1d768fb1 53 if exists $options->{trigger};
78cd1d3b 54 }
55 }
56
1d768fb1 57 if (exists $options->{isa}) {
02a0fb52 58
1d768fb1 59 if (exists $options->{does}) {
60 if (eval { $options->{isa}->can('does') }) {
61 ($options->{isa}->does($options->{does}))
02a0fb52 62 || confess "Cannot have an isa option and a does option if the isa does not do the does";
63 }
7eaef7ad 64 else {
65 confess "Cannot have an isa option which cannot ->does()";
66 }
02a0fb52 67 }
68
78cd1d3b 69 # allow for anon-subtypes here ...
1d768fb1 70 if (blessed($options->{isa}) && $options->{isa}->isa('Moose::Meta::TypeConstraint')) {
71 $options->{type_constraint} = $options->{isa};
78cd1d3b 72 }
73 else {
c07af9d2 74
1d768fb1 75 if ($options->{isa} =~ /\|/) {
76 my @type_constraints = split /\s*\|\s*/ => $options->{isa};
77 $options->{type_constraint} = Moose::Util::TypeConstraints::create_type_constraint_union(
c07af9d2 78 @type_constraints
78cd1d3b 79 );
c07af9d2 80 }
81 else {
82 # otherwise assume it is a constraint
1d768fb1 83 my $constraint = Moose::Util::TypeConstraints::find_type_constraint($options->{isa});
c07af9d2 84 # if the constraing it not found ....
85 unless (defined $constraint) {
86 # assume it is a foreign class, and make
87 # an anon constraint for it
88 $constraint = Moose::Util::TypeConstraints::subtype(
89 'Object',
1d768fb1 90 Moose::Util::TypeConstraints::where { $_->isa($options->{isa}) }
c07af9d2 91 );
92 }
1d768fb1 93 $options->{type_constraint} = $constraint;
c07af9d2 94 }
78cd1d3b 95 }
96 }
1d768fb1 97 elsif (exists $options->{does}) {
02a0fb52 98 # allow for anon-subtypes here ...
1d768fb1 99 if (blessed($options->{does}) && $options->{does}->isa('Moose::Meta::TypeConstraint')) {
100 $options->{type_constraint} = $options->{isa};
02a0fb52 101 }
102 else {
103 # otherwise assume it is a constraint
1d768fb1 104 my $constraint = Moose::Util::TypeConstraints::find_type_constraint($options->{does});
02a0fb52 105 # if the constraing it not found ....
106 unless (defined $constraint) {
107 # assume it is a foreign class, and make
108 # an anon constraint for it
109 $constraint = Moose::Util::TypeConstraints::subtype(
110 'Role',
1d768fb1 111 Moose::Util::TypeConstraints::where { $_->does($options->{does}) }
02a0fb52 112 );
113 }
1d768fb1 114 $options->{type_constraint} = $constraint;
02a0fb52 115 }
116 }
78cd1d3b 117
1d768fb1 118 if (exists $options->{coerce} && $options->{coerce}) {
119 (exists $options->{type_constraint})
4b598ea3 120 || confess "You cannot have coercion without specifying a type constraint";
1d768fb1 121 (!$options->{type_constraint}->isa('Moose::Meta::TypeConstraint::Union'))
c07af9d2 122 || confess "You cannot have coercion with a type constraint union";
4b598ea3 123 confess "You cannot have a weak reference to a coerced value"
1d768fb1 124 if $options->{weak_ref};
ca01a97b 125 }
78cd1d3b 126
1d768fb1 127 if (exists $options->{lazy} && $options->{lazy}) {
128 (exists $options->{default})
ca01a97b 129 || confess "You cannot have lazy attribute without specifying a default value for it";
1d768fb1 130 }
78cd1d3b 131}
c0e30cf5 132
d500266f 133sub initialize_instance_slot {
134 my ($self, $class, $instance, $params) = @_;
135 my $init_arg = $self->init_arg();
136 # try to fetch the init arg from the %params ...
137 my $val;
138 if (exists $params->{$init_arg}) {
139 $val = $params->{$init_arg};
140 }
141 else {
142 # skip it if it's lazy
143 return if $self->is_lazy;
144 # and die if it's required and doesn't have a default value
145 confess "Attribute (" . $self->name . ") is required"
146 if $self->is_required && !$self->has_default;
147 }
148 # if nothing was in the %params, we can use the
149 # attribute's default value (if it has one)
150 if (!defined $val && $self->has_default) {
151 $val = $self->default($instance);
152 }
153 if (defined $val) {
154 if ($self->has_type_constraint) {
c07af9d2 155 my $type_constraint = $self->type_constraint;
156 if ($self->should_coerce && $type_constraint->has_coercion) {
157 $val = $type_constraint->coercion->coerce($val);
d500266f 158 }
c07af9d2 159 (defined($type_constraint->check($val)))
160 || confess "Attribute (" .
161 $self->name .
162 ") does not pass the type contraint (" .
163 $type_constraint->name .
164 ") with '$val'";
d500266f 165 }
166 }
167 $instance->{$self->name} = $val;
168 if (defined $val && $self->is_weak_ref) {
169 weaken($instance->{$self->name});
170 }
171}
172
a15dff8d 173sub generate_accessor_method {
174 my ($self, $attr_name) = @_;
ca01a97b 175 my $value_name = $self->should_coerce ? '$val' : '$_[1]';
176 my $code = 'sub { '
177 . 'if (scalar(@_) == 2) {'
178 . ($self->is_required ?
179 'defined($_[1]) || confess "Attribute ($attr_name) is required, so cannot be set to undef";'
180 : '')
181 . ($self->should_coerce ?
182 'my $val = $self->type_constraint->coercion->coerce($_[1]);'
183 : '')
184 . ($self->has_type_constraint ?
185 ('(defined $self->type_constraint->check(' . $value_name . '))'
c07af9d2 186 . '|| confess "Attribute ($attr_name) does not pass the type contraint (" . $self->type_constraint->name . ") with \'' . $value_name . '\'"'
ca01a97b 187 . 'if defined ' . $value_name . ';')
188 : '')
189 . '$_[0]->{$attr_name} = ' . $value_name . ';'
190 . ($self->is_weak_ref ?
191 'weaken($_[0]->{$attr_name});'
192 : '')
8c9d74e7 193 . ($self->has_trigger ?
daea75c9 194 '$self->trigger->($_[0], ' . $value_name . ', $self);'
8c9d74e7 195 : '')
ca01a97b 196 . ' }'
197 . ($self->is_lazy ?
198 '$_[0]->{$attr_name} = ($self->has_default ? $self->default($_[0]) : undef)'
199 . 'unless exists $_[0]->{$attr_name};'
200 : '')
201 . ' $_[0]->{$attr_name};'
202 . ' }';
203 my $sub = eval $code;
204 confess "Could not create writer for '$attr_name' because $@ \n code: $code" if $@;
205 return $sub;
a15dff8d 206}
207
208sub generate_writer_method {
209 my ($self, $attr_name) = @_;
ca01a97b 210 my $value_name = $self->should_coerce ? '$val' : '$_[1]';
211 my $code = 'sub { '
212 . ($self->is_required ?
213 'defined($_[1]) || confess "Attribute ($attr_name) is required, so cannot be set to undef";'
214 : '')
215 . ($self->should_coerce ?
216 'my $val = $self->type_constraint->coercion->coerce($_[1]);'
217 : '')
218 . ($self->has_type_constraint ?
219 ('(defined $self->type_constraint->check(' . $value_name . '))'
c07af9d2 220 . '|| confess "Attribute ($attr_name) does not pass the type contraint (" . $self->type_constraint->name . ") with \'' . $value_name . '\'"'
ca01a97b 221 . 'if defined ' . $value_name . ';')
222 : '')
223 . '$_[0]->{$attr_name} = ' . $value_name . ';'
224 . ($self->is_weak_ref ?
225 'weaken($_[0]->{$attr_name});'
226 : '')
8c9d74e7 227 . ($self->has_trigger ?
daea75c9 228 '$self->trigger->($_[0], ' . $value_name . ', $self);'
8c9d74e7 229 : '')
ca01a97b 230 . ' }';
231 my $sub = eval $code;
232 confess "Could not create writer for '$attr_name' because $@ \n code: $code" if $@;
233 return $sub;
a15dff8d 234}
c0e30cf5 235
d7f17ebb 236sub generate_reader_method {
237 my ($self, $attr_name) = @_;
ca01a97b 238 my $code = 'sub {'
239 . 'confess "Cannot assign a value to a read-only accessor" if @_ > 1;'
240 . ($self->is_lazy ?
241 '$_[0]->{$attr_name} = ($self->has_default ? $self->default($_[0]) : undef)'
242 . 'unless exists $_[0]->{$attr_name};'
243 : '')
244 . '$_[0]->{$attr_name};'
245 . '}';
246 my $sub = eval $code;
247 confess "Could not create reader for '$attr_name' because $@ \n code: $code" if $@;
248 return $sub;
d7f17ebb 249}
250
c0e30cf5 2511;
252
253__END__
254
255=pod
256
257=head1 NAME
258
6ba6d68c 259Moose::Meta::Attribute - The Moose attribute metaclass
c0e30cf5 260
261=head1 DESCRIPTION
262
e522431d 263This is a subclass of L<Class::MOP::Attribute> with Moose specific
6ba6d68c 264extensions.
265
266For the most part, the only time you will ever encounter an
267instance of this class is if you are doing some serious deep
268introspection. To really understand this class, you need to refer
269to the L<Class::MOP::Attribute> documentation.
e522431d 270
c0e30cf5 271=head1 METHODS
272
6ba6d68c 273=head2 Overridden methods
274
275These methods override methods in L<Class::MOP::Attribute> and add
276Moose specific features. You can safely assume though that they
277will behave just as L<Class::MOP::Attribute> does.
278
c0e30cf5 279=over 4
280
281=item B<new>
282
1d768fb1 283=item B<clone>
284
d500266f 285=item B<initialize_instance_slot>
286
a15dff8d 287=item B<generate_accessor_method>
288
289=item B<generate_writer_method>
290
d7f17ebb 291=item B<generate_reader_method>
292
a15dff8d 293=back
294
6ba6d68c 295=head2 Additional Moose features
296
297Moose attributes support type-contstraint checking, weak reference
298creation and type coercion.
299
a15dff8d 300=over 4
301
302=item B<has_type_constraint>
303
6ba6d68c 304Returns true if this meta-attribute has a type constraint.
305
a15dff8d 306=item B<type_constraint>
307
6ba6d68c 308A read-only accessor for this meta-attribute's type constraint. For
309more information on what you can do with this, see the documentation
310for L<Moose::Meta::TypeConstraint>.
a15dff8d 311
6ba6d68c 312=item B<is_weak_ref>
a15dff8d 313
02a0fb52 314Returns true if this meta-attribute produces a weak reference.
4b598ea3 315
ca01a97b 316=item B<is_required>
317
02a0fb52 318Returns true if this meta-attribute is required to have a value.
ca01a97b 319
320=item B<is_lazy>
321
02a0fb52 322Returns true if this meta-attribute should be initialized lazily.
ca01a97b 323
324NOTE: lazy attributes, B<must> have a C<default> field set.
325
34a66aa3 326=item B<should_coerce>
4b598ea3 327
02a0fb52 328Returns true if this meta-attribute should perform type coercion.
6ba6d68c 329
8c9d74e7 330=item B<has_trigger>
331
02a0fb52 332Returns true if this meta-attribute has a trigger set.
333
8c9d74e7 334=item B<trigger>
335
02a0fb52 336This is a CODE reference which will be executed every time the
337value of an attribute is assigned. The CODE ref will get two values,
338the invocant and the new value. This can be used to handle I<basic>
339bi-directional relations.
340
c0e30cf5 341=back
342
343=head1 BUGS
344
345All complex software has bugs lurking in it, and this module is no
346exception. If you find a bug please either email me, or add the bug
347to cpan-RT.
348
c0e30cf5 349=head1 AUTHOR
350
351Stevan Little E<lt>stevan@iinteractive.comE<gt>
352
353=head1 COPYRIGHT AND LICENSE
354
355Copyright 2006 by Infinity Interactive, Inc.
356
357L<http://www.iinteractive.com>
358
359This library is free software; you can redistribute it and/or modify
360it under the same terms as Perl itself.
361
362=cut