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