tweaking tests
[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;
9df136d0 27 my $type_constraint = $type_constraint_obj
28 ? $type_constraint_obj->_compiled_type_constraint
29 : undef;
c9183ffc 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")' .
9df136d0 179 ' if defined($default);' . "\n";
180 $code .= ' ' . $self->_inline_init_slot($attr, $inv, $slot_access, '$default') . "\n";
97e11ef5 181 }
182 else {
9df136d0 183 $code .= ' ' . $self->_inline_init_slot($attr, $inv, $slot_access, 'undef') . "\n";
26fbace8 184 }
185
3822d5ee 186 } else {
97e11ef5 187 if ($attr->has_default) {
9df136d0 188 $code .= ' ' . $self->_inline_init_slot($attr, $inv, $slot_access, ('$attr->default(' . $inv . ')')) . "\n";
97e11ef5 189 }
190 elsif ($attr->has_builder) {
9df136d0 191 $code .= ' if (my $builder = '.$inv.'->can($attr->builder)) { ' . "\n"
192 . ' ' . $self->_inline_init_slot($attr, $inv, $slot_access, ($inv . '->$builder'))
193 . "\n } else {\n" .
3822d5ee 194 ' confess(Scalar::Util::blessed('.$inv.')." does not support builder method '.
195 '\'".$attr->builder."\' for attribute \'" . $attr->name . "\'");'. "\n }";
97e11ef5 196 }
197 else {
9df136d0 198 $code .= ' ' . $self->_inline_init_slot($attr, $inv, $slot_access, 'undef') . "\n";
3822d5ee 199 }
200 }
201 $code .= "}\n";
202 return $code;
d617b644 203}
204
9df136d0 205sub _inline_init_slot {
206 my ($self, $attr, $inv, $slot_access, $value) = @_;
207 if ($attr->has_initializer) {
208 return ('$attr->set_initial_value(' . $inv . ', ' . $value . ');');
209 }
210 else {
211 return ($slot_access . ' = ' . $value . ';');
212 }
213}
d617b644 214
215sub _inline_store {
97e11ef5 216 my ($self, $instance, $value) = @_;
217 my $attr = $self->associated_attribute;
218
219 my $mi = $attr->associated_class->get_meta_instance;
220 my $slot_name = sprintf "'%s'", $attr->slots;
221
d617b644 222 my $code = $mi->inline_set_slot_value($instance, $slot_name, $value) . ";";
97e11ef5 223 $code .= $mi->inline_weaken_slot_value($instance, $slot_name, $value) . ";"
224 if $attr->is_weak_ref;
d617b644 225 return $code;
226}
227
228sub _inline_trigger {
97e11ef5 229 my ($self, $instance, $value) = @_;
230 my $attr = $self->associated_attribute;
231 return '' unless $attr->has_trigger;
232 return sprintf('$attr->trigger->(%s, %s, $attr);', $instance, $value);
d617b644 233}
234
235sub _inline_get {
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;
d617b644 241
242 return $mi->inline_get_slot_value($instance, $slot_name);
243}
244
e27dfc11 245sub _inline_access {
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_slot_access($instance, $slot_name);
253}
254
255sub _inline_has {
97e11ef5 256 my ($self, $instance) = @_;
257 my $attr = $self->associated_attribute;
258
259 my $mi = $attr->associated_class->get_meta_instance;
260 my $slot_name = sprintf "'%s'", $attr->slots;
e27dfc11 261
262 return $mi->inline_is_slot_initialized($instance, $slot_name);
263}
264
d617b644 265sub _inline_auto_deref {
266 my ( $self, $ref_value ) = @_;
26fbace8 267 my $attr = $self->associated_attribute;
d617b644 268
39b3bc94 269 return $ref_value unless $attr->should_auto_deref;
d617b644 270
39b3bc94 271 my $type_constraint = $attr->type_constraint;
d617b644 272
273 my $sigil;
274 if ($type_constraint->is_a_type_of('ArrayRef')) {
275 $sigil = '@';
26fbace8 276 }
d617b644 277 elsif ($type_constraint->is_a_type_of('HashRef')) {
278 $sigil = '%';
26fbace8 279 }
d617b644 280 else {
281 confess "Can not auto de-reference the type constraint '" . $type_constraint->name . "'";
282 }
283
284 "(wantarray() ? $sigil\{ ( $ref_value ) || return } : ( $ref_value ) )";
285}
8ee73eeb 286
2871;
288
289__END__
290
291=pod
292
39b3bc94 293=head1 NAME
294
ecb59493 295Moose::Meta::Method::Accessor - A Moose Method metaclass for accessors
39b3bc94 296
297=head1 DESCRIPTION
298
26fbace8 299This is a subclass of L<Class::MOP::Method::Accessor> and it's primary
300responsibility is to generate the accessor methods for attributes. It
ecb59493 301can handle both closure based accessors, as well as inlined source based
26fbace8 302accessors.
ecb59493 303
304This is a fairly new addition to the MOP, but this will play an important
305role in the optimization strategy we are currently following.
306
39b3bc94 307=head1 METHODS
308
309=over 4
310
8ecb1fa0 311=item B<generate_accessor_method>
312
313=item B<generate_reader_method>
314
315=item B<generate_writer_method>
316
39b3bc94 317=item B<generate_accessor_method_inline>
318
39b3bc94 319=item B<generate_reader_method_inline>
320
39b3bc94 321=item B<generate_writer_method_inline>
322
323=back
324
325=head1 BUGS
326
26fbace8 327All complex software has bugs lurking in it, and this module is no
39b3bc94 328exception. If you find a bug please either email me, or add the bug
329to cpan-RT.
330
331=head1 AUTHOR
332
333Stevan Little E<lt>stevan@iinteractive.comE<gt>
334
335Yuval Kogman E<lt>nothingmuch@woobling.comE<gt>
336
337=head1 COPYRIGHT AND LICENSE
338
778db3ac 339Copyright 2006-2008 by Infinity Interactive, Inc.
39b3bc94 340
341L<http://www.iinteractive.com>
342
343This library is free software; you can redistribute it and/or modify
344it under the same terms as Perl itself.
345
51308c23 346=cut