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   2683 use 5.008;
  96         528  
2 96     96   1005 use strict;
  96         470  
  96         2480  
3 96     96   545 use warnings;
  96         332  
  96         7952  
4              
5             package Sub::HandlesVia::Handler;
6              
7             our $AUTHORITY = 'cpan:TOBYINK';
8             our $VERSION = '0.050000';
9              
10 96     96   1278 use Sub::HandlesVia::Mite -all;
  96         247  
  96         1366  
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   11962 builder => sub { shift->args },
37             );
38              
39             # Not proper predicates because they check definedness
40 1403     1403 1 3733 sub has_min_args { defined shift->min_args }
41 5098     5098 1 12168 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   41248 no warnings 'uninitialized';
  96         235  
  96         131568  
108 2292     2292   3882 my $self = shift;
109 2292 100 66     5208 if ($self->has_max_args and $self->max_args==0) {
    50 66        
    50 33        
    50 33        
    50          
110 2074         6891 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         623 return '@args';
125             }
126              
127             sub curry {
128 1403     1403 1 4283 my ($self, @curried) = @_;
129 1403 50 66     3802 if ($self->has_max_args and @curried > $self->max_args) {
130 0         0 die "too many arguments to curry";
131             }
132 1403         10292 my %copy = %$self;
133 1403         4067 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       8834 signature => $self->signature ? do { my @sig = @{$self->{signature}}; splice(@sig,0,scalar(@curried)); \@sig } : undef,
  1189 100       2009  
  1189 100       3076  
  1189         2656  
  1189         5262  
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 54 my $self = shift;
151 20         160 ref($self)->new(%$self, is_chainable => 1);
152             }
153              
154             sub _real_additional_validation {
155 1381     1381   2526 my $me = shift;
156 1381         2713 my $av = $me->additional_validation;
157 1381 100       3975 return $av if ref $av;
158            
159 555         2600 my ($lib) = split /:/, $me->name;
160             return sub {
161 555     555   1052 my $self = shift;
162 555         1338 my ($sig_was_checked, $callbacks) = @_;
163 555         2815 my $ti = "Sub::HandlesVia::HandlerLibrary::$lib"->_type_inspector($callbacks->{isa});
164 555 100 66     4381 if ($ti and $ti->{trust_mutated} eq 'always') {
165 170         853 return { code => '1;', env => {} };
166             }
167 385 100 66     1730 if ($ti and $ti->{trust_mutated} eq 'maybe') {
168 150         792 return { code => '1;', env => {} };
169             }
170 235         862 return;
171 555 50       4129 } if $av eq 'no incoming values';
172              
173 0         0 return;
174             }
175              
176             sub lookup {
177 4244     4244 1 7890 my $class = shift;
178 4244 100       9095 my ($method, $traits) = map { ref($_) eq 'ARRAY' ? $_ : [$_] } @_;
  8488         29133  
179 4244         12245 my ($method_name, @curry) = @$method;
180            
181 4244         6258 my $handler;
182 4244         6621 my $make_chainable = 0;
183 4244         6207 my $make_loose = 0;
184              
185 4244 100       8497 if (ref $method_name eq 'CODE') {
186 1         10 $handler = Sub::HandlesVia::Handler::CodeRef->new(
187             name => '__ANON__',
188             delegated_coderef => $method_name,
189             );
190             }
191             else {
192 4243 100       12523 if ($method_name =~ /\s*\.\.\.$/) {
193 20         146 $method_name =~ s/\s*\.\.\.$//;
194 20         53 ++$make_chainable;
195             }
196 4243 50       9589 if ($method_name =~ /^\~\s*/) {
197 0         0 $method_name =~ s/^\~\s*//;
198 0         0 ++$make_loose;
199             }
200 4243 100       10005 if ($method_name =~ /^(.+?)\s*\-\>\s*(.+?)$/) {
201 3         12 $traits = [$1];
202 3         11 $method_name = $2;
203             }
204             }
205            
206 4244 100       9075 if (not $handler) {
207 4243         8471 SEARCH: for my $trait (@$traits) {
208 4243 50       12286 my $class = $trait =~ /:/
209             ? $trait
210             : "Sub::HandlesVia::HandlerLibrary::$trait";
211 4243 50       9786 if ( $class ne $trait ) {
212 4243         6780 local $@;
213 4243 50       278636 eval "require $class; 1"
214             or warn $@;
215             }
216 4243 100 66     37926 if ($class->isa('Sub::HandlesVia::HandlerLibrary') and $class->has_handler($method_name)) {
217 4242         12245 $handler = $class->get_handler($method_name);
218             }
219             }
220             }
221            
222 4244 100       10627 if (not $handler) {
223 1         7 $handler = Sub::HandlesVia::Handler::Traditional->new(name => $method_name);
224             }
225            
226 4244 100       11809 $handler = $handler->curry(@curry) if @curry;
227 4244 50       12004 $handler = $handler->loose if $make_loose;
228 4244 100       8368 $handler = $handler->chainable if $make_chainable;
229            
230 4244         13780 return $handler;
231             }
232              
233             sub install_method {
234 4244     4244 1 15534 my ( $self, %arg ) = @_;
235 4244 50       11335 my $gen = $arg{code_generator} or die;
236            
237 4244         16180 $gen->generate_and_install_method( $arg{method_name}, $self );
238            
239 4244         82263 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   1835 use Exporter::Shiny qw( handler );
  96         1114  
  96         1137  
265             sub _generate_handler {
266 107     107   16318 my $me = shift;
267             return sub {
268 4399 50   4399   297930 my (%args) = @_%2 ? (template=>@_) : @_;
269 4399         23485 $me->new(%args);
270 107         1114 };
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.050000';
278              
279 96     96   22801 use Sub::HandlesVia::Mite -all;
  96         328  
  96         788  
280             extends 'Sub::HandlesVia::Handler';
281              
282             has '+name' => ( required => true );
283              
284 1     1   4 sub is_mutator { 0 }
285              
286             sub template {
287 1     1   3 my $self = shift;
288 1         7 require B;
289 1         6 my $q_name = B::perlstring( $self->name );
290 1         17 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.050000';
299              
300 96     96   18995 use Sub::HandlesVia::Mite -all;
  96         268  
  96         535  
301             extends 'Sub::HandlesVia::Handler';
302              
303             has delegated_coderef => (
304             is => 'ro',
305             isa => 'CodeRef',
306             required => true,
307             );
308              
309 1     1   4 sub is_mutator { 0 }
310              
311             sub BUILD {
312 2 50   2   9 $_[1]{delegated_coderef} or die 'delegated_coderef required';
313             }
314              
315             sub _tweak_env {
316 1     1   3 my ( $self, $env ) = @_;
317 1         17 $env->{'$shv_callback'} = \($self->delegated_coderef);
318             }
319              
320             sub template {
321 1     1   4 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