correct usage of compiled_type_constraint
[gitmo/Moose.git] / lib / Moose / Meta / Method / Accessor.pm
CommitLineData
8ee73eeb 1
2package Moose::Meta::Method::Accessor;
3
4use strict;
5use warnings;
6
39b3bc94 7use Carp 'confess';
8
a4e516f6 9our $VERSION = '0.10';
d44714be 10our $AUTHORITY = 'cpan:STEVAN';
8ee73eeb 11
39b3bc94 12use base 'Moose::Meta::Method',
d617b644 13 'Class::MOP::Method::Accessor';
14
946289d1 15## Inline method generators
d617b644 16
c9183ffc 17sub _eval_code {
18 my ( $self, $code ) = @_;
19
20 # NOTE:
21 # set up the environment
22 my $attr = $self->associated_attribute;
23 my $attr_name = $attr->name;
24
25 my $type_constraint_obj = $attr->type_constraint;
26 my $type_constraint_name = $type_constraint_obj && $type_constraint_obj->name;
27 my $type_constraint = $type_constraint_obj
5e02366b 28 ? $type_constraint_obj->_compiled_type_constraint
c9183ffc 29 : undef;
30
31 my $sub = eval $code;
32 confess "Could not create writer for '$attr_name' because $@ \n code: $code" if $@;
33 return $sub;
34
35}
36
946289d1 37sub generate_accessor_method_inline {
3ccdc84a 38 my $self = $_[0];
26fbace8 39 my $attr = $self->associated_attribute;
3ccdc84a 40 my $attr_name = $attr->name;
41 my $inv = '$_[0]';
e27dfc11 42 my $slot_access = $self->_inline_access($inv, $attr_name);
ac211120 43 my $value_name = $self->_value_needs_copy ? '$val' : '$_[1]';
d617b644 44
c9183ffc 45 $self->_eval_code('sub { ' . "\n"
e27dfc11 46 . $self->_inline_pre_body(@_) . "\n"
e7c06c1e 47 . 'if (scalar(@_) >= 2) {' . "\n"
ac211120 48 . $self->_inline_copy_value . "\n"
e27dfc11 49 . $self->_inline_check_required . "\n"
50 . $self->_inline_check_coercion . "\n"
51 . $self->_inline_check_constraint($value_name) . "\n"
32dd4a95 52 . $self->_inline_store($inv, $value_name) . "\n"
53 . $self->_inline_trigger($inv, $value_name) . "\n"
e27dfc11 54 . ' }' . "\n"
55 . $self->_inline_check_lazy . "\n"
56 . $self->_inline_post_body(@_) . "\n"
57 . 'return ' . $self->_inline_auto_deref($self->_inline_get($inv)) . "\n"
c9183ffc 58 . ' }');
d617b644 59}
60
946289d1 61sub generate_writer_method_inline {
3ccdc84a 62 my $self = $_[0];
26fbace8 63 my $attr = $self->associated_attribute;
3ccdc84a 64 my $attr_name = $attr->name;
65 my $inv = '$_[0]';
66 my $slot_access = $self->_inline_get($inv, $attr_name);
ac211120 67 my $value_name = $self->_value_needs_copy ? '$val' : '$_[1]';
3ccdc84a 68
c9183ffc 69 $self->_eval_code('sub { '
c350159f 70 . $self->_inline_pre_body(@_)
ac211120 71 . $self->_inline_copy_value
d617b644 72 . $self->_inline_check_required
73 . $self->_inline_check_coercion
32dd4a95 74 . $self->_inline_check_constraint($value_name)
75 . $self->_inline_store($inv, $value_name)
76 . $self->_inline_post_body(@_)
77 . $self->_inline_trigger($inv, $value_name)
c9183ffc 78 . ' }');
d617b644 79}
80
946289d1 81sub generate_reader_method_inline {
3ccdc84a 82 my $self = $_[0];
26fbace8 83 my $attr = $self->associated_attribute;
3ccdc84a 84 my $attr_name = $attr->name;
85 my $inv = '$_[0]';
86 my $slot_access = $self->_inline_get($inv, $attr_name);
26fbace8 87
c9183ffc 88 $self->_eval_code('sub {'
c350159f 89 . $self->_inline_pre_body(@_)
d617b644 90 . 'confess "Cannot assign a value to a read-only accessor" if @_ > 1;'
91 . $self->_inline_check_lazy
c350159f 92 . $self->_inline_post_body(@_)
3ccdc84a 93 . 'return ' . $self->_inline_auto_deref( $slot_access ) . ';'
c9183ffc 94 . '}');
d617b644 95}
96
ac211120 97sub _inline_copy_value {
98 return '' unless shift->_value_needs_copy;
99 return 'my $val = $_[1];'
100}
101
102sub _value_needs_copy {
103 my $attr = (shift)->associated_attribute;
104 return $attr->should_coerce;
105}
106
51308c23 107sub generate_reader_method { shift->generate_reader_method_inline(@_) }
108sub generate_writer_method { shift->generate_writer_method_inline(@_) }
109sub generate_accessor_method { shift->generate_accessor_method_inline(@_) }
8ecb1fa0 110
3ccdc84a 111sub _inline_pre_body { '' }
c350159f 112sub _inline_post_body { '' }
113
d617b644 114sub _inline_check_constraint {
97e11ef5 115 my ($self, $value) = @_;
116
117 my $attr = $self->associated_attribute;
118
119 return '' unless $attr->has_type_constraint;
120
121 # FIXME
122 # This sprintf is insanely annoying, we should
123 # fix it someday - SL
124 return sprintf <<'EOF', $value, $value, $value, $value, $value, $value, $value
7c4f0d32 125$type_constraint->(%s)
126 || confess "Attribute (" . $attr_name . ") does not pass the type constraint ("
127 . $type_constraint_name . ") with "
6361ccf5 128 . (defined(%s) ? overload::StrVal(%s) : "undef")
d617b644 129 if defined(%s);
130EOF
131}
132
133sub _inline_check_coercion {
97e11ef5 134 my $attr = (shift)->associated_attribute;
135
136 return '' unless $attr->should_coerce;
ac211120 137 return '$val = $attr->type_constraint->coerce($_[1]);'
d617b644 138}
139
140sub _inline_check_required {
97e11ef5 141 my $attr = (shift)->associated_attribute;
142
143 return '' unless $attr->is_required;
d617b644 144 return 'defined($_[1]) || confess "Attribute ($attr_name) is required, so cannot be set to undef";'
145}
146
147sub _inline_check_lazy {
3ccdc84a 148 my $self = $_[0];
26fbace8 149 my $attr = $self->associated_attribute;
150
3822d5ee 151 return '' unless $attr->is_lazy;
26fbace8 152
3ccdc84a 153 my $inv = '$_[0]';
e27dfc11 154 my $slot_access = $self->_inline_access($inv, $attr->name);
3822d5ee 155
e27dfc11 156 my $slot_exists = $self->_inline_has($inv, $attr->name);
3822d5ee 157
158 my $code = 'unless (' . $slot_exists . ') {' . "\n";
159 if ($attr->has_type_constraint) {
97e11ef5 160 if ($attr->has_default || $attr->has_builder) {
161 if ($attr->has_default) {
3822d5ee 162 $code .= ' my $default = $attr->default(' . $inv . ');'."\n";
97e11ef5 163 }
164 elsif ($attr->has_builder) {
3822d5ee 165 $code .= ' my $default;'."\n".
166 ' if(my $builder = '.$inv.'->can($attr->builder)){ '."\n".
167 ' $default = '.$inv.'->$builder; '. "\n } else {\n" .
168 ' confess(Scalar::Util::blessed('.$inv.')." does not support builder method '.
169 '\'".$attr->builder."\' for attribute \'" . $attr->name . "\'");'. "\n }";
170 }
7c4f0d32 171 $code .= ' $default = $type_constraint_obj->coerce($default);'."\n" if $attr->should_coerce;
172 $code .= ' ($type_constraint->($default))' .
173 ' || confess "Attribute (" . $attr_name . ") does not pass the type constraint ("' .
6361ccf5 174 ' . $type_constraint_name . ") with " . (defined($default) ? overload::StrVal($default) : "undef")' .
3822d5ee 175 ' if defined($default);' . "\n" .
176 ' ' . $slot_access . ' = $default; ' . "\n";
97e11ef5 177 }
178 else {
3822d5ee 179 $code .= ' ' . $slot_access . " = undef; \n";
26fbace8 180 }
181
3822d5ee 182 } else {
97e11ef5 183 if ($attr->has_default) {
3822d5ee 184 $code .= ' '.$slot_access.' = $attr->default(' . $inv . ');'."\n";
97e11ef5 185 }
186 elsif ($attr->has_builder) {
3822d5ee 187 $code .= ' if(my $builder = '.$inv.'->can($attr->builder)){ '."\n".
188 ' '.$slot_access.' = '.$inv.'->$builder; '. "\n } else {\n" .
189 ' confess(Scalar::Util::blessed('.$inv.')." does not support builder method '.
190 '\'".$attr->builder."\' for attribute \'" . $attr->name . "\'");'. "\n }";
97e11ef5 191 }
192 else {
3822d5ee 193 $code .= ' ' . $slot_access . " = undef; \n";
194 }
195 }
196 $code .= "}\n";
197 return $code;
d617b644 198}
199
200
201sub _inline_store {
97e11ef5 202 my ($self, $instance, $value) = @_;
203 my $attr = $self->associated_attribute;
204
205 my $mi = $attr->associated_class->get_meta_instance;
206 my $slot_name = sprintf "'%s'", $attr->slots;
207
d617b644 208 my $code = $mi->inline_set_slot_value($instance, $slot_name, $value) . ";";
97e11ef5 209 $code .= $mi->inline_weaken_slot_value($instance, $slot_name, $value) . ";"
210 if $attr->is_weak_ref;
d617b644 211 return $code;
212}
213
214sub _inline_trigger {
97e11ef5 215 my ($self, $instance, $value) = @_;
216 my $attr = $self->associated_attribute;
217 return '' unless $attr->has_trigger;
218 return sprintf('$attr->trigger->(%s, %s, $attr);', $instance, $value);
d617b644 219}
220
221sub _inline_get {
97e11ef5 222 my ($self, $instance) = @_;
223 my $attr = $self->associated_attribute;
224
225 my $mi = $attr->associated_class->get_meta_instance;
226 my $slot_name = sprintf "'%s'", $attr->slots;
d617b644 227
228 return $mi->inline_get_slot_value($instance, $slot_name);
229}
230
e27dfc11 231sub _inline_access {
97e11ef5 232 my ($self, $instance) = @_;
233 my $attr = $self->associated_attribute;
234
235 my $mi = $attr->associated_class->get_meta_instance;
236 my $slot_name = sprintf "'%s'", $attr->slots;
e27dfc11 237
238 return $mi->inline_slot_access($instance, $slot_name);
239}
240
241sub _inline_has {
97e11ef5 242 my ($self, $instance) = @_;
243 my $attr = $self->associated_attribute;
244
245 my $mi = $attr->associated_class->get_meta_instance;
246 my $slot_name = sprintf "'%s'", $attr->slots;
e27dfc11 247
248 return $mi->inline_is_slot_initialized($instance, $slot_name);
249}
250
d617b644 251sub _inline_auto_deref {
252 my ( $self, $ref_value ) = @_;
26fbace8 253 my $attr = $self->associated_attribute;
d617b644 254
39b3bc94 255 return $ref_value unless $attr->should_auto_deref;
d617b644 256
39b3bc94 257 my $type_constraint = $attr->type_constraint;
d617b644 258
259 my $sigil;
260 if ($type_constraint->is_a_type_of('ArrayRef')) {
261 $sigil = '@';
26fbace8 262 }
d617b644 263 elsif ($type_constraint->is_a_type_of('HashRef')) {
264 $sigil = '%';
26fbace8 265 }
d617b644 266 else {
267 confess "Can not auto de-reference the type constraint '" . $type_constraint->name . "'";
268 }
269
270 "(wantarray() ? $sigil\{ ( $ref_value ) || return } : ( $ref_value ) )";
271}
8ee73eeb 272
2731;
274
275__END__
276
277=pod
278
39b3bc94 279=head1 NAME
280
ecb59493 281Moose::Meta::Method::Accessor - A Moose Method metaclass for accessors
39b3bc94 282
283=head1 DESCRIPTION
284
26fbace8 285This is a subclass of L<Class::MOP::Method::Accessor> and it's primary
286responsibility is to generate the accessor methods for attributes. It
ecb59493 287can handle both closure based accessors, as well as inlined source based
26fbace8 288accessors.
ecb59493 289
290This is a fairly new addition to the MOP, but this will play an important
291role in the optimization strategy we are currently following.
292
39b3bc94 293=head1 METHODS
294
295=over 4
296
8ecb1fa0 297=item B<generate_accessor_method>
298
299=item B<generate_reader_method>
300
301=item B<generate_writer_method>
302
39b3bc94 303=item B<generate_accessor_method_inline>
304
39b3bc94 305=item B<generate_reader_method_inline>
306
39b3bc94 307=item B<generate_writer_method_inline>
308
309=back
310
311=head1 BUGS
312
26fbace8 313All complex software has bugs lurking in it, and this module is no
39b3bc94 314exception. If you find a bug please either email me, or add the bug
315to cpan-RT.
316
317=head1 AUTHOR
318
319Stevan Little E<lt>stevan@iinteractive.comE<gt>
320
321Yuval Kogman E<lt>nothingmuch@woobling.comE<gt>
322
323=head1 COPYRIGHT AND LICENSE
324
778db3ac 325Copyright 2006-2008 by Infinity Interactive, Inc.
39b3bc94 326
327L<http://www.iinteractive.com>
328
329This library is free software; you can redistribute it and/or modify
330it under the same terms as Perl itself.
331
51308c23 332=cut