use new method names from cmop
[gitmo/Moose.git] / lib / Moose / Meta / Method / Accessor.pm
1
2 package Moose::Meta::Method::Accessor;
3
4 use strict;
5 use warnings;
6
7 use Try::Tiny;
8
9 our $VERSION   = '1.19';
10 $VERSION = eval $VERSION;
11 our $AUTHORITY = 'cpan:STEVAN';
12
13 use base 'Moose::Meta::Method',
14          'Class::MOP::Method::Accessor';
15
16 sub _error_thrower {
17     my $self = shift;
18     return $self->associated_attribute
19         if ref($self) && defined($self->associated_attribute);
20     return $self->SUPER::_error_thrower;
21 }
22
23 sub _compile_code {
24     my $self = shift;
25     my @args = @_;
26     try {
27         $self->SUPER::_compile_code(@args);
28     }
29     catch {
30         $self->throw_error(
31             'Could not create writer for '
32           . "'" . $self->associated_attribute->name . "' "
33           . 'because ' . $_,
34             error => $_,
35         );
36     };
37 }
38
39 sub _eval_environment {
40     my $self = shift;
41
42     my $attr                = $self->associated_attribute;
43     my $type_constraint_obj = $attr->type_constraint;
44
45     return {
46         '$attr'                => \$attr,
47         '$meta'                => \$self,
48         '$type_constraint_obj' => \$type_constraint_obj,
49         '$type_constraint'     => \(
50               $type_constraint_obj
51                   ? $type_constraint_obj->_compiled_type_constraint
52                   : undef
53         ),
54     };
55 }
56
57 sub _generate_accessor_method_inline {
58     my $self        = shift;
59
60     my $inv         = '$_[0]';
61     my $slot_access = $self->_get_value($inv);
62     my $value       = $self->_value_needs_copy ? '$val' : '$_[1]';
63     my $old         = '@old';
64
65     $self->_compile_code([
66         'sub {',
67             $self->_inline_pre_body(@_),
68             'if (scalar(@_) >= 2) {',
69                 $self->_inline_copy_value($value),
70                 $self->_inline_check_required,
71                 $self->_inline_tc_code($value),
72                 $self->_inline_get_old_value_for_trigger($inv, $old),
73                 $self->_inline_store_value($inv, $value),
74                 $self->_inline_trigger($inv, $value, $old),
75             '}',
76             $self->_inline_check_lazy($inv),
77             $self->_inline_post_body(@_),
78             $self->_inline_return_auto_deref($slot_access),
79         '}',
80     ]);
81 }
82
83 sub _generate_writer_method_inline {
84     my $self        = shift;
85
86     my $inv   = '$_[0]';
87     my $value = $self->_value_needs_copy ? '$val' : '$_[1]';
88     my $old   = '@old';
89
90     $self->_compile_code([
91         'sub {',
92             $self->_inline_pre_body(@_),
93             $self->_inline_copy_value($value),
94             $self->_inline_check_required,
95             $self->_inline_tc_code($value),
96             $self->_inline_get_old_value_for_trigger($inv, $old),
97             $self->_inline_store_value($inv, $value),
98             $self->_inline_post_body(@_),
99             $self->_inline_trigger($inv, $value, $old),
100         '}',
101     ]);
102 }
103
104 sub _generate_reader_method_inline {
105     my $self        = shift;
106
107     my $inv         = '$_[0]';
108     my $slot_access = $self->_get_value($inv);
109
110     $self->_compile_code([
111         'sub {',
112             $self->_inline_pre_body(@_),
113             'if (@_ > 1) {',
114                 $self->_inline_throw_error(
115                     '"Cannot assign a value to a read-only accessor"',
116                     'data => \@_'
117                 ) . ';',
118             '}',
119             $self->_inline_check_lazy($inv),
120             $self->_inline_post_body(@_),
121             $self->_inline_return_auto_deref($slot_access),
122         '}',
123     ]);
124 }
125
126 sub _inline_copy_value {
127     my $self = shift;
128     my ($value) = @_;
129
130     return unless $self->_value_needs_copy;
131     return 'my ' . $value . ' = $_[1];'
132 }
133
134 sub _value_needs_copy {
135     my $self = shift;
136     return $self->associated_attribute->should_coerce;
137 }
138
139 sub _instance_is_inlinable {
140     my $self = shift;
141     return $self->associated_attribute->associated_class->instance_metaclass->is_inlinable;
142 }
143
144 sub _generate_reader_method {
145     my $self = shift;
146     $self->_instance_is_inlinable ? $self->_generate_reader_method_inline(@_)
147                                   : $self->SUPER::_generate_reader_method(@_);
148 }
149
150 sub _generate_writer_method {
151     my $self = shift;
152     $self->_instance_is_inlinable ? $self->_generate_writer_method_inline(@_)
153                                   : $self->SUPER::_generate_writer_method(@_);
154 }
155
156 sub _generate_accessor_method {
157     my $self = shift;
158     $self->_instance_is_inlinable ? $self->_generate_accessor_method_inline(@_)
159                                   : $self->SUPER::_generate_accessor_method(@_);
160 }
161
162 sub _generate_predicate_method {
163     my $self = shift;
164     $self->_instance_is_inlinable ? $self->_generate_predicate_method_inline(@_)
165                                   : $self->SUPER::_generate_predicate_method(@_);
166 }
167
168 sub _generate_clearer_method {
169     my $self = shift;
170     $self->_instance_is_inlinable ? $self->_generate_clearer_method_inline(@_)
171                                   : $self->SUPER::_generate_clearer_method(@_);
172 }
173
174 sub _inline_pre_body  { return }
175 sub _inline_post_body { return }
176
177 sub _inline_tc_code {
178     my $self = shift;
179     return (
180         $self->_inline_check_coercion(@_),
181         $self->_inline_check_constraint(@_),
182     );
183 }
184
185 sub _inline_check_constraint {
186     my $self = shift;
187     my ($value) = @_;
188
189     my $attr = $self->associated_attribute;
190     return unless $attr->has_type_constraint;
191
192     my $attr_name = quotemeta($attr->name);
193
194     return (
195         'if (!$type_constraint->(' . $value . ')) {',
196             $self->_inline_throw_error(
197                 '"Attribute (' . $attr_name . ') does not pass the type '
198               . 'constraint because: " . '
199               . '$type_constraint_obj->get_message(' . $value . ')',
200                 'data => ' . $value
201             ) . ';',
202         '}',
203     );
204 }
205
206 sub _inline_check_coercion {
207     my $self = shift;
208     my ($value) = @_;
209
210     my $attr = $self->associated_attribute;
211     return unless $attr->should_coerce && $attr->type_constraint->has_coercion;
212
213     return $value . ' = $type_constraint_obj->coerce(' . $value . ');';
214 }
215
216 sub _inline_check_required {
217     my $self = shift;
218
219     my $attr = $self->associated_attribute;
220     return unless $attr->is_required;
221
222     my $attr_name = quotemeta($attr->name);
223
224     return (
225         'if (@_ < 2) {',
226             $self->_inline_throw_error(
227                 '"Attribute (' . $attr_name . ') is required, so cannot '
228               . 'be set to undef"' # defined $_[1] is not good enough
229             ) . ';',
230         '}',
231     );
232 }
233
234 sub _inline_check_lazy {
235     my $self = shift;
236     my ($instance, $default) = @_;
237
238     my $attr = $self->associated_attribute;
239     return unless $attr->is_lazy;
240
241     my $slot_exists = $self->_has_value($instance);
242
243     return (
244         'if (!' . $slot_exists . ') {',
245             $self->_inline_init_from_default($instance, '$default', 'lazy'),
246         '}',
247     );
248 }
249
250 sub _inline_init_from_default {
251     my $self = shift;
252     my ($instance, $default, $for_lazy) = @_;
253
254     my $attr = $self->associated_attribute;
255
256     if (!($attr->has_default || $attr->has_builder)) {
257         $self->throw_error(
258             'You cannot have a lazy attribute '
259           . '(' . $attr->name . ') '
260           . 'without specifying a default value for it',
261             attr => $attr,
262         );
263     }
264
265     return (
266         $self->_inline_generate_default($instance, $default),
267         # intentionally not using _inline_tc_code, since that can be overridden
268         # to do things like possibly only do member tc checks, which isn't
269         # appropriate for checking the result of a default
270         $attr->has_type_constraint
271             ? ($self->_inline_check_coercion($default, $for_lazy),
272                $self->_inline_check_constraint($default, $for_lazy))
273             : (),
274         $self->_inline_init_slot($attr, $instance, $default),
275     );
276 }
277
278 sub _inline_generate_default {
279     my $self = shift;
280     my ($instance, $default) = @_;
281
282     my $attr = $self->associated_attribute;
283
284     if ($attr->has_default) {
285         return 'my ' . $default . ' = $attr->default(' . $instance . ');';
286     }
287     elsif ($attr->has_builder) {
288         return (
289             'my ' . $default . ';',
290             'if (my $builder = ' . $instance . '->can($attr->builder)) {',
291                 $default . ' = ' . $instance . '->$builder;',
292             '}',
293             'else {',
294                 'my $class = ref(' . $instance . ') || ' . $instance . ';',
295                 'my $builder_name = $attr->builder;',
296                 'my $attr_name = $attr->name;',
297                 $self->_inline_throw_error(
298                     '"$class does not support builder method '
299                   . '\'$builder_name\' for attribute \'$attr_name\'"'
300                 ) . ';',
301             '}',
302         );
303     }
304     else {
305         $self->throw_error(
306             "Can't generate a default for " . $attr->name
307           . " since no default or builder was specified"
308         );
309     }
310 }
311
312 sub _inline_init_slot {
313     my $self = shift;
314     my ($attr, $inv, $value) = @_;
315
316     if ($attr->has_initializer) {
317         return '$attr->set_initial_value(' . $inv . ', ' . $value . ');';
318     }
319     else {
320         return $self->_inline_store_value($inv, $value);
321     }
322 }
323
324 sub _inline_store_value {
325     my $self = shift;
326     my ($inv, $value) = @_;
327
328     return $self->associated_attribute->_inline_set_value($inv, $value);
329 }
330
331 sub _inline_get_old_value_for_trigger {
332     my $self = shift;
333     my ($instance, $old) = @_;
334
335     my $attr = $self->associated_attribute;
336     return unless $attr->has_trigger;
337
338     return (
339         'my ' . $old . ' = ' . $self->_has_value($instance),
340             '? ' . $self->_get_value($instance),
341             ': ();',
342     );
343 }
344
345 sub _inline_trigger {
346     my $self = shift;
347     my ($instance, $value, $old) = @_;
348
349     my $attr = $self->associated_attribute;
350     return unless $attr->has_trigger;
351
352     return '$attr->trigger->(' . $instance . ', ' . $value . ', ' . $old . ');';
353 }
354
355 sub _inline_return_auto_deref {
356     my $self = shift;
357
358     return 'return ' . $self->_auto_deref(@_) . ';';
359 }
360
361 # expressions
362
363 sub _get_value {
364     my ($self, $instance) = @_;
365
366     return $self->associated_attribute->_inline_instance_get($instance);
367 }
368
369 sub _has_value {
370     my ($self, $instance) = @_;
371
372     return $self->associated_attribute->_inline_instance_has($instance);
373 }
374
375 sub _auto_deref {
376     my $self = shift;
377     my ($ref_value) = @_;
378
379     my $attr = $self->associated_attribute;
380     return $ref_value unless $attr->should_auto_deref;
381
382     my $type_constraint = $attr->type_constraint;
383
384     my $sigil;
385     if ($type_constraint->is_a_type_of('ArrayRef')) {
386         $sigil = '@';
387     }
388     elsif ($type_constraint->is_a_type_of('HashRef')) {
389         $sigil = '%';
390     }
391     else {
392         $self->throw_error(
393             'Can not auto de-reference the type constraint \''
394           . $type_constraint->name
395           . '\'',
396             type_constraint => $type_constraint,
397         );
398     }
399
400     return 'wantarray '
401              . '? ' . $sigil . '{ (' . $ref_value . ') || return } '
402              . ': (' . $ref_value . ')';
403 }
404
405 1;
406
407 __END__
408
409 =pod
410
411 =head1 NAME
412
413 Moose::Meta::Method::Accessor - A Moose Method metaclass for accessors
414
415 =head1 DESCRIPTION
416
417 This class is a subclass of L<Class::MOP::Method::Accessor> that
418 provides additional Moose-specific functionality, all of which is
419 private.
420
421 To understand this class, you should read the the
422 L<Class::MOP::Method::Accessor> documentation.
423
424 =head1 BUGS
425
426 See L<Moose/BUGS> for details on reporting bugs.
427
428 =head1 AUTHOR
429
430 Stevan Little E<lt>stevan@iinteractive.comE<gt>
431
432 Yuval Kogman E<lt>nothingmuch@woobling.comE<gt>
433
434 =head1 COPYRIGHT AND LICENSE
435
436 Copyright 2006-2010 by Infinity Interactive, Inc.
437
438 L<http://www.iinteractive.com>
439
440 This library is free software; you can redistribute it and/or modify
441 it under the same terms as Perl itself.
442
443 =cut