failing test for two subclasses of the same non-Moose class with different metaclasses
[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
688fcdda 9our $VERSION = '0.12';
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;
8bb7da15 118 my $attr_name = $attr->name;
97e11ef5 119
120 return '' unless $attr->has_type_constraint;
121
8bb7da15 122 my $type_constraint_name = $attr->type_constraint->name;
123
97e11ef5 124 # FIXME
125 # This sprintf is insanely annoying, we should
126 # fix it someday - SL
688fcdda 127 return sprintf <<'EOF', $value, $attr_name, $value, $value,
7c4f0d32 128$type_constraint->(%s)
688fcdda 129 || confess "Attribute (%s) does not pass the type constraint because: "
130 . $type_constraint_obj->get_message(%s)
d617b644 131 if defined(%s);
132EOF
133}
134
135sub _inline_check_coercion {
97e11ef5 136 my $attr = (shift)->associated_attribute;
137
138 return '' unless $attr->should_coerce;
ac211120 139 return '$val = $attr->type_constraint->coerce($_[1]);'
d617b644 140}
141
142sub _inline_check_required {
97e11ef5 143 my $attr = (shift)->associated_attribute;
8bb7da15 144
145 my $attr_name = $attr->name;
97e11ef5 146
147 return '' unless $attr->is_required;
8bb7da15 148 return qq{defined(\$_[1]) || confess "Attribute ($attr_name) is required, so cannot be set to undef";}
d617b644 149}
150
151sub _inline_check_lazy {
3ccdc84a 152 my $self = $_[0];
26fbace8 153 my $attr = $self->associated_attribute;
154
3822d5ee 155 return '' unless $attr->is_lazy;
26fbace8 156
3ccdc84a 157 my $inv = '$_[0]';
e27dfc11 158 my $slot_access = $self->_inline_access($inv, $attr->name);
3822d5ee 159
e27dfc11 160 my $slot_exists = $self->_inline_has($inv, $attr->name);
3822d5ee 161
162 my $code = 'unless (' . $slot_exists . ') {' . "\n";
163 if ($attr->has_type_constraint) {
97e11ef5 164 if ($attr->has_default || $attr->has_builder) {
165 if ($attr->has_default) {
3822d5ee 166 $code .= ' my $default = $attr->default(' . $inv . ');'."\n";
97e11ef5 167 }
168 elsif ($attr->has_builder) {
3822d5ee 169 $code .= ' my $default;'."\n".
170 ' if(my $builder = '.$inv.'->can($attr->builder)){ '."\n".
171 ' $default = '.$inv.'->$builder; '. "\n } else {\n" .
172 ' confess(Scalar::Util::blessed('.$inv.')." does not support builder method '.
173 '\'".$attr->builder."\' for attribute \'" . $attr->name . "\'");'. "\n }";
174 }
7c4f0d32 175 $code .= ' $default = $type_constraint_obj->coerce($default);'."\n" if $attr->should_coerce;
176 $code .= ' ($type_constraint->($default))' .
177 ' || confess "Attribute (" . $attr_name . ") does not pass the type constraint ("' .
6361ccf5 178 ' . $type_constraint_name . ") with " . (defined($default) ? overload::StrVal($default) : "undef")' .
3822d5ee 179 ' if defined($default);' . "\n" .
180 ' ' . $slot_access . ' = $default; ' . "\n";
97e11ef5 181 }
182 else {
3822d5ee 183 $code .= ' ' . $slot_access . " = undef; \n";
26fbace8 184 }
185
3822d5ee 186 } else {
97e11ef5 187 if ($attr->has_default) {
3822d5ee 188 $code .= ' '.$slot_access.' = $attr->default(' . $inv . ');'."\n";
97e11ef5 189 }
190 elsif ($attr->has_builder) {
3822d5ee 191 $code .= ' if(my $builder = '.$inv.'->can($attr->builder)){ '."\n".
192 ' '.$slot_access.' = '.$inv.'->$builder; '. "\n } else {\n" .
193 ' confess(Scalar::Util::blessed('.$inv.')." does not support builder method '.
194 '\'".$attr->builder."\' for attribute \'" . $attr->name . "\'");'. "\n }";
97e11ef5 195 }
196 else {
3822d5ee 197 $code .= ' ' . $slot_access . " = undef; \n";
198 }
199 }
200 $code .= "}\n";
201 return $code;
d617b644 202}
203
204
205sub _inline_store {
97e11ef5 206 my ($self, $instance, $value) = @_;
207 my $attr = $self->associated_attribute;
208
209 my $mi = $attr->associated_class->get_meta_instance;
210 my $slot_name = sprintf "'%s'", $attr->slots;
211
d617b644 212 my $code = $mi->inline_set_slot_value($instance, $slot_name, $value) . ";";
97e11ef5 213 $code .= $mi->inline_weaken_slot_value($instance, $slot_name, $value) . ";"
214 if $attr->is_weak_ref;
d617b644 215 return $code;
216}
217
218sub _inline_trigger {
97e11ef5 219 my ($self, $instance, $value) = @_;
220 my $attr = $self->associated_attribute;
221 return '' unless $attr->has_trigger;
222 return sprintf('$attr->trigger->(%s, %s, $attr);', $instance, $value);
d617b644 223}
224
225sub _inline_get {
97e11ef5 226 my ($self, $instance) = @_;
227 my $attr = $self->associated_attribute;
228
229 my $mi = $attr->associated_class->get_meta_instance;
230 my $slot_name = sprintf "'%s'", $attr->slots;
d617b644 231
232 return $mi->inline_get_slot_value($instance, $slot_name);
233}
234
e27dfc11 235sub _inline_access {
97e11ef5 236 my ($self, $instance) = @_;
237 my $attr = $self->associated_attribute;
238
239 my $mi = $attr->associated_class->get_meta_instance;
240 my $slot_name = sprintf "'%s'", $attr->slots;
e27dfc11 241
242 return $mi->inline_slot_access($instance, $slot_name);
243}
244
245sub _inline_has {
97e11ef5 246 my ($self, $instance) = @_;
247 my $attr = $self->associated_attribute;
248
249 my $mi = $attr->associated_class->get_meta_instance;
250 my $slot_name = sprintf "'%s'", $attr->slots;
e27dfc11 251
252 return $mi->inline_is_slot_initialized($instance, $slot_name);
253}
254
d617b644 255sub _inline_auto_deref {
256 my ( $self, $ref_value ) = @_;
26fbace8 257 my $attr = $self->associated_attribute;
d617b644 258
39b3bc94 259 return $ref_value unless $attr->should_auto_deref;
d617b644 260
39b3bc94 261 my $type_constraint = $attr->type_constraint;
d617b644 262
263 my $sigil;
264 if ($type_constraint->is_a_type_of('ArrayRef')) {
265 $sigil = '@';
26fbace8 266 }
d617b644 267 elsif ($type_constraint->is_a_type_of('HashRef')) {
268 $sigil = '%';
26fbace8 269 }
d617b644 270 else {
271 confess "Can not auto de-reference the type constraint '" . $type_constraint->name . "'";
272 }
273
274 "(wantarray() ? $sigil\{ ( $ref_value ) || return } : ( $ref_value ) )";
275}
8ee73eeb 276
2771;
278
279__END__
280
281=pod
282
39b3bc94 283=head1 NAME
284
ecb59493 285Moose::Meta::Method::Accessor - A Moose Method metaclass for accessors
39b3bc94 286
287=head1 DESCRIPTION
288
26fbace8 289This is a subclass of L<Class::MOP::Method::Accessor> and it's primary
290responsibility is to generate the accessor methods for attributes. It
ecb59493 291can handle both closure based accessors, as well as inlined source based
26fbace8 292accessors.
ecb59493 293
294This is a fairly new addition to the MOP, but this will play an important
295role in the optimization strategy we are currently following.
296
39b3bc94 297=head1 METHODS
298
299=over 4
300
8ecb1fa0 301=item B<generate_accessor_method>
302
303=item B<generate_reader_method>
304
305=item B<generate_writer_method>
306
39b3bc94 307=item B<generate_accessor_method_inline>
308
39b3bc94 309=item B<generate_reader_method_inline>
310
39b3bc94 311=item B<generate_writer_method_inline>
312
313=back
314
315=head1 BUGS
316
26fbace8 317All complex software has bugs lurking in it, and this module is no
39b3bc94 318exception. If you find a bug please either email me, or add the bug
319to cpan-RT.
320
321=head1 AUTHOR
322
323Stevan Little E<lt>stevan@iinteractive.comE<gt>
324
325Yuval Kogman E<lt>nothingmuch@woobling.comE<gt>
326
327=head1 COPYRIGHT AND LICENSE
328
778db3ac 329Copyright 2006-2008 by Infinity Interactive, Inc.
39b3bc94 330
331L<http://www.iinteractive.com>
332
333This library is free software; you can redistribute it and/or modify
334it under the same terms as Perl itself.
335
51308c23 336=cut