File Coverage

blib/lib/Sub/HandlesVia/Toolkit.pm
Criterion Covered Total %
statement 83 110 75.4
branch 24 42 57.1
condition 6 12 50.0
subroutine 15 21 71.4
pod 0 4 0.0
total 128 189 67.7


line stmt bran cond sub pod time code
1 92     92   1725 use 5.008;
  92         327  
2 92     92   577 use strict;
  92         229  
  92         2022  
3 92     92   514 use warnings;
  92         203  
  92         6011  
4              
5              
6             our $AUTHORITY = 'cpan:TOBYINK';
7             our $VERSION = '0.045';
8              
9             use Sub::HandlesVia::Mite;
10 92     92   681  
  92         238  
  92         1083  
11             use Type::Params qw(compile_named_oo);
12 92     92   60055 use Types::Standard qw( ArrayRef HashRef Str Num Int CodeRef Bool Item Object );
  92         8538373  
  92         1252  
13 92     92   39594 use Types::Standard qw( assert_HashRef is_ArrayRef is_CodeRef is_Str );
  92         1252  
  92         1333  
14 92     92   341114  
  92         294  
  92         586  
15             my $sig;
16             $sig ||= compile_named_oo(
17             target => Str,
18 341   66 341 0 2000 attribute => ArrayRef->of(Str|CodeRef)->plus_coercions(Str|CodeRef, '[$_]'),
19             handles_via => ArrayRef->of(Str)->plus_coercions(Str, '[$_]'),
20             handles => HashRef->plus_coercions(ArrayRef, '+{map(+($_,$_),@$_)}'),
21             );
22            
23             my $me = shift;
24             my $arg = &$sig;
25 341         6338723  
26 341         1439 my $gen = $me->code_generator_for_attribute(
27             $arg->target,
28 341         29446 $arg->attribute,
29             );
30            
31             use Sub::HandlesVia::Handler;
32             my %handles = %{ $arg->handles };
33 92     92   230833 for my $h (sort keys %handles) {
  92         349  
  92         2039  
34 341         1016
  341         4708  
35 341         3590 my $handler = 'Sub::HandlesVia::Handler'->lookup(
36             $handles{$h},
37             $arg->handles_via,
38 4219         23919 );
39            
40             $handler->install_method(
41             method_name => $h,
42 4219         13139 code_generator => $gen,
43             );
44             }
45             }
46              
47             my %native = qw(
48             Array 1
49             Blessed 1
50             Bool 1
51             Code 1
52             Counter 1
53             Hash 1
54             Number 1
55             Scalar 1
56             String 1
57             );
58              
59             sort keys %native;
60             }
61              
62 0     0 0 0 my %default_type = (
63             Array => ArrayRef,
64             Hash => HashRef,
65             String => Str,
66             Number => Num,
67             Counter => Int,
68             Code => CodeRef,
69             Bool => Bool,
70             Scalar => Item,
71             Blessed => Object,
72             );
73              
74             my ($me, $target, $attr, $spec) = (shift, @_);
75              
76             delete $spec->{no_inline};
77              
78 334     334 0 1415 # Clean our stuff out of traits list...
79             if (ref $spec->{traits} and not $spec->{handles_via}) {
80 334         1306 my @keep = grep !$native{$_}, @{$spec->{traits}};
81             my @cull = grep $native{$_}, @{$spec->{traits}};
82             delete $spec->{traits};
83 334 100 66     2496 if (@keep) {
84 223         562 $spec->{traits} = \@keep;
  223         1309  
85 223         556 }
  223         1027  
86 223         615 if (@cull) {
87 223 50       848 $spec->{handles_via} = \@cull;
88 0         0 }
89             }
90 223 50       836  
91 223         815 return unless $spec->{handles_via};
92            
93             my @handles_via = ref($spec->{handles_via}) ? @{$spec->{handles_via}} : $spec->{handles_via};
94             my $joined = join('|', @handles_via);
95 334 50       1349  
96             if ($default_type{$joined} and not exists $spec->{isa}) {
97 334 100       1721 $spec->{isa} = $default_type{$joined};
  277         959  
98 334         1190 $spec->{coerce} = 1 if $default_type{$joined}->has_coercion;
99             }
100 334 100 66     5213
101 13         283 $spec->{handles} = { map +($_ => $_), @{ $spec->{handles} } }
102 13 50       70 if is_ArrayRef $spec->{handles};
103             assert_HashRef $spec->{handles};
104              
105 5         39 return {
106 334 100       5626 target => $target,
107 334         1996 attribute => $attr,
108             handles_via => delete($spec->{handles_via}),
109             handles => delete($spec->{handles}),
110             };
111             }
112              
113 334         4620 my ($me, $target, $attr) = (shift, @_);
114            
115             my ($get_slot, $set_slot, $default) = @$attr;
116             $set_slot = $get_slot if @$attr < 2;
117            
118 3     3 0 15 my $captures = {};
119             my ($get, $set, $slot, $get_is_lvalue) = (undef, undef, undef, 0);
120 3         10
121 3 50       17 require B;
122            
123 3         8 if (ref $get_slot) {
124 3         12 $get = sub { shift->generate_self . '->$shv_reader' };
125             $captures->{'$shv_reader'} = \$get_slot;
126 3         23 }
127             elsif ($get_slot =~ /\A \[ ([0-9]+) \] \z/sx) {
128 3 50       30 my $index = $1;
    50          
    100          
129 0     0   0 $get = sub { shift->generate_self . "->[$index]" };
  0         0  
130 0         0 $slot = $get;
131             ++$get_is_lvalue;
132             }
133 0         0 elsif ($get_slot =~ /\A \{ (.+) \} \z/sx) {
134 0     0   0 my $key = B::perlstring($1);
  0         0  
135 0         0 $get = sub { shift->generate_self . "->{$key}" };
136 0         0 $slot = $get;
137             ++$get_is_lvalue;
138             }
139 1         8 else {
140 1     4   7 my $method = B::perlstring($get_slot);
  4         11  
141 1         4 $get = sub { shift->generate_self . "->\${\\ $method}" };
142 1         3 }
143            
144             if (ref $set_slot) {
145 2         10 $set = sub {
146 2     8   13 my ($gen, $val) = @_;
  8         27  
147             $gen->generate_self . "->\$shv_writer($val)";
148             };
149 3 50       25 $captures->{'$shv_writer'} = \$set_slot;
    50          
    100          
150             }
151 0     0   0 elsif ($set_slot =~ /\A \[ ([0-9]+) \] \z/sx) {
152 0         0 my $index = $1;
153 0         0 $set = sub {
154 0         0 my ($gen, $val) = @_;
155             my $self = $gen->generate_self;
156             "($self\->[$index] = $val)";
157 0         0 };
158             }
159 0     0   0 elsif ($set_slot =~ /\A \{ (.+) \} \z/sx) {
160 0         0 my $key = B::perlstring($1);
161 0         0 $set = sub {
162 0         0 my ($gen, $val) = @_;
163             my $self = $gen->generate_self;
164             "($self\->{$key} = $val)";
165 1         6 };
166             }
167 2     2   11 else {
168 2         8 my $method = B::perlstring($set_slot);
169 2         17 $set = sub {
170 1         7 my ($gen, $val) = @_;
171             my $self = $gen->generate_self;
172             "$self\->\${\\ $method}($val)";
173 2         9 };
174             }
175 4     4   19
176 4         34 if (is_CodeRef $default) {
177 4         33 $captures->{'$shv_default_for_reset'} = \$default;
178 2         11 }
179              
180             require Sub::HandlesVia::CodeGenerator;
181 3 50       18 return 'Sub::HandlesVia::CodeGenerator'->new(
182 0         0 toolkit => $me,
183             target => $target,
184             attribute => $attr,
185 3         1630 env => $captures,
186             coerce => !!0,
187             generator_for_get => $get,
188             generator_for_set => $set,
189             get_is_lvalue => $get_is_lvalue,
190             set_checks_isa => !!1,
191             set_strictly => !!1,
192             generator_for_default => sub {
193             my ( $gen, $handler ) = @_ or die;
194             if ( !$default and $handler ) {
195             return $handler->default_for_reset->();
196             }
197             elsif ( is_CodeRef $default ) {
198 0 0   0     return sprintf(
199 0 0 0       '(%s)->$shv_default_for_reset',
    0          
    0          
200 0           $gen->generate_self,
201             );
202             }
203 0           elsif ( is_Str $default ) {
204             require B;
205             return sprintf(
206             '(%s)->${\ %s }',
207             $gen->generate_self,
208             B::perlstring( $default ),
209 0           );
210 0           }
211             return;
212             },
213             ( $slot ? ( generator_for_slot => $slot ) : () ),
214             );
215             }
216 0            
217             1;
218 3 100       70  
219              
220             =pod
221              
222             =encoding utf-8
223              
224             =head1 NAME
225              
226             Sub::HandlesVia::Toolkit - integration with OO frameworks for Sub::HandlesVia
227              
228             =head1 DESCRIPTION
229              
230             B<< This module is part of Sub::HandlesVia's internal API. >>
231             It is mostly of interest to people extending Sub::HandlesVia.
232              
233             Detect what subclass of Sub::HandlesVia::Toolkit is suitable for a class:
234              
235             my $toolkit = Sub::HandlesVia->detect_toolkit($class);
236              
237             Extract handles_via information from a C<has> attribute spec hash:
238              
239             my $shvdata = $toolkit->clean_spec($class, $attrname, \%spec);
240              
241             This not only returns the data that Sub::HandlesVia needs, it also cleans
242             C<< %spec >> so that it can be passed to a Moose-like C<has> function
243             without it complaining about unrecognized options.
244              
245             $toolkit->install_delegations($shvdata) if $shvdata;
246              
247             =head1 BUGS
248              
249             Please report any bugs to
250             L<https://github.com/tobyink/p5-sub-handlesvia/issues>.
251              
252             =head1 SEE ALSO
253              
254             L<Sub::HandlesVia>.
255              
256             =head1 AUTHOR
257              
258             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
259              
260             =head1 COPYRIGHT AND LICENCE
261              
262             This software is copyright (c) 2020, 2022 by Toby Inkster.
263              
264             This is free software; you can redistribute it and/or modify it under
265             the same terms as the Perl 5 programming language system itself.
266              
267             =head1 DISCLAIMER OF WARRANTIES
268              
269             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
270             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
271             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
272