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