File Coverage

blib/lib/Sub/HandlesVia/Toolkit/Moo.pm
Criterion Covered Total %
statement 132 202 65.3
branch 60 136 44.1
condition 20 61 32.7
subroutine 19 29 65.5
pod 0 3 0.0
total 231 431 53.6


line stmt bran cond sub pod time code
1 35     132   633 use 5.008;
  35         301  
2 35     105   192 use strict;
  35         77  
  35         717  
3 35     105   165 use warnings;
  35         72  
  35         2225  
4              
5              
6             our $AUTHORITY = 'cpan:TOBYINK';
7             our $VERSION = '0.045';
8              
9             use Sub::HandlesVia::Mite;
10 35     75   15943 extends 'Sub::HandlesVia::Toolkit';
  35         105  
  35         1141  
11              
12             use Types::Standard qw( is_ArrayRef is_Str assert_HashRef is_CodeRef is_Undef );
13 35     49   3180 use Types::Standard qw( ArrayRef HashRef Str Num Int CodeRef Bool );
  35         91  
  35         361  
14 35     35   106324  
  35         79  
  35         149  
15             my $me = shift;
16             my ($target) = @_;
17 104     104 0 290 $me->install_has_wrapper($target);
18 104         330 }
19 104         423  
20             my $me = shift;
21             my ($target) = @_;
22              
23 104     104 0 218 my ($installer, $orig);
24 104         242 if ($INC{'Moo/Role.pm'} && 'Moo::Role'->is_role($target)) {
25             $installer = 'Moo::Role::_install_tracked';
26 104         264 $orig = $Moo::Role::INFO{$target}{exports}{has};
27 104 100 100     813 }
28 3         118 else {
29 3         8 require Moo;
30             $installer = 'Moo::_install_tracked';
31             $orig = $Moo::MAKERS{$target}{exports}{has} || $Moo::MAKERS{$target}{non_methods}{has};
32 101         2129 }
33 101         265
34 101   33     897 $orig ||= $target->can('has');
35             ref($orig) or croak("$target doesn't have a `has` function");
36            
37 104   66     447 $target->$installer(has => sub {
38 104 50       424 if (@_ % 2 == 0) {
39             require Carp;
40             Carp::croak("Invalid options for attribute(s): even number of arguments expected, got " . scalar @_);
41 124 50   124   769727 }
        124      
        109      
42 0         0 my ($attrs, %spec) = @_;
43 0         0 return $orig->($attrs, %spec) unless $spec{handles}; # shortcut
44             for my $attr ( ref($attrs) ? @$attrs : $attrs ) {
45 124         973 ( my $real_attr = $attr ) =~ s/^[+]//;
46 124 100       554 my $shv = $me->clean_spec($target, $real_attr, \%spec);
47 120 50       638 $orig->($attr, %spec);
48 120         456 $me->install_delegations($shv) if $shv;
49 120         1075 }
50 120         824 return;
51 120 50       855302 });
52             }
53 120         22023  
54 104         1200 my ($me, $target, $attrname) = (shift, @_);
55            
56             if (ref $attrname) {
57             @$attrname==1 or die;
58 120     120 0 509 ($attrname) = @$attrname;
59             }
60 120 50       606
61 120 50       525 my $ctor_maker = $INC{'Moo.pm'} && 'Moo'->_constructor_maker_for($target);
62 120         398
63             if (!$ctor_maker) {
64             return $me->_code_generator_for_role_attribute($target, $attrname);
65 120   66     1066 }
66            
67 120 100       1613 my $spec = $ctor_maker->all_attribute_specs->{$attrname};
68 3         16 my $maker = 'Moo'->_accessor_maker_for($target);
69              
70             my $type = $spec->{isa} ? Types::TypeTiny::to_TypeTiny($spec->{isa}) : undef;
71 117         593 my $coerce = exists($spec->{coerce}) ? $spec->{coerce} : 0;
72 117         852 if ((ref($coerce)||'') eq 'CODE') {
73             $type = $type->plus_coercions(Types::Standard::Any(), $coerce);
74 117 50       3648 $coerce = 1;
75 117 100       2552 }
76 117 50 100     857
77 0         0 my $slot = sub {
78 0         0 my $gen = shift;
79             my ($code) = $maker->generate_simple_get($gen->generate_self, $attrname, $spec);
80             $code;
81             };
82 1     1   3
83 1         5 my $captures = {};
84 1         32 my ($is_simple_get, $get) = $maker->is_simple_get($attrname, $spec)
85 117         832 ? (1, sub {
86             my $gen = shift;
87 117         346 my $selfvar = $gen ? $gen->generate_self : '$_[0]';
88             my ($return) = $maker->generate_simple_get($selfvar, $attrname, $spec);
89             %$captures = ( %$captures, %{ delete($maker->{captures}) or {} } );
90 1796     1796   3114 $return;
91 1796 100       5560 })
92 1796         6425 : (0, sub {
93 1796 50       48301 my $gen = shift;
  1796         8916  
94 1796         11644 my $selfvar = $gen ? $gen->generate_self : '$_[0]';
95             my ($return) = $maker->_generate_use_default(
96             $selfvar,
97 332     332   611 $attrname,
98 332 100       1061 $spec,
99 332         1211 $maker->_generate_simple_has($selfvar, $attrname, $spec),
100             );
101             %$captures = ( %$captures, %{ delete($maker->{captures}) or {} } );
102             $return;
103             });
104             my ($is_simple_set, $set) = $maker->is_simple_set($attrname, $spec)
105 332 100       73795 ? (1, sub {
  332         1909  
106 332         2430 my ($gen, $var) = @_;
107 117 100       663 my $selfvar = $gen ? $gen->generate_self : '$_[0]';
108             my $code = $maker->_generate_simple_set($selfvar, $attrname, $spec, $var);
109             $captures = { %$captures, %{ delete($maker->{captures}) or {} } }; # merge environments
110 0     0   0 $code;
111 0 0       0 })
112 0         0 : (0, sub { # that allows us to avoid going down this yucky code path
113 0 0       0 my ($gen, $var) = @_;
  0         0  
114 0         0 my $selfvar = $gen ? $gen->generate_self : '$_[0]';
115             my $code = $maker->_generate_set($attrname, $spec);
116             $captures = { %$captures, %{ delete($maker->{captures}) or {} } }; # merge environments
117 626     626   2412 $code = "do { local \@_ = ($selfvar, $var); $code }";
118 626 100       2255 $code;
119 626         6480 });
120 626 100       126004
  626         3925  
121 626         3159 # force $captures to be updated
122 626         3576 $get->(undef, '$dummy');
123 117 50       1871 $set->(undef, '$dummy');
124            
125             my $default;
126 117         2364 if (exists $spec->{default}) {
127 117         475 $default = [ default => $spec->{default} ];
128             }
129 117         256 elsif (exists $spec->{builder}) {
130 117 100       516 $default = [ builder => $spec->{builder} ];
    100          
131 90         384 }
132            
133             if (is_CodeRef $default->[1]) {
134 8         30 $captures->{'$shv_default_for_reset'} = \$default->[1];
135             }
136            
137 117 100       672 require Sub::HandlesVia::CodeGenerator;
138 47         182 return 'Sub::HandlesVia::CodeGenerator'->new(
139             toolkit => $me,
140             target => $target,
141 117         19151 attribute => $attrname,
142             attribute_spec => $spec,
143             env => $captures,
144             isa => $type,
145             coerce => !!$coerce,
146             generator_for_slot => $slot,
147             generator_for_get => $get,
148             generator_for_set => $set,
149             get_is_lvalue => $is_simple_get,
150             set_checks_isa => !$is_simple_set,
151             set_strictly => $spec->{weak_ref} || $spec->{trigger},
152             generator_for_default => sub {
153             my ( $gen, $handler ) = @_ or die;
154             if ( !$default and $handler ) {
155             return $handler->default_for_reset->();
156             }
157 19 50   19   101 elsif ( $default->[0] eq 'builder' ) {
158 19 50 33     341 return sprintf(
    100 66        
    100 33        
    50 33        
    50          
159 0         0 '(%s)->%s',
160             $gen->generate_self,
161             $default->[1],
162 4         28 );
163             }
164             elsif ( $default->[0] eq 'default' and is_CodeRef $default->[1] ) {
165             return sprintf(
166             '(%s)->$shv_default_for_reset',
167             $gen->generate_self,
168             );
169 5         25 }
170             elsif ( $default->[0] eq 'default' and is_Undef $default->[1] ) {
171             return 'undef';
172             }
173             elsif ( $default->[0] eq 'default' and is_Str $default->[1] ) {
174             require B;
175 0         0 return B::perlstring( $default->[1] );
176             }
177             return;
178 10         64 },
179 10         96 );
180             }
181 0         0  
182             my ($me, $target, $attrname) = (shift, @_);
183 117   66     2460
184             if (ref $attrname) {
185             @$attrname==1 or die;
186             ($attrname) = @$attrname;
187 3     3   46 }
188            
189 3 50       11 require B;
190 0 0       0
191 0         0 my %all_specs = @{ $Moo::Role::INFO{$target}{attributes} };
192             my $spec = $all_specs{$attrname};
193              
194 3         25 my ($reader_name, $writer_name);
195            
196 3         8 if ($spec->{is} eq 'ro') {
  3         20  
197 3         10 $reader_name = $attrname;
198             }
199 3         8 elsif ($spec->{is} eq 'rw') {
200             $reader_name = $attrname;
201 3 50       15 $writer_name = $attrname;
    0          
    0          
202 3         10 }
203             elsif ($spec->{is} eq 'rwp') {
204             $reader_name = $attrname;
205 0         0 $writer_name = "_set_$attrname";
206 0         0 }
207             if (exists $spec->{reader}) {
208             $reader_name = $spec->{reader};
209 0         0 }
210 0         0 if (exists $spec->{writer}) {
211             $writer_name = $spec->{reader};
212 3 50       14 }
213 3         8 if (exists $spec->{accessor}) {
214             $reader_name = $spec->{accessor} unless defined $reader_name;
215 3 50       11 $writer_name = $spec->{accessor} unless defined $writer_name;
216 0         0 }
217            
218 3 50       13 my $type = $spec->{isa} ? Types::TypeTiny::to_TypeTiny($spec->{isa}) : undef;
219 0 0       0 my $coerce = $spec->{coerce};
220 0 0       0 if ((ref($coerce)||'') eq 'CODE') {
221             $type = $type->plus_coercions(Types::Standard::Any(), $coerce);
222             $coerce = 1;
223 3 50       14 }
224 3         62
225 3 50 50     23 my $captures = {};
226 0         0 my ($get, $set);
227 0         0
228             if (defined $reader_name) {
229             $get = ($reader_name =~ /^[\W0-9]\w*$/s)
230 3         8 ? sub { my $gen = shift; sprintf "%s->%s", $gen->generate_self, $reader_name }
231 3         23 : sub { my $gen = shift; sprintf "%s->\${\\ %s }", $gen->generate_self, B::perlstring($reader_name) };
232             }
233 3 50       13 else {
234             my ($default, $default_literal) = (undef, 0);
235 0     0   0 if (is_Coderef $spec->{default}) {
  0         0  
236 3 50   7   31 $default = $spec->{default};
  7         21  
  7         25  
237             }
238             elsif (exists $spec->{default}) {
239 0         0 ++$default_literal;
240 0 0 0     0 $default = $spec->{default};
    0 0        
    0          
    0          
241 0         0 }
242             elsif (is_CodeRef $spec->{builder} or (($spec->{builder}||0) eq 1)) {
243             $default = '_build_'.$attrname;
244 0         0 }
245 0         0 elsif ($spec->{builder}) {
246             $default = $spec->{builder};
247             }
248 0         0 else {
249             ++$default_literal;
250             }
251 0         0 my $dammit_i_need_to_build_a_reader = sub {
252             my $instance = shift;
253             exists($instance->{$attrname}) or do {
254 0         0 $instance->{$attrname} ||= $default_literal ? $default : $instance->$default;
255             };
256             $instance->{$attrname};
257 0     0   0 };
258 0 0       0 $captures->{'$shv_reader'} = \$dammit_i_need_to_build_a_reader;
259 0 0 0     0 $get = sub { my $gen = shift; $gen->generate_self . '->$shv_reader()' };
260             }
261 0         0
262 0         0
263 0         0 if (defined $writer_name) {
264 0     0   0 $set = $writer_name =~ /^[\W0-9]\w*$/s
  0         0  
  0         0  
265             ? sub { my ($gen, $val) = @_; sprintf "%s->%s(%s)", $gen->generate_self, $writer_name, $val }
266             : sub { my ($gen, $val) = @_; sprintf "%s->\${\\ %s }(%s)", $gen->generate_self, B::perlstring($writer_name), $val };
267             }
268 3 50       11 else {
269             my $trigger;
270 0     0   0 if (($spec->{trigger}||0) eq 1) {
  0         0  
271 0 0   0   0 $trigger = "_trigger_$attrname";
  0         0  
  0         0  
272             }
273             my $weaken = $spec->{weak_ref} || 0;
274 3         7 my $dammit_i_need_to_build_a_writer = sub {
275 3 50 50     23 my ($instance, $new_value) = (shift, @_);
276 0         0 if ($type) {
277             ($type->has_coercion && $coerce)
278 3   50     18 ? ($new_value = $type->assert_coerce($new_value))
279             : $type->assert_valid($new_value);
280 0     0   0 }
281 0 0       0 if ($trigger) {
282 0 0 0     0 $instance->$trigger($new_value, exists($instance->{$attrname}) ? $instance->{$attrname} : ())
283             }
284             $instance->{$attrname} = $new_value;
285             if ($weaken and ref $new_value) {
286 0 0       0 Scalar::Util::weaken($instance->{$attrname});
287 0 0       0 }
288             $instance->{$attrname};
289 0         0 };
290 0 0 0     0 $captures->{'$shv_writer'} = \$dammit_i_need_to_build_a_writer;
291 0         0 $set = sub { my ($gen, $val) = @_; $gen->generate_self . "->\$shv_writer($val)" };
292             }
293 0         0  
294 3         27 my $default;
295 3         12 if (exists $spec->{default}) {
296 3     0   15 $default = [ default => $spec->{default} ];
  0         0  
  0         0  
297             }
298             elsif (exists $spec->{builder}) {
299 3         7 $default = [ builder => $spec->{builder} ];
300 3 100       15 }
    50          
301 1         4
302             if (is_CodeRef $default->[1]) {
303             $captures->{'$shv_default_for_reset'} = \$default->[1];
304 2         22 }
305            
306             require Sub::HandlesVia::CodeGenerator;
307 3 100       21 return 'Sub::HandlesVia::CodeGenerator'->new(
308 1         7 toolkit => $me,
309             target => $target,
310             attribute => $attrname,
311 3         1580 attribute_spec => $spec,
312             env => $captures,
313             isa => $type,
314             coerce => !!$coerce,
315             generator_for_slot => sub { shift->generate_self.'->{'.B::perlstring($attrname).'}' }, # icky
316             generator_for_get => $get,
317             generator_for_set => $set,
318             get_is_lvalue => !!0,
319             set_checks_isa => !!1,
320 0     0     set_strictly => !!0,
321             generator_for_default => sub {
322             my ( $gen, $handler ) = @_ or die;
323             if ( !$default and $handler ) {
324             return $handler->default_for_reset->();
325             }
326             elsif ( $default->[0] eq 'builder' ) {
327 0 0   0     return sprintf(
328 0 0 0       '(%s)->%s',
    0 0        
    0 0        
    0 0        
    0          
329 0           $gen->generate_self,
330             $default->[1],
331             );
332 0           }
333             elsif ( $default->[0] eq 'default' and is_CodeRef $default->[1] ) {
334             return sprintf(
335             '(%s)->$shv_default_for_reset',
336             $gen->generate_self,
337             );
338             }
339 0           elsif ( $default->[0] eq 'default' and is_Undef $default->[1] ) {
340             return 'undef';
341             }
342             elsif ( $default->[0] eq 'default' and is_Str $default->[1] ) {
343             require B;
344             return B::perlstring( $default->[1] );
345 0           }
346             return;
347             },
348 0           );
349 0           }
350              
351 0           1;