File Coverage

blib/lib/HTML/FormFu/Util.pm
Criterion Covered Total %
statement 182 219 83.1
branch 78 116 67.2
condition 63 102 61.7
subroutine 41 44 93.1
pod 0 14 0.0
total 364 495 73.5


line stmt bran cond sub pod time code
1 408     408   283445 use strict;
  408         895  
  408         19118  
2              
3             package HTML::FormFu::Util;
4             # ABSTRACT: various utilities
5             $HTML::FormFu::Util::VERSION = '2.07';
6 408     408   2198 use warnings;
  408         764  
  408         12011  
7              
8 408     408   153093 use HTML::FormFu::Constants qw( $SPACE );
  408         1112  
  408         49358  
9 408     408   161588 use HTML::FormFu::Literal;
  408         1046  
  408         12902  
10 408     408   2694 use Scalar::Util qw( blessed reftype );
  408         779  
  408         20218  
11 408     408   2241 use Readonly;
  408         739  
  408         15048  
12 408     408   2187 use Exporter qw/ import /;
  408         830  
  408         10133  
13 408     408   2282 use Carp qw/ croak /;
  408         903  
  408         1387176  
14              
15             Readonly my $EMPTY_STR => q{};
16             Readonly my $SPACE => q{ };
17              
18             our $LAST_SUB = $EMPTY_STR;
19              
20             our @EXPORT_OK = qw(
21             DEBUG
22             DEBUG_PROCESS
23             DEBUG_CONSTRAINTS
24             DEBUG_CONSTRAINTS_WHEN
25             DEBUG_CONSTRAINTS_OTHERS
26             debug
27             append_xml_attribute
28             has_xml_attribute
29             remove_xml_attribute
30             _parse_args
31             require_class
32             xml_escape
33             literal
34             _filter_components
35             _get_elements
36             process_attrs
37             split_name
38             _merge_hashes
39             );
40              
41             # the empty prototype () means that when false, all debugging calls
42             # will be optimised out during compilation
43              
44             sub DEBUG {
45 9182 50   9182 0 61937 $ENV{HTML_FORMFU_DEBUG} || 0;
46             }
47              
48             sub DEBUG_PROCESS () {
49             DEBUG
50             || $ENV{HTML_FORMFU_DEBUG_PROCESS}
51 4824 50 33 4824 0 8602 || 0;
52             }
53              
54             sub DEBUG_CONSTRAINTS {
55             DEBUG
56             || DEBUG_PROCESS
57             || $ENV{HTML_FORMFU_DEBUG_CONSTRAINTS}
58 2709 50 33 2709 0 4475 || 0;
      33        
59             }
60              
61             sub DEBUG_CONSTRAINTS_WHEN {
62             DEBUG
63             || DEBUG_PROCESS
64             || $ENV{HTML_FORMFU_DEBUG_CONSTRAINTS}
65             || $ENV{HTML_FORMFU_DEBUG_CONSTRAINTS_WHEN}
66 168 50 33 168 0 291 || 0;
      33        
      33        
67             }
68              
69             sub DEBUG_CONSTRAINTS_OTHERS {
70             DEBUG
71             || DEBUG_PROCESS
72             || $ENV{HTML_FORMFU_DEBUG_CONSTRAINTS}
73             || $ENV{HTML_FORMFU_DEBUG_CONSTRAINTS_OTHERS}
74 659 50 33 659 0 1077 || 0;
      33        
      33        
75             }
76              
77             sub debug {
78 0     0 0 0 my ($message) = @_;
79              
80 0         0 my ( undef, undef, undef, $sub ) = caller(1);
81              
82 0         0 require Data::Dumper;
83              
84 0 0       0 warn "\n" if $sub ne $LAST_SUB;
85              
86 0 0       0 if ( @_ > 1 ) {
    0          
87 0 0       0 warn "$sub()\n" if $sub ne $LAST_SUB;
88              
89 0         0 while (@_) {
90 0         0 my $key = shift;
91 0         0 my $value = shift;
92              
93 0 0       0 if ( !defined $value ) {
    0          
94 0         0 $value = "is undef\n";
95             }
96             elsif ( ref $value ) {
97 0         0 $value = Data::Dumper::Dumper($value);
98 0         0 $value =~ s/^\$VAR1 = //;
99             }
100             else {
101 0         0 $value = "'$value'\n";
102             }
103              
104 0         0 warn "$key: $value";
105             }
106             }
107             elsif ( ref $message ) {
108 0 0       0 warn "$sub()\n" if $sub ne $LAST_SUB;
109              
110 0         0 $message = Data::Dumper::Dumper($message);
111 0         0 $message =~ s/^\$VAR1 = / /;
112              
113 0         0 warn "$message\n";
114             }
115             else {
116 0 0       0 warn "$sub\n" if $sub ne $LAST_SUB;
117              
118 0         0 warn "$message\n";
119             }
120              
121 0         0 $LAST_SUB = $sub;
122              
123 0         0 return;
124             }
125              
126             sub _filter_components {
127 19501     19501   33145 my ( $args, $components ) = @_;
128              
129 19501         41946 for my $name ( keys %$args ) {
130              
131             # get_errors() handles this itself
132 2153 100       6688 next if $name eq 'forced';
133              
134 382         555 my $value;
135              
136             @$components = grep {
137 382         750 $_->can($name)
138             && defined( $value = $_->$name )
139 303 50 33     2853 && $value eq $args->{$name}
140             } @$components;
141             }
142              
143 19501         51078 return $components;
144             }
145              
146             sub _get_elements {
147 11981     11981   21795 my ( $args, $elements ) = @_;
148              
149 11981         29386 for my $name ( keys %$args ) {
150 1430         2807 my $value;
151 1430 50       4437 next unless defined $args->{$name};
152             @$elements = grep {
153 1430         3580 $_->can($name)
154             && defined( $value = $_->$name )
155             && (
156             ref( $args->{$name} ) eq 'Regexp'
157             ? $value =~ $args->{$name}
158 5527 100 66     47800 : $value eq $args->{$name} )
    100          
159             } @$elements;
160             }
161              
162 11981         47385 return $elements;
163             }
164              
165             sub append_xml_attribute {
166 145     145 0 495 my ( $attrs, $key, $value ) = @_;
167              
168 145 50       602 croak '$attrs arg must be a hash reference'
169             if ref $attrs ne 'HASH';
170              
171 145         526 my %dispatcher = _append_subs();
172              
173 145 100 66     831 if ( exists $attrs->{$key} && defined $attrs->{$key} ) {
174 19         39 my $orig = 'string';
175              
176 19 100 66     96 if ( blessed $attrs->{$key}
177             && $attrs->{$key}->isa('HTML::FormFu::Literal') )
178             {
179 4         10 $orig = 'literal';
180             }
181              
182 19         32 my $new = 'string';
183              
184 19 100 66     64 if ( blessed $value
185             && $value->isa('HTML::FormFu::Literal') )
186             {
187 3         7 $new = 'literal';
188             }
189              
190 19         52 $attrs->{$key} = $dispatcher{$orig}->{$new}->( $attrs->{$key}, $value );
191             }
192             else {
193 126         425 $attrs->{$key} = $value;
194             }
195              
196 145         1768 return $attrs;
197             }
198              
199             sub _append_subs {
200             return (
201             literal => {
202             string => sub {
203 2     2   6 $_[0]->push( xml_escape(" $_[1]") );
204 2         6 return $_[0];
205             },
206             literal => sub {
207 2     2   32 $_[0]->push(" $_[1]");
208 2         6 return $_[0];
209             },
210             },
211             string => {
212             string => sub {
213 14     14   51 $_[0] .= " $_[1]";
214 14         32 return $_[0];
215             },
216             literal => sub {
217 1     1   5 $_[1]->unshift( xml_escape("$_[0] ") );
218 1         3 return $_[1];
219             },
220             },
221 145     145   2297 );
222             }
223              
224             sub has_xml_attribute {
225 17     17 0 337 my ( $attrs, $key, $value ) = @_;
226              
227 17 50       49 croak '$attrs arg must be a hash reference'
228             if ref $attrs ne 'HASH';
229              
230 17         32 my %dispatcher = _has_subs();
231              
232 17 50 33     87 if ( exists $attrs->{$key} && defined $attrs->{$key} ) {
233 17         29 my $orig = 'string';
234              
235 17 100 66     79 if ( blessed $attrs->{$key}
236             && $attrs->{$key}->isa('HTML::FormFu::Literal') )
237             {
238 8         14 $orig = 'literal';
239             }
240              
241 17         28 my $new = 'string';
242              
243 17 100 66     59 if ( blessed $value
244             && $value->isa('HTML::FormFu::Literal') )
245             {
246 8         16 $new = 'literal';
247             }
248              
249 17         42 return $dispatcher{$orig}->{$new}->( $attrs->{$key}, $value );
250             }
251              
252 0         0 return;
253             }
254              
255             sub _has_subs {
256             return (
257             literal => {
258             string => sub {
259 4     4   13 my $x = "$_[0]";
260 4         31 my $y = xml_escape("$_[1]");
261             return
262 4   100     159 $x =~ /^\Q$y\E ?/
263             || $x =~ / \Q$y\E /
264             || $x =~ / ?\Q$y\E$/;
265             },
266             literal => sub {
267 4     4   33 my $x = "$_[0]";
268 4         32 my $y = "$_[1]";
269             return
270 4   100     183 $x =~ /^\Q$y\E ?/
271             || $x =~ / \Q$y\E /
272             || $x =~ / ?\Q$y\E$/;
273             },
274             },
275             string => {
276             string => sub {
277 5     5   11 my ( $x, $y ) = @_;
278             return
279 5   100     178 $x =~ /^\Q$y\E ?/
280             || $x =~ / \Q$y\E /
281             || $x =~ / ?\Q$y\E$/;
282             },
283             literal => sub {
284 4     4   10 my $x = xml_escape( $_[0] );
285 4         13 my $y = "$_[1]";
286             return
287 4   100     167 $x =~ /^\Q$y\E ?/
288             || $x =~ / \Q$y\E /
289             || $x =~ / ?\Q$y\E$/;
290             },
291             },
292 17     17   161 );
293             }
294              
295             sub remove_xml_attribute {
296 18     18 0 568 my ( $attrs, $key, $value ) = @_;
297              
298 18 50       57 croak '$attrs arg must be a hash reference'
299             if ref $attrs ne 'HASH';
300              
301 18         32 my %dispatcher = _remove_subs();
302              
303 18 100 66     96 if ( exists $attrs->{$key} && defined $attrs->{$key} ) {
304 17         26 my $orig = 'string';
305              
306 17 100 66     101 if ( blessed $attrs->{$key}
307             && $attrs->{$key}->isa('HTML::FormFu::Literal') )
308             {
309 13         23 $orig = 'literal';
310             }
311              
312 17         28 my $new = 'string';
313              
314 17 100 66     58 if ( blessed $value
315             && $value->isa('HTML::FormFu::Literal') )
316             {
317 8         12 $new = 'literal';
318             }
319              
320 17         44 $attrs->{$key} = $dispatcher{$orig}->{$new}->( $attrs->{$key}, $value );
321             }
322              
323 18         236 return $attrs;
324             }
325              
326             sub _remove_subs {
327             return (
328             literal => {
329             string => sub {
330 6     6   20 my $x = "$_[0]";
331 6         48 my $y = xml_escape("$_[1]");
332 6 100 100     97 $x =~ s/^\Q$y\E ?//
333             || $x =~ s/ \Q$y\E / /
334             || $x =~ s/ ?\Q$y\E$//;
335 6         15 return literal($x);
336             },
337             literal => sub {
338 7     7   21 my $x = "$_[0]";
339 7         51 my $y = "$_[1]";
340 7 100 100     172 $x =~ s/^\Q$y\E ?//
341             || $x =~ s/ \Q$y\E / /
342             || $x =~ s/ ?\Q$y\E$//;
343 7         21 return literal($x);
344             },
345             },
346             string => {
347             string => sub {
348 3     3   7 my ( $x, $y ) = @_;
349 3 100 100     64 $x =~ s/^\Q$y\E ?//
350             || $x =~ s/ \Q$y\E / /
351             || $x =~ s/ ?\Q$y\E$//;
352 3         12 return $x;
353             },
354             literal => sub {
355 1     1   3 my $x = xml_escape( $_[0] );
356 1         4 my $y = "$_[1]";
357 1 50 33     26 $x =~ s/^\Q$y\E ?//
358             || $x =~ s/ \Q$y\E / /
359             || $x =~ s/ ?\Q$y\E$//;
360 1         3 return literal($x);
361             },
362             },
363 18     18   186 );
364             }
365              
366             sub _parse_args {
367              
368 32678 100   32678   71068 if ( !@_ ) {
    100          
    100          
369 29224         59148 return;
370             }
371             elsif ( @_ > 1 ) {
372 14         61 return @_;
373             }
374             elsif ( ref $_[0] ) {
375 3095         4935 return %{ $_[0] };
  3095         15097  
376             }
377             else {
378 345         1505 return ( name => $_[0] );
379             }
380             }
381              
382             sub require_class {
383 1961     1961 0 5560 my ($class) = @_;
384              
385 1961 50       6040 croak "class argument missing" if !defined $class;
386              
387 1961         11093 $class =~ s|::|/|g;
388 1961         4933 $class .= ".pm";
389              
390 1961 100       7386 if ( !exists $::INC{$class} ) {
391 997         4310 eval { require $class };
  997         548754  
392 997 50       135039 croak $@ if $@;
393              
394             }
395              
396 1961         6706 return;
397             }
398              
399             sub xml_escape {
400 56567     56567 0 81495 my $val = shift;
401              
402 56567 100       140511 return undef if !defined $val; ## no critic (ProhibitExplicitReturnUndef);
403              
404 52952 100       102253 if ( ref $val eq 'HASH' ) {
    50          
    100          
405 31844         54795 my %val = %$val;
406              
407 31844         76331 while ( my ( $key, $value ) = each %val ) {
408 995         3234 $val{$key} = xml_escape($value);
409             }
410              
411 31844         130075 return \%val;
412             }
413             elsif ( ref $val eq 'ARRAY' ) {
414 0         0 my @val = @$val;
415 0         0 my @new;
416 0         0 for my $val (@val) {
417 0         0 push @new, xml_escape($val);
418             }
419 0         0 return \@new;
420             }
421             elsif ( ref $val ) {
422 67         757 return "$val";
423             }
424              
425 21041 100       36415 return $val if !length $val;
426              
427 20643         31053 $val =~ s/&/&/g;
428 20643         26499 $val =~ s/"/"/g;
429 20643         26554 $val =~ s/'/'/g;
430 20643         26625 $val =~ s/</&lt;/g;
431 20643         25879 $val =~ s/>/&gt;/g;
432              
433 20643         54336 return $val;
434             }
435              
436             sub literal {
437 70     70 0 1973 return HTML::FormFu::Literal->new(@_);
438             }
439              
440             sub process_attrs {
441 6658     6658 0 11036 my ($attrs) = @_;
442              
443 6658 50       17107 croak 'argument to process_attrs() must be a hashref'
444             if reftype($attrs) ne 'HASH';
445              
446 6658         8845 my @attribute_parts;
447              
448 6658         14898 for my $attribute ( sort keys %$attrs ) {
449             my $value
450             = defined $attrs->{$attribute}
451 952 50       2898 ? $attrs->{$attribute}
452             : $EMPTY_STR;
453              
454 952         3998 push @attribute_parts, sprintf '%s="%s"', $attribute, $value;
455             }
456              
457 6658         19029 my $xml = join $SPACE, @attribute_parts;
458              
459 6658 100       37104 if ( length $xml ) {
460 725         2047 $xml = " $xml";
461             }
462              
463 6658         25989 return $xml;
464             }
465              
466             sub split_name {
467 8314     8314 0 14319 my ($name) = @_;
468              
469 8314 50       16992 croak "split_name requires 1 arg" if @_ != 1;
470              
471 8314 50       16646 return if !defined $name;
472              
473 8314 50       27014 if ( $name =~ /^ \w+ \[ /x ) {
    100          
474              
475             # copied from Catalyst::Plugin::Params::Nested::Expander
476             # redistributed under the same terms as Perl
477              
478 0         0 return grep {defined} (
  0         0  
479             $name =~ /
480             ^ (\w+) # root param
481             | \[ (\w+) \] # nested
482             /gx
483             );
484             }
485             elsif ( $name =~ /\./ ) {
486              
487             # Copied from CGI::Expand
488             # redistributed under the same terms as Perl
489              
490             # m// splits on unescaped '.' chars. Can't fail b/c \G on next
491             # non ./ * -> escaped anything -> non ./ *
492 1818         7152 $name =~ m/^ ( [^\\\.]* (?: \\(?:.|$) [^\\\.]* )* ) /gx;
493 1818         3866 my $first = $1;
494 1818         3178 $first =~ s/\\(.)/$1/g; # remove escaping
495              
496 1818         6958 my (@segments) = $name =~
497              
498             # . -> ( non ./ * -> escaped anything -> non ./ * )
499             m/\G (?:[\.]) ( [^\\\.]* (?: \\(?:.|$) [^\\\.]* )* ) /gx;
500              
501             # Escapes removed later, can be used to avoid using as array index
502              
503 1818         8136 return ( $first, @segments );
504             }
505              
506 6496         17227 return ($name);
507             }
508              
509             # sub _merge_hashes originally copied from Catalyst::Utils::merge_hashes()
510             # redistributed under the same terms as Perl
511              
512             sub _merge_hashes {
513 658     658   1498 my ( $lefthash, $righthash ) = @_;
514              
515 658 100 100     3184 return $lefthash if !defined $righthash || !keys %$righthash;
516              
517 143         422 my %merged = %$lefthash;
518              
519 143         880 while ( my ( $key, $right_value ) = each %$righthash ) {
520              
521 189         391 my $left_value = $lefthash->{$key};
522              
523 189 100       479 if ( exists $lefthash->{$key} ) {
524              
525             my $is_left_ref = exists $lefthash->{$key}
526 23   66     120 && ref $lefthash->{$key} eq 'HASH';
527              
528 23 100 100     260 if ( ref $left_value eq 'HASH' && ref $right_value eq 'ARRAY' ) {
    50 33        
    50 33        
    100 66        
529 1         6 $merged{$key} = _merge_hash_array( $left_value, $right_value );
530             }
531             elsif ( ref $left_value eq 'ARRAY' && ref $right_value eq 'HASH' ) {
532 0         0 $merged{$key} = _merge_array_hash( $left_value, $right_value );
533             }
534             elsif ( ref $left_value eq 'ARRAY' && ref $right_value eq 'ARRAY' )
535             {
536 0         0 $merged{$key} = _merge_array_array( $left_value, $right_value );
537             }
538             elsif ( ref $left_value eq 'HASH' && ref $right_value eq 'HASH' ) {
539 20         89 $merged{$key} = _merge_hashes( $left_value, $right_value );
540             }
541             else {
542 2         8 $merged{$key} = $right_value;
543             }
544             }
545             else {
546 166         611 $merged{$key} = $right_value;
547             }
548             }
549              
550 143         555 return \%merged;
551             }
552              
553             sub _merge_hash_array {
554 1     1   3 my ( $left, $right ) = @_;
555              
556 1         15 return [ $left, @$right ];
557             }
558              
559             sub _merge_array_hash {
560 0     0     my ( $left, $right ) = @_;
561              
562 0           return [ @$left, $right ];
563             }
564              
565             sub _merge_array_array {
566 0     0     my ( $left, $right ) = @_;
567              
568 0           return [ @$left, @$right ];
569             }
570              
571             1;
572              
573             __END__
574              
575             =pod
576              
577             =encoding UTF-8
578              
579             =head1 NAME
580              
581             HTML::FormFu::Util - various utilities
582              
583             =head1 VERSION
584              
585             version 2.07
586              
587             =head1 AUTHOR
588              
589             Carl Franks <cpan@fireartist.com>
590              
591             =head1 COPYRIGHT AND LICENSE
592              
593             This software is copyright (c) 2018 by Carl Franks.
594              
595             This is free software; you can redistribute it and/or modify it under
596             the same terms as the Perl 5 programming language system itself.
597              
598             =cut