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