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