bug for immutable class that does a role
[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
330dbb07 9our $VERSION = '0.55';
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;
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;
33 confess "Could not create writer for '$attr_name' because $@ \n code: $code" if $@;
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"
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(@_)
c9183ffc 77 . ' }');
d617b644 78}
79
946289d1 80sub generate_reader_method_inline {
3ccdc84a 81 my $self = $_[0];
26fbace8 82 my $attr = $self->associated_attribute;
3ccdc84a 83 my $attr_name = $attr->name;
84 my $inv = '$_[0]';
85 my $slot_access = $self->_inline_get($inv, $attr_name);
26fbace8 86
c9183ffc 87 $self->_eval_code('sub {'
c350159f 88 . $self->_inline_pre_body(@_)
d617b644 89 . 'confess "Cannot assign a value to a read-only accessor" if @_ > 1;'
90 . $self->_inline_check_lazy
c350159f 91 . $self->_inline_post_body(@_)
3ccdc84a 92 . 'return ' . $self->_inline_auto_deref( $slot_access ) . ';'
c9183ffc 93 . '}');
d617b644 94}
95
ac211120 96sub _inline_copy_value {
97 return '' unless shift->_value_needs_copy;
98 return 'my $val = $_[1];'
99}
100
101sub _value_needs_copy {
102 my $attr = (shift)->associated_attribute;
103 return $attr->should_coerce;
104}
105
51308c23 106sub generate_reader_method { shift->generate_reader_method_inline(@_) }
107sub generate_writer_method { shift->generate_writer_method_inline(@_) }
108sub generate_accessor_method { shift->generate_accessor_method_inline(@_) }
8ecb1fa0 109
3ccdc84a 110sub _inline_pre_body { '' }
c350159f 111sub _inline_post_body { '' }
112
d617b644 113sub _inline_check_constraint {
97e11ef5 114 my ($self, $value) = @_;
115
116 my $attr = $self->associated_attribute;
8bb7da15 117 my $attr_name = $attr->name;
97e11ef5 118
119 return '' unless $attr->has_type_constraint;
120
8bb7da15 121 my $type_constraint_name = $attr->type_constraint->name;
122
97e11ef5 123 # FIXME
124 # This sprintf is insanely annoying, we should
125 # fix it someday - SL
688fcdda 126 return sprintf <<'EOF', $value, $attr_name, $value, $value,
7c4f0d32 127$type_constraint->(%s)
688fcdda 128 || confess "Attribute (%s) does not pass the type constraint because: "
ab76842e 129 . $type_constraint_obj->get_message(%s);
d617b644 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;
8bb7da15 142
143 my $attr_name = $attr->name;
97e11ef5 144
145 return '' unless $attr->is_required;
b6af66f8 146 return qq{(\@_ >= 2) || confess "Attribute ($attr_name) is required, so cannot be set to undef";} # defined $_[1] is not good enough
d617b644 147}
148
149sub _inline_check_lazy {
3ccdc84a 150 my $self = $_[0];
26fbace8 151 my $attr = $self->associated_attribute;
152
3822d5ee 153 return '' unless $attr->is_lazy;
26fbace8 154
3ccdc84a 155 my $inv = '$_[0]';
e27dfc11 156 my $slot_access = $self->_inline_access($inv, $attr->name);
3822d5ee 157
e27dfc11 158 my $slot_exists = $self->_inline_has($inv, $attr->name);
3822d5ee 159
160 my $code = 'unless (' . $slot_exists . ') {' . "\n";
161 if ($attr->has_type_constraint) {
97e11ef5 162 if ($attr->has_default || $attr->has_builder) {
163 if ($attr->has_default) {
3822d5ee 164 $code .= ' my $default = $attr->default(' . $inv . ');'."\n";
97e11ef5 165 }
166 elsif ($attr->has_builder) {
3822d5ee 167 $code .= ' my $default;'."\n".
168 ' if(my $builder = '.$inv.'->can($attr->builder)){ '."\n".
169 ' $default = '.$inv.'->$builder; '. "\n } else {\n" .
170 ' confess(Scalar::Util::blessed('.$inv.')." does not support builder method '.
171 '\'".$attr->builder."\' for attribute \'" . $attr->name . "\'");'. "\n }";
172 }
7c4f0d32 173 $code .= ' $default = $type_constraint_obj->coerce($default);'."\n" if $attr->should_coerce;
174 $code .= ' ($type_constraint->($default))' .
175 ' || confess "Attribute (" . $attr_name . ") does not pass the type constraint ("' .
ab76842e 176 ' . $type_constraint_name . ") with " . (defined($default) ? overload::StrVal($default) : "undef");'
177 . "\n";
9df136d0 178 $code .= ' ' . $self->_inline_init_slot($attr, $inv, $slot_access, '$default') . "\n";
97e11ef5 179 }
180 else {
9df136d0 181 $code .= ' ' . $self->_inline_init_slot($attr, $inv, $slot_access, 'undef') . "\n";
26fbace8 182 }
183
3822d5ee 184 } else {
97e11ef5 185 if ($attr->has_default) {
9df136d0 186 $code .= ' ' . $self->_inline_init_slot($attr, $inv, $slot_access, ('$attr->default(' . $inv . ')')) . "\n";
97e11ef5 187 }
188 elsif ($attr->has_builder) {
9df136d0 189 $code .= ' if (my $builder = '.$inv.'->can($attr->builder)) { ' . "\n"
190 . ' ' . $self->_inline_init_slot($attr, $inv, $slot_access, ($inv . '->$builder'))
191 . "\n } else {\n" .
3822d5ee 192 ' confess(Scalar::Util::blessed('.$inv.')." does not support builder method '.
193 '\'".$attr->builder."\' for attribute \'" . $attr->name . "\'");'. "\n }";
97e11ef5 194 }
195 else {
9df136d0 196 $code .= ' ' . $self->_inline_init_slot($attr, $inv, $slot_access, 'undef') . "\n";
3822d5ee 197 }
198 }
199 $code .= "}\n";
200 return $code;
d617b644 201}
202
9df136d0 203sub _inline_init_slot {
204 my ($self, $attr, $inv, $slot_access, $value) = @_;
205 if ($attr->has_initializer) {
206 return ('$attr->set_initial_value(' . $inv . ', ' . $value . ');');
207 }
208 else {
209 return ($slot_access . ' = ' . $value . ';');
210 }
211}
d617b644 212
213sub _inline_store {
97e11ef5 214 my ($self, $instance, $value) = @_;
215 my $attr = $self->associated_attribute;
97e11ef5 216 my $mi = $attr->associated_class->get_meta_instance;
217 my $slot_name = sprintf "'%s'", $attr->slots;
d617b644 218
c26a8868 219 my $gen_code = sub {
220 my ($ins_name, $val_name) = @_;
221
222 my $code = $mi->inline_set_slot_value(
223 $ins_name, $slot_name, $val_name) . ";\n";
224
225 if ($attr->is_weak_ref) {
226 $code .= $mi->inline_weaken_slot_value(
227 $ins_name, $slot_name, $val_name) . ";\n";
228 }
229
230 return $code;
231 };
232
233 if ($attr->can('_with_inline_triggers')) {
234 return $attr->_with_inline_triggers(
235 $instance, $value, '$attr', $gen_code);
236 }
237
238 return $gen_code->($instance, $value, '$attr');
d617b644 239}
240
241sub _inline_get {
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;
d617b644 247
248 return $mi->inline_get_slot_value($instance, $slot_name);
249}
250
e27dfc11 251sub _inline_access {
97e11ef5 252 my ($self, $instance) = @_;
253 my $attr = $self->associated_attribute;
254
255 my $mi = $attr->associated_class->get_meta_instance;
256 my $slot_name = sprintf "'%s'", $attr->slots;
e27dfc11 257
258 return $mi->inline_slot_access($instance, $slot_name);
259}
260
261sub _inline_has {
97e11ef5 262 my ($self, $instance) = @_;
263 my $attr = $self->associated_attribute;
264
265 my $mi = $attr->associated_class->get_meta_instance;
266 my $slot_name = sprintf "'%s'", $attr->slots;
e27dfc11 267
268 return $mi->inline_is_slot_initialized($instance, $slot_name);
269}
270
d617b644 271sub _inline_auto_deref {
272 my ( $self, $ref_value ) = @_;
26fbace8 273 my $attr = $self->associated_attribute;
d617b644 274
39b3bc94 275 return $ref_value unless $attr->should_auto_deref;
d617b644 276
39b3bc94 277 my $type_constraint = $attr->type_constraint;
d617b644 278
279 my $sigil;
280 if ($type_constraint->is_a_type_of('ArrayRef')) {
281 $sigil = '@';
26fbace8 282 }
d617b644 283 elsif ($type_constraint->is_a_type_of('HashRef')) {
284 $sigil = '%';
26fbace8 285 }
d617b644 286 else {
287 confess "Can not auto de-reference the type constraint '" . $type_constraint->name . "'";
288 }
289
290 "(wantarray() ? $sigil\{ ( $ref_value ) || return } : ( $ref_value ) )";
291}
8ee73eeb 292
2931;
294
295__END__
296
297=pod
298
39b3bc94 299=head1 NAME
300
ecb59493 301Moose::Meta::Method::Accessor - A Moose Method metaclass for accessors
39b3bc94 302
303=head1 DESCRIPTION
304
26fbace8 305This is a subclass of L<Class::MOP::Method::Accessor> and it's primary
306responsibility is to generate the accessor methods for attributes. It
ecb59493 307can handle both closure based accessors, as well as inlined source based
26fbace8 308accessors.
ecb59493 309
310This is a fairly new addition to the MOP, but this will play an important
311role in the optimization strategy we are currently following.
312
39b3bc94 313=head1 METHODS
314
315=over 4
316
8ecb1fa0 317=item B<generate_accessor_method>
318
319=item B<generate_reader_method>
320
321=item B<generate_writer_method>
322
39b3bc94 323=item B<generate_accessor_method_inline>
324
39b3bc94 325=item B<generate_reader_method_inline>
326
39b3bc94 327=item B<generate_writer_method_inline>
328
329=back
330
331=head1 BUGS
332
26fbace8 333All complex software has bugs lurking in it, and this module is no
39b3bc94 334exception. If you find a bug please either email me, or add the bug
335to cpan-RT.
336
337=head1 AUTHOR
338
339Stevan Little E<lt>stevan@iinteractive.comE<gt>
340
341Yuval Kogman E<lt>nothingmuch@woobling.comE<gt>
342
343=head1 COPYRIGHT AND LICENSE
344
778db3ac 345Copyright 2006-2008 by Infinity Interactive, Inc.
39b3bc94 346
347L<http://www.iinteractive.com>
348
349This library is free software; you can redistribute it and/or modify
350it under the same terms as Perl itself.
351
51308c23 352=cut