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