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