File Coverage

blib/lib/Sub/HandlesVia/Handler.pm
Criterion Covered Total %
statement 102 125 81.6
branch 46 66 69.7
condition 14 24 58.3
subroutine 27 29 93.1
pod 8 8 100.0
total 197 252 78.1


line stmt bran cond sub pod time code
1 96     96   2898 use 5.008;
  96         599  
2 96     96   871 use strict;
  96         480  
  96         2494  
3 96     96   522 use warnings;
  96         252  
  96         8237  
4              
5             package Sub::HandlesVia::Handler;
6              
7             our $AUTHORITY = 'cpan:TOBYINK';
8             our $VERSION = '0.046';
9              
10 96     96   1364 use Sub::HandlesVia::Mite -all;
  96         300  
  96         1312  
11              
12             has name => (
13             is => ro,
14             isa => 'Str',
15             );
16              
17             has template => (
18             is => ro,
19             isa => 'Str',
20             );
21              
22             has lvalue_template => (
23             is => ro,
24             isa => 'Str',
25             );
26              
27             has args => (
28             is => ro,
29             isa => 'Int|Undef',
30             default => undef,
31             );
32              
33             has [ 'min_args', 'max_args' ] => (
34             is => lazy,
35             isa => 'Int|Undef',
36 4560     4560   11944 builder => sub { shift->args },
37             );
38              
39             # Not proper predicates because they check definedness
40 1403     1403 1 3748 sub has_min_args { defined shift->min_args }
41 5098     5098 1 12195 sub has_max_args { defined shift->max_args }
42              
43             has signature => (
44             is => ro,
45             isa => 'ArrayRef|Undef',
46             );
47              
48             has usage => (
49             is => lazy,
50             isa => 'Str',
51             builder => true,
52             );
53              
54             has curried => (
55             is => ro,
56             isa => 'ArrayRef',
57             );
58              
59             has [ 'is_chainable', 'no_validation_needed' ] => (
60             is => ro,
61             isa => 'Bool',
62             coerce => true,
63             );
64              
65             has is_mutator => (
66             is => lazy,
67             isa => 'Bool',
68             coerce => true,
69             default => sub { defined $_[0]{lvalue_template} or $_[0]{template} =~ /«/ }
70             );
71              
72             has allow_getter_shortcuts => (
73             is => ro,
74             isa => 'Bool',
75             coerce => true,
76             default => true,
77             );
78              
79             has prefer_shift_self => (
80             is => ro,
81             isa => 'Bool',
82             coerce => true,
83             default => false,
84             );
85              
86             has additional_validation => (
87             is => ro,
88             isa => 'CodeRef|Str|Undef',
89             );
90              
91             has default_for_reset => (
92             is => ro,
93             isa => 'CodeRef',
94             );
95              
96             has documentation => (
97             is => ro,
98             isa => 'Str',
99             );
100              
101             has _examples => (
102             is => ro,
103             isa => 'CodeRef',
104             );
105              
106             sub _build_usage {
107 96     96   41940 no warnings 'uninitialized';
  96         232  
  96         139275  
108 2292     2292   3987 my $self = shift;
109 2292 100 66     5409 if ($self->has_max_args and $self->max_args==0) {
    50 66        
    50 33        
    50 33        
    50          
110 2074         6787 return '';
111             }
112             elsif ($self->min_args==0 and $self->max_args==1) {
113 0         0 return '$arg?';
114             }
115             elsif ($self->min_args==1 and $self->max_args==1) {
116 0         0 return '$arg';
117             }
118             elsif ($self->min_args > 0 and $self->max_args > 0) {
119 0         0 return sprintf('@min_%d_max_%d_args', $self->min_args, $self->max_args);
120             }
121             elsif ($self->max_args > 0) {
122 0         0 return sprintf('@max_%d_args', $self->max_args);
123             }
124 218         660 return '@args';
125             }
126              
127             sub curry {
128 1403     1403 1 4458 my ($self, @curried) = @_;
129 1403 50 66     3866 if ($self->has_max_args and @curried > $self->max_args) {
130 0         0 die "too many arguments to curry";
131             }
132 1403         10217 my %copy = %$self;
133 1403         3930 delete $copy{usage};
134             ref($self)->new(
135             %copy,
136             name => sprintf('%s[curried]', $self->name),
137             max_args => $self->has_max_args ? $self->max_args - @curried : undef,
138             min_args => $self->has_min_args ? $self->min_args - @curried : undef,
139 1403 100       8932 signature => $self->signature ? do { my @sig = @{$self->{signature}}; splice(@sig,0,scalar(@curried)); \@sig } : undef,
  1189 100       2019  
  1189 100       3093  
  1189         2760  
  1189         5348  
140             curried => \@curried,
141             );
142             }
143              
144             sub loose {
145 0     0 1 0 my $self = shift;
146 0         0 ref($self)->new(%$self, signature => undef);
147             }
148              
149             sub chainable {
150 20     20 1 53 my $self = shift;
151 20         162 ref($self)->new(%$self, is_chainable => 1);
152             }
153              
154             sub _real_additional_validation {
155 1381     1381   2450 my $me = shift;
156 1381         2799 my $av = $me->additional_validation;
157 1381 100       4312 return $av if ref $av;
158            
159 555         2793 my ($lib) = split /:/, $me->name;
160             return sub {
161 555     555   989 my $self = shift;
162 555         1450 my ($sig_was_checked, $callbacks) = @_;
163 555         2988 my $ti = "Sub::HandlesVia::HandlerLibrary::$lib"->_type_inspector($callbacks->{isa});
164 555 100 66     4390 if ($ti and $ti->{trust_mutated} eq 'always') {
165 170         914 return { code => '1;', env => {} };
166             }
167 385 100 66     1862 if ($ti and $ti->{trust_mutated} eq 'maybe') {
168 150         871 return { code => '1;', env => {} };
169             }
170 235         887 return;
171 555 50       4439 } if $av eq 'no incoming values';
172              
173 0         0 return;
174             }
175              
176             sub lookup {
177 4244     4244 1 7905 my $class = shift;
178 4244 100       9049 my ($method, $traits) = map { ref($_) eq 'ARRAY' ? $_ : [$_] } @_;
  8488         29014  
179 4244         12486 my ($method_name, @curry) = @$method;
180            
181 4244         6468 my $handler;
182 4244         6904 my $make_chainable = 0;
183 4244         6504 my $make_loose = 0;
184              
185 4244 100       8535 if (ref $method_name eq 'CODE') {
186 1         8 $handler = Sub::HandlesVia::Handler::CodeRef->new(
187             name => '__ANON__',
188             delegated_coderef => $method_name,
189             );
190             }
191             else {
192 4243 100       12955 if ($method_name =~ /\s*\.\.\.$/) {
193 20         124 $method_name =~ s/\s*\.\.\.$//;
194 20         54 ++$make_chainable;
195             }
196 4243 50       9511 if ($method_name =~ /^\~\s*/) {
197 0         0 $method_name =~ s/^\~\s*//;
198 0         0 ++$make_loose;
199             }
200 4243 100       10146 if ($method_name =~ /^(.+?)\s*\-\>\s*(.+?)$/) {
201 3         13 $traits = [$1];
202 3         9 $method_name = $2;
203             }
204             }
205            
206 4244 100       8994 if (not $handler) {
207 4243         8435 SEARCH: for my $trait (@$traits) {
208 4243 50       12675 my $class = $trait =~ /:/
209             ? $trait
210             : "Sub::HandlesVia::HandlerLibrary::$trait";
211 4243 50       9846 if ( $class ne $trait ) {
212 4243         6621 local $@;
213 4243 50       281219 eval "require $class; 1"
214             or warn $@;
215             }
216 4243 100 66     40039 if ($class->isa('Sub::HandlesVia::HandlerLibrary') and $class->has_handler($method_name)) {
217 4242         12427 $handler = $class->get_handler($method_name);
218             }
219             }
220             }
221            
222 4244 100       10619 if (not $handler) {
223 1         14 $handler = Sub::HandlesVia::Handler::Traditional->new(name => $method_name);
224             }
225            
226 4244 100       12130 $handler = $handler->curry(@curry) if @curry;
227 4244 50       12703 $handler = $handler->loose if $make_loose;
228 4244 100       8441 $handler = $handler->chainable if $make_chainable;
229            
230 4244         14122 return $handler;
231             }
232              
233             sub install_method {
234 4244     4244 1 15665 my ( $self, %arg ) = @_;
235 4244 50       11711 my $gen = $arg{code_generator} or die;
236            
237 4244         16284 $gen->generate_and_install_method( $arg{method_name}, $self );
238            
239 4244         85113 return;
240             }
241              
242             sub code_as_string {
243 0     0 1 0 my ($self, %arg ) = @_;
244 0 0       0 my $gen = $arg{code_generator} or die;
245              
246 0         0 my $eval = $gen->_generate_ec_args_for_handler( $arg{method_name}, $self );
247 0         0 my $code = join "\n", @{$eval->{source}};
  0         0  
248 0 0       0 if ($arg{method_name}) {
249 0         0 $code =~ s/sub/sub $arg{method_name}/xs;
250             }
251 0 0       0 if (eval { require Perl::Tidy }) {
  0         0  
252 0         0 my $tidy = '';
253 0         0 Perl::Tidy::perltidy(
254             source => \$code,
255             destination => \$tidy,
256             );
257 0         0 $code = $tidy;
258             }
259 0         0 $code;
260             }
261              
262       4245     sub _tweak_env {}
263              
264 96     96   1955 use Exporter::Shiny qw( handler );
  96         1280  
  96         1226  
265             sub _generate_handler {
266 107     107   17391 my $me = shift;
267             return sub {
268 4399 50   4399   289299 my (%args) = @_%2 ? (template=>@_) : @_;
269 4399         24290 $me->new(%args);
270 107         1106 };
271             }
272              
273             package Sub::HandlesVia::Handler::Traditional;
274             # XXX: can this be replaced by Blessed trait?
275              
276             our $AUTHORITY = 'cpan:TOBYINK';
277             our $VERSION = '0.046';
278              
279 96     96   21812 use Sub::HandlesVia::Mite -all;
  96         310  
  96         918  
280             extends 'Sub::HandlesVia::Handler';
281              
282             has '+name' => ( required => true );
283              
284 1     1   3 sub is_mutator { 0 }
285              
286             sub template {
287 1     1   4 my $self = shift;
288 1         16 require B;
289 1         10 my $q_name = B::perlstring( $self->name );
290 1         8 return sprintf(
291             '$GET->${\\ '.$q_name.'}( @ARG )',
292             );
293             }
294              
295             package Sub::HandlesVia::Handler::CodeRef;
296              
297             our $AUTHORITY = 'cpan:TOBYINK';
298             our $VERSION = '0.046';
299              
300 96     96   19646 use Sub::HandlesVia::Mite -all;
  96         371  
  96         629  
301             extends 'Sub::HandlesVia::Handler';
302              
303             has delegated_coderef => (
304             is => 'ro',
305             isa => 'CodeRef',
306             required => true,
307             );
308              
309 1     1   7 sub is_mutator { 0 }
310              
311             sub BUILD {
312 2 50   2   12 $_[1]{delegated_coderef} or die 'delegated_coderef required';
313             }
314              
315             sub _tweak_env {
316 1     1   5 my ( $self, $env ) = @_;
317 1         4 $env->{'$shv_callback'} = \($self->delegated_coderef);
318             }
319              
320             sub template {
321 1     1   5 return '$shv_callback->(my $shvtmp = $GET, @ARG)';
322             }
323              
324             1;
325              
326             __END__
327              
328             =pod
329              
330             =encoding utf-8
331              
332             =head1 NAME
333              
334             Sub::HandlesVia::Handler - template for a method that can be delegated to
335              
336             =head1 DESCRIPTION
337              
338             B<< This module is part of Sub::HandlesVia's internal API. >>
339             It is mostly of interest to people extending Sub::HandlesVia.
340              
341             This module works in conjunction with L<Sub::HandlesVia::CodeGenerator>
342             and subclasses of L<Sub::HandlesVia::Toolkit> to build a string of Perl
343             code which can be compiled into a method to install into your class.
344              
345             =head1 CONSTRUCTORS
346              
347             =head2 C<< new( %attributes ) >>
348              
349             Standard Moose-like constructor.
350              
351             =head2 C<< lookup( $method, $trait ) >>
352              
353             Looks up a method from existing handler libraries.
354              
355             my $h = Sub::HandlesVia::Handler->lookup( 'get', 'Array' );
356              
357             Curried version:
358              
359             my $h = Sub::HandlesVia::Handler->lookup( [ 'get', 0 ], 'Array' );
360              
361             The C<< $trait >> may be an arrayref of possible traits.
362              
363             =head1 EXPORTS
364              
365             Nothing is exported by default.
366              
367             =head2 C<< handler %attributes >>
368              
369             Shortcut for the C<new> constructor.
370              
371             use Sub::HandlesVia::Handler 'handler';
372            
373             my $h = handler( %attr );
374             # is the same as
375             my $h = Sub::HandlesVia::Handler->new( %attr );
376              
377             =head1 ATTRIBUTES
378              
379             =head2 C<< name >> B<< Str >>
380              
381             The name of the function being delegated to.
382              
383             =head2 C<< is_mutator >> B<Bool>
384              
385             Indicates whether this handler might mutate an attribute value.
386             The default is to try to detect it based on analysis of the templates.
387              
388             =head2 C<< template >> B<< Str >>
389              
390             Specially formatted string (see section below) containing the Perl code
391             to implement the method.
392              
393             =head2 C<< lvalue_template >> B<< Maybe[Str] >>
394              
395             If defined, a shortcut for implementing it when the attribute slot
396             value can be used as an lvalue.
397              
398             =head2 C<< args >> B<< Maybe[PositiveOrZeroInt] >>
399              
400             The number of arguments which the method being generated expects
401             (does not include the attibute value itself).
402              
403             =head2 C<< min_args >> and C<< max_args >> B<< Maybe[PositiveOrZeroInt] >>
404              
405             For methods which take a variable number of arguments. If omitted, default
406             to C<args>.
407              
408             =head2 C<< signature >> B<< Maybe[ArrayRef[TypeTiny]] >>
409              
410             A signature for said arguments.
411              
412             =head2 C<< usage >> B<< Str >>
413              
414             A signature to show in documentation, like C<< '$index, $value' >>.
415             If not provided, will be generated magically from C<args>, C<min_args>,
416             and C<max_args>.
417              
418             =head2 C<< curried >> B<< Maybe[ArrayRef[Item]] >>
419              
420             An arrayref of curried arguments.
421              
422             =head2 C<< is_chainable >> B<Bool>
423              
424             Whether to force the generated method to be chainable.
425              
426             =head2 C<< no_validation_needed >> B<Bool>
427              
428             Whether to do less validation of input data.
429              
430             =head2 C<< default_for_reset >> B<< Maybe[Str] >>
431              
432             If this handler has to "reset" an attribute value to its default,
433             and the attribute doesn't have a default, this string of Perl code
434             is evaluated to provide a default. An example might be C<< "[]" >>.
435              
436             =head2 C<< prefer_shift_self >> B<Bool>
437              
438             Indicates this handler would prefer the code generator to shift
439             C<< $self >> off C<< @_ >>.
440              
441             =head2 C<< documentation >> B<< Maybe[Str] >>
442              
443             String of pod to describe the handler.
444              
445             =head2 C<< _examples >> B<< Maybe[CodeRef] >>
446              
447             This coderef, if called with parameters C<< $class >>, C<< $attr >>, and
448             C<< $method >>, will generate a code example to insert into the pod.
449              
450             =head2 C<< additional_validation >> B<< Maybe[CodeRef] >>
451              
452             Coderef providing a slightly annoying API. To be described later.
453              
454             =head2 C<< allow_getter_shortcuts >> B<Bool>
455              
456             Defaults to true. Rarely useful to override.
457              
458             =head1 METHODS
459              
460             =head2 C<< has_min_args() >> and C<< has_max_args() >>
461              
462             Indicate whether this handler has a defined min or max args.
463              
464             =head2 C<< install_method( %args ) >>
465              
466             The required arguments are C<method_name> and C<code_generator>.
467             Installs the delegated method into the target class (taken from
468             the code generator).
469              
470             =head2 C<< code_as_string( %args ) >>
471              
472             Same required arguments as C<install_method>, but returns the Perl
473             code for the method as a string.
474              
475             =head2 C<< curry( @args ) >>
476              
477             Pseudo-constructor.
478              
479             Creates a new Sub::HandlesVia::Handler object like this one, but
480             with the given arguments curried.
481              
482             =head2 C<< loose >>
483              
484             Pseudo-constructor.
485              
486             Creates a new Sub::HandlesVia::Handler object like this one, but
487             with looser argument validation.
488              
489             =head2 C<< chainable >>
490              
491             Pseudo-constructor.
492              
493             Creates a new Sub::HandlesVia::Handler object like this one, but
494             chainable.
495              
496             =head1 TEMPLATE FORMAT
497              
498             The template is a string of Perl code, except if the following special
499             things are found in it, they are substituted.
500              
501             =over
502              
503             =item C<< $SELF >>
504              
505             The invocant.
506              
507             =item C<< $SLOT >>
508              
509             Direct hashref access for the attribute.
510              
511             =item C<< $GET >>
512              
513             The current value of the attribute.
514              
515             =item C<< @ARG >>
516              
517             Any additional arguments passed to the delegated method.
518              
519             C<< $ARG[$n] >> will also work.
520              
521             =item C<< #ARG >>
522              
523             The number of additional arguments passed to the delegated method.
524              
525             =item C<< $ARG >>
526              
527             The first element in C<< @ARG >>.
528              
529             =item C<< $DEFAULT >>
530              
531             The attribute's default value, if known.
532              
533             =item C<< « EXPR » >>
534              
535             An expression in double angled quotes sets the attribute's value to the
536             expression.
537              
538             =back
539              
540             For example, a handler to halve the value of a numeric attribute might be:
541              
542             'Sub::HandlesVia::Handler'->new(
543             name => 'MyNumber:halve',
544             args => 0,
545             template => '« $GET / 2 »',
546             lvalue_template => '$GET /= 2',
547             );
548              
549             =head1 SUBCLASSES
550              
551             Sub::HandlesVia::Handler::Traditional and Sub::HandlesVia::Handler::CodeRef
552             are provided. See source code for this module for more info.
553              
554             =head1 BUGS
555              
556             Please report any bugs to
557             L<https://github.com/tobyink/p5-sub-handlesvia/issues>.
558              
559             =head1 SEE ALSO
560              
561             L<Sub::HandlesVia>.
562              
563             =head1 AUTHOR
564              
565             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
566              
567             =head1 COPYRIGHT AND LICENCE
568              
569             This software is copyright (c) 2020, 2022 by Toby Inkster.
570              
571             This is free software; you can redistribute it and/or modify it under
572             the same terms as the Perl 5 programming language system itself.
573              
574             =head1 DISCLAIMER OF WARRANTIES
575              
576             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
577             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
578             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
579