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