Release commit for 0.091012
[gitmo/Moo.git] / lib / Method / Generate / Accessor.pm
CommitLineData
51a3b106 1package Method::Generate::Accessor;
2
3use strictures 1;
b1eebd55 4use Moo::_Utils;
5use base qw(Moo::Object);
51a3b106 6use Sub::Quote;
7use B 'perlstring';
3202e039 8use Scalar::Util 'blessed';
daa05b62 9BEGIN {
6e294299 10 our $CAN_HAZ_XS =
33d35735 11 !$ENV{MOO_XS_DISABLE}
12 &&
6e294299 13 _maybe_load_module('Class::XSAccessor')
14 &&
edb9977c 15 (eval { Class::XSAccessor->VERSION('1.07') })
6e294299 16 ;
daa05b62 17}
51a3b106 18
316917c9 19sub generate_method {
6f68f022 20 my ($self, $into, $name, $spec, $quote_opts) = @_;
9f10cfe0 21 $name =~ s/^\+//;
51a3b106 22 die "Must have an is" unless my $is = $spec->{is};
dcae37d3 23 if ($is eq 'ro') {
24 $spec->{reader} = $name unless exists $spec->{reader};
25 } elsif ($is eq 'rw') {
26 $spec->{accessor} = $name unless exists $spec->{accessor};
27 } elsif ($is eq 'lazy') {
dcae37d3 28 $spec->{reader} = $name unless exists $spec->{reader};
29 $spec->{lazy} = 1;
30 $spec->{builder} ||= '_build_'.$name unless $spec->{default};
2bb6aaa3 31 } elsif ($is eq 'rwp') {
32 $spec->{reader} = $name unless exists $spec->{reader};
33 $spec->{writer} = "_set_${name}" unless exists $spec->{writer};
dcae37d3 34 } elsif ($is ne 'bare') {
35 die "Unknown is ${is}";
36 }
2bb6aaa3 37 $spec->{builder} = '_build_'.$name if ($spec->{builder}||0) eq 1;
38 if (($spec->{predicate}||0) eq 1) {
39 $spec->{predicate} = $name =~ /^_/ ? "_has${name}" : "has_${name}";
40 }
41 if (($spec->{clearer}||0) eq 1) {
42 $spec->{clearer} = $name =~ /^_/ ? "_clear${name}" : "clear_${name}";
43 }
44 if (($spec->{trigger}||0) eq 1) {
45 $spec->{trigger} = quote_sub('shift->_trigger_'.$name.'(@_)');
46 }
533797e1 47 if (exists $spec->{default}) {
b02331f2 48 my $default = $spec->{default};
3202e039 49 my $invalid = "Invalid default '" . overload::StrVal($default)
50 . "' for $into->$name - not a coderef";
51 die "$invalid or code-convertible object"
52 unless ref $default and (ref $default eq 'CODE' or blessed($default));
53 die "$invalid and could not be converted to a coderef: $@"
54 if !eval { \&$default };
533797e1 55 }
56
dcae37d3 57 my %methods;
58 if (my $reader = $spec->{reader}) {
59 if (our $CAN_HAZ_XS && $self->is_simple_get($name, $spec)) {
60 $methods{$reader} = $self->_generate_xs(
873df570 61 getters => $into, $reader, $name, $spec
dcae37d3 62 );
63 } else {
e57f338d 64 $self->{captures} = {};
dcae37d3 65 $methods{$reader} =
66 quote_sub "${into}::${reader}"
33d35735 67 => ' die "'.$reader.' is a read-only accessor" if @_ > 1;'."\n"
68 .$self->_generate_get($name, $spec)
e57f338d 69 => delete $self->{captures}
dcae37d3 70 ;
71 }
72 }
73 if (my $accessor = $spec->{accessor}) {
74 if (
75 our $CAN_HAZ_XS
76 && $self->is_simple_get($name, $spec)
77 && $self->is_simple_set($name, $spec)
78 ) {
79 $methods{$accessor} = $self->_generate_xs(
873df570 80 accessors => $into, $accessor, $name, $spec
dcae37d3 81 );
82 } else {
e57f338d 83 $self->{captures} = {};
dcae37d3 84 $methods{$accessor} =
85 quote_sub "${into}::${accessor}"
86 => $self->_generate_getset($name, $spec)
e57f338d 87 => delete $self->{captures}
dcae37d3 88 ;
89 }
90 }
91 if (my $writer = $spec->{writer}) {
92 if (
93 our $CAN_HAZ_XS
94 && $self->is_simple_set($name, $spec)
95 ) {
96 $methods{$writer} = $self->_generate_xs(
873df570 97 setters => $into, $writer, $name, $spec
dcae37d3 98 );
51a3b106 99 } else {
e57f338d 100 $self->{captures} = {};
dcae37d3 101 $methods{$writer} =
102 quote_sub "${into}::${writer}"
103 => $self->_generate_set($name, $spec)
e57f338d 104 => delete $self->{captures}
dcae37d3 105 ;
51a3b106 106 }
dcae37d3 107 }
8f333548 108 if (my $pred = $spec->{predicate}) {
dcae37d3 109 $methods{$pred} =
110 quote_sub "${into}::${pred}" =>
ceea0e38 111 ' '.$self->_generate_simple_has('$_[0]', $name, $spec)."\n"
dcae37d3 112 ;
8f333548 113 }
114 if (my $cl = $spec->{clearer}) {
dcae37d3 115 $methods{$cl} =
116 quote_sub "${into}::${cl}" =>
ceea0e38 117 $self->_generate_simple_clear('$_[0]', $name, $spec)."\n"
dcae37d3 118 ;
901efe1a 119 }
4db3a740 120 if (my $hspec = $spec->{handles}) {
121 my $asserter = $spec->{asserter} ||= '_assert_'.$name;
122 my @specs = do {
123 if (ref($hspec) eq 'ARRAY') {
124 map [ $_ => $_ ], @$hspec;
125 } elsif (ref($hspec) eq 'HASH') {
126 map [ $_ => ref($hspec->{$_}) ? @{$hspec->{$_}} : $hspec->{$_} ],
127 keys %$hspec;
128 } elsif (!ref($hspec)) {
129 map [ $_ => $_ ], Role::Tiny->methods_provided_by($hspec);
130 } else {
131 die "You gave me a handles of ${hspec} and I have no idea why";
132 }
133 };
134 foreach my $spec (@specs) {
135 my ($proxy, $target, @args) = @$spec;
e57f338d 136 $self->{captures} = {};
4db3a740 137 $methods{$proxy} =
138 quote_sub "${into}::${proxy}" =>
139 $self->_generate_delegation($asserter, $target, \@args),
e57f338d 140 delete $self->{captures}
4db3a740 141 ;
142 }
143 }
144 if (my $asserter = $spec->{asserter}) {
e57f338d 145 $self->{captures} = {};
4db3a740 146 $methods{$asserter} =
147 quote_sub "${into}::${asserter}" =>
148 'do { '.$self->_generate_get($name, $spec).qq! }||die "Attempted to access '${name}' but it is not set"!,
e57f338d 149 delete $self->{captures}
4db3a740 150 ;
151 }
dcae37d3 152 \%methods;
51a3b106 153}
154
3a9a65a4 155sub is_simple_attribute {
156 my ($self, $name, $spec) = @_;
8f333548 157 # clearer doesn't have to be listed because it doesn't
158 # affect whether defined/exists makes a difference
901efe1a 159 !grep $spec->{$_},
82a5b146 160 qw(lazy default builder coerce isa trigger predicate weak_ref);
3a9a65a4 161}
162
daa05b62 163sub is_simple_get {
164 my ($self, $name, $spec) = @_;
901efe1a 165 !($spec->{lazy} and ($spec->{default} or $spec->{builder}));
daa05b62 166}
167
168sub is_simple_set {
169 my ($self, $name, $spec) = @_;
82a5b146 170 !grep $spec->{$_}, qw(coerce isa trigger weak_ref);
daa05b62 171}
172
4ced3a94 173sub has_eager_default {
174 my ($self, $name, $spec) = @_;
175 (!$spec->{lazy} and ($spec->{default} or $spec->{builder}));
176}
177
51a3b106 178sub _generate_get {
46389f86 179 my ($self, $name, $spec) = @_;
ceea0e38 180 my $simple = $self->_generate_simple_get('$_[0]', $name, $spec);
901efe1a 181 if ($self->is_simple_get($name, $spec)) {
182 $simple;
183 } else {
184 'do { '.$self->_generate_use_default(
185 '$_[0]', $name, $spec,
ceea0e38 186 $self->_generate_simple_has('$_[0]', $name, $spec),
ef21bc32 187 ).'; '
188 .($spec->{isa}
189 ?($self->_generate_isa_check($name, $simple, $spec->{isa}).'; ')
190 :''
191 )
192 .$simple.' }';
901efe1a 193 }
46389f86 194}
195
196sub _generate_simple_has {
197 my ($self, $me, $name) = @_;
198 "exists ${me}->{${\perlstring $name}}";
199}
200
02e9ef74 201sub _generate_simple_clear {
202 my ($self, $me, $name) = @_;
203 " delete ${me}->{${\perlstring $name}}\n"
204}
205
649ac264 206sub generate_get_default {
207 my $self = shift;
e57f338d 208 $self->{captures} = {};
649ac264 209 my $code = $self->_generate_get_default(@_);
e57f338d 210 ($code, delete $self->{captures});
649ac264 211}
212
213sub _generate_use_default {
214 my ($self, $me, $name, $spec, $test) = @_;
160c2c2c 215 my $get_value = $self->_generate_get_default($me, $name, $spec);
216 if ($spec->{coerce}) {
217 $get_value = $self->_generate_coerce(
b1f04da5 218 $name, $get_value,
160c2c2c 219 $spec->{coerce}
220 )
221 }
46389f86 222 $self->_generate_simple_set(
160c2c2c 223 $me, $name, $spec, $get_value
46389f86 224 ).' unless '.$test;
a16d301e 225}
226
649ac264 227sub _generate_get_default {
228 my ($self, $me, $name, $spec) = @_;
229 $spec->{default}
230 ? $self->_generate_call_code($name, 'default', $me, $spec->{default})
231 : "${me}->${\$spec->{builder}}"
232}
233
a16d301e 234sub generate_simple_get {
901efe1a 235 my ($self, @args) = @_;
236 $self->_generate_simple_get(@args);
a16d301e 237}
238
239sub _generate_simple_get {
240 my ($self, $me, $name) = @_;
241 my $name_str = perlstring $name;
242 "${me}->{${name_str}}";
51a3b106 243}
244
245sub _generate_set {
dcae37d3 246 my ($self, $name, $spec) = @_;
901efe1a 247 if ($self->is_simple_set($name, $spec)) {
32381de9 248 $self->_generate_simple_set('$_[0]', $name, $spec, '$_[1]');
6d377074 249 } else {
82a5b146 250 my ($coerce, $trigger, $isa_check) = @{$spec}{qw(coerce trigger isa)};
32381de9 251 my $simple = $self->_generate_simple_set('$self', $name, $spec, '$value');
e57f338d 252 my $code = "do { my (\$self, \$value) = \@_;\n";
82a5b146 253 if ($coerce) {
254 $code .=
255 " \$value = "
b1f04da5 256 .$self->_generate_coerce($name, '$value', $coerce).";\n";
82a5b146 257 }
901efe1a 258 if ($isa_check) {
259 $code .=
e57f338d 260 " ".$self->_generate_isa_check($name, '$value', $isa_check).";\n";
901efe1a 261 }
262 if ($trigger) {
e57f338d 263 my $fire = $self->_generate_trigger($name, '$self', '$value', $trigger);
901efe1a 264 $code .=
e57f338d 265 " ".$simple.";\n ".$fire.";\n"
901efe1a 266 ." \$value;\n";
267 } else {
268 $code .= " ".$simple.";\n";
269 }
270 $code .= " }";
271 $code;
a16d301e 272 }
a16d301e 273}
82a5b146 274
275sub generate_coerce {
276 my $self = shift;
277 $self->{captures} = {};
278 my $code = $self->_generate_coerce(@_);
279 ($code, delete $self->{captures});
280}
281
282sub _generate_coerce {
b1f04da5 283 my ($self, $name, $value, $coerce) = @_;
82a5b146 284 $self->_generate_call_code($name, 'coerce', "${value}", $coerce);
285}
286
a16d301e 287sub generate_trigger {
288 my $self = shift;
e57f338d 289 $self->{captures} = {};
a16d301e 290 my $code = $self->_generate_trigger(@_);
e57f338d 291 ($code, delete $self->{captures});
a16d301e 292}
293
294sub _generate_trigger {
295 my ($self, $name, $obj, $value, $trigger) = @_;
6d377074 296 $self->_generate_call_code($name, 'trigger', "${obj}, ${value}", $trigger);
297}
298
299sub generate_isa_check {
901efe1a 300 my ($self, @args) = @_;
e57f338d 301 $self->{captures} = {};
901efe1a 302 my $code = $self->_generate_isa_check(@args);
e57f338d 303 ($code, delete $self->{captures});
6d377074 304}
305
306sub _generate_isa_check {
307 my ($self, $name, $value, $check) = @_;
308 $self->_generate_call_code($name, 'isa_check', $value, $check);
309}
310
311sub _generate_call_code {
312 my ($self, $name, $type, $values, $sub) = @_;
313 if (my $quoted = quoted_from_sub($sub)) {
625d6219 314 my $code = $quoted->[1];
e57f338d 315 my $at_ = '@_ = ('.$values.');';
8c6626cf 316 if (my $captures = $quoted->[2]) {
6d377074 317 my $cap_name = qq{\$${type}_captures_for_${name}};
8c6626cf 318 $self->{captures}->{$cap_name} = \$captures;
e57f338d 319 Sub::Quote::inlinify(
320 $code, $values, Sub::Quote::capture_unroll($cap_name, $captures, 6)
321 );
322 } else {
323 Sub::Quote::inlinify($code, $values);
8c6626cf 324 }
901efe1a 325 } else {
326 my $cap_name = qq{\$${type}_for_${name}};
327 $self->{captures}->{$cap_name} = \$sub;
328 "${cap_name}->(${values})";
625d6219 329 }
a16d301e 330}
331
3a9a65a4 332sub generate_populate_set {
333 my $self = shift;
e57f338d 334 $self->{captures} = {};
3a9a65a4 335 my $code = $self->_generate_populate_set(@_);
e57f338d 336 ($code, delete $self->{captures});
3a9a65a4 337}
338
339sub _generate_populate_set {
340 my ($self, $me, $name, $spec, $source, $test) = @_;
4ced3a94 341 if ($self->has_eager_default($name, $spec)) {
3a9a65a4 342 my $get_indent = ' ' x ($spec->{isa} ? 6 : 4);
d02da2bc 343 my $get_default = $self->_generate_get_default(
344 '$new', $_, $spec
345 );
3a9a65a4 346 my $get_value =
d02da2bc 347 defined($spec->{init_arg})
348 ? "(\n${get_indent} ${test}\n${get_indent} ? ${source}\n${get_indent} : "
349 .$get_default
350 ."\n${get_indent})"
351 : $get_default;
b1f04da5 352 if ($spec->{coerce}) {
353 $get_value = $self->_generate_coerce(
354 $name, $get_value,
355 $spec->{coerce}
356 )
7fe3b886 357 }
3a9a65a4 358 ($spec->{isa}
359 ? " {\n my \$value = ".$get_value.";\n "
5d349892 360 .$self->_generate_isa_check(
361 $name, '$value', $spec->{isa}
362 ).";\n"
32381de9 363 .' '.$self->_generate_simple_set($me, $name, $spec, '$value').";\n"
5d349892 364 ." }\n"
32381de9 365 : ' '.$self->_generate_simple_set($me, $name, $spec, $get_value).";\n"
3a9a65a4 366 )
367 .($spec->{trigger}
368 ? ' '
5d349892 369 .$self->_generate_trigger(
ceea0e38 370 $name, $me, $self->_generate_simple_get($me, $name, $spec),
5d349892 371 $spec->{trigger}
372 )." if ${test};\n"
3a9a65a4 373 : ''
374 );
375 } else {
376 " if (${test}) {\n"
82a5b146 377 .($spec->{coerce}
378 ? " $source = "
379 .$self->_generate_coerce(
b1f04da5 380 $name, $source,
82a5b146 381 $spec->{coerce}
382 ).";\n"
383 : ""
384 )
3a9a65a4 385 .($spec->{isa}
386 ? " "
5d349892 387 .$self->_generate_isa_check(
388 $name, $source, $spec->{isa}
389 ).";\n"
3a9a65a4 390 : ""
391 )
32381de9 392 ." ".$self->_generate_simple_set($me, $name, $spec, $source).";\n"
3a9a65a4 393 .($spec->{trigger}
5d349892 394 ? " "
395 .$self->_generate_trigger(
ceea0e38 396 $name, $me, $self->_generate_simple_get($me, $name, $spec),
5d349892 397 $spec->{trigger}
398 ).";\n"
399 : ""
3a9a65a4 400 )
401 ." }\n";
402 }
403}
404
873df570 405sub _generate_core_set {
406 my ($self, $me, $name, $spec, $value) = @_;
407 my $name_str = perlstring $name;
408 "${me}->{${name_str}} = ${value}";
409}
410
a16d301e 411sub _generate_simple_set {
32381de9 412 my ($self, $me, $name, $spec, $value) = @_;
a16d301e 413 my $name_str = perlstring $name;
2215d4b9 414
32381de9 415 if ($spec->{weak_ref}) {
6998abed 416 $value = '$preserve = '.$value;
417 my $simple = $self->_generate_core_set($me, $name, $spec, $value);
faa9ce11 418 require Scalar::Util;
2215d4b9 419
420 # Perl < 5.8.3 can't weaken refs to readonly vars
421 # (e.g. string constants). This *can* be solved by:
422 #
423 #Internals::SetReadWrite($foo);
424 #Scalar::Util::weaken ($foo);
425 #Internals::SetReadOnly($foo);
426 #
427 # but requires XS and is just too damn crazy
428 # so simply throw a better exception
6998abed 429 my $weak_simple = "my \$preserve; Scalar::Util::weaken(${simple})";
430 Moo::_Utils::lt_5_8_3() ? <<"EOC" : $weak_simple;
2215d4b9 431
6998abed 432 my \$preserve;
2215d4b9 433 eval { Scalar::Util::weaken($simple); 1 } or do {
434 if( \$@ =~ /Modification of a read-only value attempted/) {
faa9ce11 435 require Carp;
2215d4b9 436 Carp::croak( sprintf (
437 'Reference to readonly value in "%s" can not be weakened on Perl < 5.8.3',
438 $name_str,
439 ) );
440 } else {
441 die \$@;
442 }
443 };
444EOC
32381de9 445 } else {
6998abed 446 $self->_generate_core_set($me, $name, $spec, $value);
32381de9 447 }
51a3b106 448}
449
450sub _generate_getset {
a16d301e 451 my ($self, $name, $spec) = @_;
dcae37d3 452 q{(@_ > 1}."\n ? ".$self->_generate_set($name, $spec)
f537c364 453 ."\n : ".$self->_generate_get($name, $spec)."\n )";
51a3b106 454}
455
4db3a740 456sub _generate_delegation {
457 my ($self, $asserter, $target, $args) = @_;
458 my $arg_string = do {
459 if (@$args) {
460 # I could, I reckon, linearise out non-refs here using perlstring
461 # plus something to check for numbers but I'm unsure if it's worth it
462 $self->{captures}{'@curries'} = $args;
463 '@curries, @_';
464 } else {
465 '@_';
466 }
467 };
468 "shift->${asserter}->${target}(${arg_string});";
469}
470
daa05b62 471sub _generate_xs {
dcae37d3 472 my ($self, $type, $into, $name, $slot) = @_;
daa05b62 473 Class::XSAccessor->import(
e6f2e914 474 class => $into,
9f10cfe0 475 $type => { $name => $slot },
476 replace => 1,
daa05b62 477 );
901efe1a 478 $into->can($name);
daa05b62 479}
480
02e9ef74 481sub default_construction_string { '{}' }
482
51a3b106 4831;