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