File Coverage

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


line stmt bran cond sub pod time code
1             package HTML::FormFu::Util;
2              
3 404     404   147910 use strict;
  404         431  
  404         8836  
4 404     404   1221 use warnings;
  404         408  
  404         13450  
5             our $VERSION = '2.05'; # VERSION
6              
7 404     404   119515 use HTML::FormFu::Constants qw( $SPACE );
  404         689  
  404         41599  
8 404     404   126723 use HTML::FormFu::Literal;
  404         671  
  404         9519  
9 404     404   1722 use Scalar::Util qw( blessed reftype );
  404         409  
  404         16787  
10 404     404   1369 use Readonly;
  404         412  
  404         12062  
11 404     404   1281 use Exporter qw/ import /;
  404         419  
  404         7678  
12 404     404   1339 use Carp qw/ croak /;
  404         436  
  404         984662  
13              
14             Readonly my $EMPTY_STR => q{};
15             Readonly my $SPACE => q{ };
16              
17             our $LAST_SUB = $EMPTY_STR;
18              
19             our @EXPORT_OK = qw(
20             DEBUG
21             DEBUG_PROCESS
22             DEBUG_CONSTRAINTS
23             DEBUG_CONSTRAINTS_WHEN
24             DEBUG_CONSTRAINTS_OTHERS
25             debug
26             append_xml_attribute
27             has_xml_attribute
28             remove_xml_attribute
29             _parse_args
30             require_class
31             xml_escape
32             literal
33             _filter_components
34             _get_elements
35             process_attrs
36             split_name
37             _merge_hashes
38             );
39              
40             # the empty prototype () means that when false, all debugging calls
41             # will be optimised out during compilation
42              
43             sub DEBUG {
44 9521 50   9521 0 59645 $ENV{HTML_FORMFU_DEBUG} || 0;
45             }
46              
47             sub DEBUG_PROCESS () {
48             DEBUG
49             || $ENV{HTML_FORMFU_DEBUG_PROCESS}
50 4993 50 33 4993 0 5474 || 0;
51             }
52              
53             sub DEBUG_CONSTRAINTS {
54             DEBUG
55             || DEBUG_PROCESS
56             || $ENV{HTML_FORMFU_DEBUG_CONSTRAINTS}
57 2879 50 33 2879 0 3022 || 0;
      33        
58             }
59              
60             sub DEBUG_CONSTRAINTS_WHEN {
61             DEBUG
62             || DEBUG_PROCESS
63             || $ENV{HTML_FORMFU_DEBUG_CONSTRAINTS}
64             || $ENV{HTML_FORMFU_DEBUG_CONSTRAINTS_WHEN}
65 168 50 33 168 0 173 || 0;
      33        
      33        
66             }
67              
68             sub DEBUG_CONSTRAINTS_OTHERS {
69             DEBUG
70             || DEBUG_PROCESS
71             || $ENV{HTML_FORMFU_DEBUG_CONSTRAINTS}
72             || $ENV{HTML_FORMFU_DEBUG_CONSTRAINTS_OTHERS}
73 659 50 33 659 0 1213 || 0;
      33        
      33        
74             }
75              
76             sub debug {
77 0     0 0 0 my ($message) = @_;
78              
79 0         0 my ( undef, undef, undef, $sub ) = caller(1);
80              
81 0         0 require Data::Dumper;
82              
83 0 0       0 warn "\n" if $sub ne $LAST_SUB;
84              
85 0 0       0 if ( @_ > 1 ) {
    0          
86 0 0       0 warn "$sub()\n" if $sub ne $LAST_SUB;
87              
88 0         0 while (@_) {
89 0         0 my $key = shift;
90 0         0 my $value = shift;
91              
92 0 0       0 if ( !defined $value ) {
    0          
93 0         0 $value = "is undef\n";
94             }
95             elsif ( ref $value ) {
96 0         0 $value = Data::Dumper::Dumper($value);
97 0         0 $value =~ s/^\$VAR1 = //;
98             }
99             else {
100 0         0 $value = "'$value'\n";
101             }
102              
103 0         0 warn "$key: $value";
104             }
105             }
106             elsif ( ref $message ) {
107 0 0       0 warn "$sub()\n" if $sub ne $LAST_SUB;
108              
109 0         0 $message = Data::Dumper::Dumper($message);
110 0         0 $message =~ s/^\$VAR1 = / /;
111              
112 0         0 warn "$message\n";
113             }
114             else {
115 0 0       0 warn "$sub\n" if $sub ne $LAST_SUB;
116              
117 0         0 warn "$message\n";
118             }
119              
120 0         0 $LAST_SUB = $sub;
121              
122 0         0 return;
123             }
124              
125             sub _filter_components {
126 19702     19702   15569 my ( $args, $components ) = @_;
127              
128 19702         24910 for my $name ( keys %$args ) {
129              
130             # get_errors() handles this itself
131 2146 100       4454 next if $name eq 'forced';
132              
133 382         2010 my $value;
134              
135             @$components = grep {
136 382         438 $_->can($name)
137             && defined( $value = $_->$name )
138 304 50 33     2366 && $value eq $args->{$name}
139             } @$components;
140             }
141              
142 19702         35092 return $components;
143             }
144              
145             sub _get_elements {
146 11990     11990   10373 my ( $args, $elements ) = @_;
147              
148 11990         18820 for my $name ( keys %$args ) {
149 1480         1534 my $value;
150 1480 50       3917 next unless defined $args->{$name};
151             @$elements = grep {
152 1480         2387 $_->can($name)
153             && defined( $value = $_->$name )
154             && (
155             ref( $args->{$name} ) eq 'Regexp'
156             ? $value =~ $args->{$name}
157 5763 100 66     39248 : $value eq $args->{$name} )
    100          
158             } @$elements;
159             }
160              
161 11990         34619 return $elements;
162             }
163              
164             sub append_xml_attribute {
165 144     144 0 238 my ( $attrs, $key, $value ) = @_;
166              
167 144 50       462 croak '$attrs arg must be a hash reference'
168             if ref $attrs ne 'HASH';
169              
170 144         359 my %dispatcher = _append_subs();
171              
172 144 100 66     2172 if ( exists $attrs->{$key} && defined $attrs->{$key} ) {
173 19         20 my $orig = 'string';
174              
175 19 100 66     99 if ( blessed $attrs->{$key}
176             && $attrs->{$key}->isa('HTML::FormFu::Literal') )
177             {
178 4         5 $orig = 'literal';
179             }
180              
181 19         21 my $new = 'string';
182              
183 19 100 66     78 if ( blessed $value
184             && $value->isa('HTML::FormFu::Literal') )
185             {
186 3         5 $new = 'literal';
187             }
188              
189 19         43 $attrs->{$key} = $dispatcher{$orig}->{$new}->( $attrs->{$key}, $value );
190             }
191             else {
192 125         300 $attrs->{$key} = $value;
193             }
194              
195 144         1414 return $attrs;
196             }
197              
198             sub _append_subs {
199             return (
200             literal => {
201             string => sub {
202 2     2   5 $_[0]->push( xml_escape(" $_[1]") );
203 2         2 return $_[0];
204             },
205             literal => sub {
206 2     2   25 $_[0]->push(" $_[1]");
207 2         4 return $_[0];
208             },
209             },
210             string => {
211             string => sub {
212 14     14   25 $_[0] .= " $_[1]";
213 14         24 return $_[0];
214             },
215             literal => sub {
216 1     1   4 $_[1]->unshift( xml_escape("$_[0] ") );
217 1         2 return $_[1];
218             },
219             },
220 144     144   1767 );
221             }
222              
223             sub has_xml_attribute {
224 17     17 0 181 my ( $attrs, $key, $value ) = @_;
225              
226 17 50       35 croak '$attrs arg must be a hash reference'
227             if ref $attrs ne 'HASH';
228              
229 17         23 my %dispatcher = _has_subs();
230              
231 17 50 33     72 if ( exists $attrs->{$key} && defined $attrs->{$key} ) {
232 17         16 my $orig = 'string';
233              
234 17 100 66     73 if ( blessed $attrs->{$key}
235             && $attrs->{$key}->isa('HTML::FormFu::Literal') )
236             {
237 8         8 $orig = 'literal';
238             }
239              
240 17         16 my $new = 'string';
241              
242 17 100 66     51 if ( blessed $value
243             && $value->isa('HTML::FormFu::Literal') )
244             {
245 8         7 $new = 'literal';
246             }
247              
248 17         34 return $dispatcher{$orig}->{$new}->( $attrs->{$key}, $value );
249             }
250              
251 0         0 return;
252             }
253              
254             sub _has_subs {
255             return (
256             literal => {
257             string => sub {
258 4     4   9 my $x = "$_[0]";
259 4         21 my $y = xml_escape("$_[1]");
260             return
261 4   100     127 $x =~ /^\Q$y\E ?/
262             || $x =~ / \Q$y\E /
263             || $x =~ / ?\Q$y\E$/;
264             },
265             literal => sub {
266 4     4   28 my $x = "$_[0]";
267 4         22 my $y = "$_[1]";
268             return
269 4   100     167 $x =~ /^\Q$y\E ?/
270             || $x =~ / \Q$y\E /
271             || $x =~ / ?\Q$y\E$/;
272             },
273             },
274             string => {
275             string => sub {
276 5     5   6 my ( $x, $y ) = @_;
277             return
278 5   100     149 $x =~ /^\Q$y\E ?/
279             || $x =~ / \Q$y\E /
280             || $x =~ / ?\Q$y\E$/;
281             },
282             literal => sub {
283 4     4   6 my $x = xml_escape( $_[0] );
284 4         10 my $y = "$_[1]";
285             return
286 4   100     170 $x =~ /^\Q$y\E ?/
287             || $x =~ / \Q$y\E /
288             || $x =~ / ?\Q$y\E$/;
289             },
290             },
291 17     17   141 );
292             }
293              
294             sub remove_xml_attribute {
295 18     18 0 329 my ( $attrs, $key, $value ) = @_;
296              
297 18 50       42 croak '$attrs arg must be a hash reference'
298             if ref $attrs ne 'HASH';
299              
300 18         23 my %dispatcher = _remove_subs();
301              
302 18 100 66     73 if ( exists $attrs->{$key} && defined $attrs->{$key} ) {
303 17         15 my $orig = 'string';
304              
305 17 100 66     101 if ( blessed $attrs->{$key}
306             && $attrs->{$key}->isa('HTML::FormFu::Literal') )
307             {
308 13         13 $orig = 'literal';
309             }
310              
311 17         14 my $new = 'string';
312              
313 17 100 66     58 if ( blessed $value
314             && $value->isa('HTML::FormFu::Literal') )
315             {
316 8         7 $new = 'literal';
317             }
318              
319 17         30 $attrs->{$key} = $dispatcher{$orig}->{$new}->( $attrs->{$key}, $value );
320             }
321              
322 18         150 return $attrs;
323             }
324              
325             sub _remove_subs {
326             return (
327             literal => {
328             string => sub {
329 6     6   17 my $x = "$_[0]";
330 6         35 my $y = xml_escape("$_[1]");
331 6 100 100     79 $x =~ s/^\Q$y\E ?//
332             || $x =~ s/ \Q$y\E / /
333             || $x =~ s/ ?\Q$y\E$//;
334 6         9 return literal($x);
335             },
336             literal => sub {
337 7     7   18 my $x = "$_[0]";
338 7         36 my $y = "$_[1]";
339 7 100 100     147 $x =~ s/^\Q$y\E ?//
340             || $x =~ s/ \Q$y\E / /
341             || $x =~ s/ ?\Q$y\E$//;
342 7         291 return literal($x);
343             },
344             },
345             string => {
346             string => sub {
347 3     3   4 my ( $x, $y ) = @_;
348 3 100 100     51 $x =~ s/^\Q$y\E ?//
349             || $x =~ s/ \Q$y\E / /
350             || $x =~ s/ ?\Q$y\E$//;
351 3         7 return $x;
352             },
353             literal => sub {
354 1     1   2 my $x = xml_escape( $_[0] );
355 1         3 my $y = "$_[1]";
356 1 50 33     21 $x =~ s/^\Q$y\E ?//
357             || $x =~ s/ \Q$y\E / /
358             || $x =~ s/ ?\Q$y\E$//;
359 1         2 return literal($x);
360             },
361             },
362 18     18   174 );
363             }
364              
365             sub _parse_args {
366              
367 32877 100   32877   46970 if ( !@_ ) {
    100          
    100          
368 29380         45209 return;
369             }
370             elsif ( @_ > 1 ) {
371 14         50 return @_;
372             }
373             elsif ( ref $_[0] ) {
374 3125         2967 return %{ $_[0] };
  3125         12647  
375             }
376             else {
377 358         1262 return ( name => $_[0] );
378             }
379             }
380              
381             sub require_class {
382 1961     1961 0 3382 my ($class) = @_;
383              
384 1961 50       7953 croak "class argument missing" if !defined $class;
385              
386 1961         19054 $class =~ s|::|/|g;
387 1961         3683 $class .= ".pm";
388              
389 1961 100       5699 if ( !exists $::INC{$class} ) {
390 996         1589 eval { require $class };
  996         507936  
391 996 50       84777 croak $@ if $@;
392              
393             }
394              
395 1961         5260 return;
396             }
397              
398             sub xml_escape {
399 56596     56596 0 41894 my $val = shift;
400              
401 56596 100       105191 return undef if !defined $val; ## no critic (ProhibitExplicitReturnUndef);
402              
403 52993 100       74405 if ( ref $val eq 'HASH' ) {
    50          
    100          
404 31930         37481 my %val = %$val;
405              
406 31930         51098 while ( my ( $key, $value ) = each %val ) {
407 981         2120 $val{$key} = xml_escape($value);
408             }
409              
410 31930         96173 return \%val;
411             }
412             elsif ( ref $val eq 'ARRAY' ) {
413 0         0 my @val = @$val;
414 0         0 my @new;
415 0         0 for my $val (@val) {
416 0         0 push @new, xml_escape($val);
417             }
418 0         0 return \@new;
419             }
420             elsif ( ref $val ) {
421 67         563 return "$val";
422             }
423              
424 20996 100       25813 return $val if !length $val;
425              
426 20602         16976 $val =~ s/&/&/g;
427 20602         13469 $val =~ s/"/"/g;
428 20602         13549 $val =~ s/'/'/g;
429 20602         13325 $val =~ s/</&lt;/g;
430 20602         13707 $val =~ s/>/&gt;/g;
431              
432 20602         39022 return $val;
433             }
434              
435             sub literal {
436 70     70 0 1251 return HTML::FormFu::Literal->new(@_);
437             }
438              
439             sub process_attrs {
440 6627     6627 0 5208 my ($attrs) = @_;
441              
442 6627 50       13288 croak 'argument to process_attrs() must be a hashref'
443             if reftype($attrs) ne 'HASH';
444              
445 6627         4333 my @attribute_parts;
446              
447 6627         9541 for my $attribute ( sort keys %$attrs ) {
448             my $value
449             = defined $attrs->{$attribute}
450 937 50       1916 ? $attrs->{$attribute}
451             : $EMPTY_STR;
452              
453 937         2685 push @attribute_parts, sprintf '%s="%s"', $attribute, $value;
454             }
455              
456 6627         13578 my $xml = join $SPACE, @attribute_parts;
457              
458 6627 100       22362 if ( length $xml ) {
459 714         1294 $xml = " $xml";
460             }
461              
462 6627         19731 return $xml;
463             }
464              
465             sub split_name {
466 8409     8409 0 6846 my ($name) = @_;
467              
468 8409 50       12413 croak "split_name requires 1 arg" if @_ != 1;
469              
470 8409 50       11403 return if !defined $name;
471              
472 8409 50       19414 if ( $name =~ /^ \w+ \[ /x ) {
    100          
473              
474             # copied from Catalyst::Plugin::Params::Nested::Expander
475             # redistributed under the same terms as Perl
476              
477 0         0 return grep {defined} (
  0         0  
478             $name =~ /
479             ^ (\w+) # root param
480             | \[ (\w+) \] # nested
481             /gx
482             );
483             }
484             elsif ( $name =~ /\./ ) {
485              
486             # Copied from CGI::Expand
487             # redistributed under the same terms as Perl
488              
489             # m// splits on unescaped '.' chars. Can't fail b/c \G on next
490             # non ./ * -> escaped anything -> non ./ *
491 1778         4221 $name =~ m/^ ( [^\\\.]* (?: \\(?:.|$) [^\\\.]* )* ) /gx;
492 1778         2196 my $first = $1;
493 1778         1647 $first =~ s/\\(.)/$1/g; # remove escaping
494              
495 1778         4833 my (@segments) = $name =~
496              
497             # . -> ( non ./ * -> escaped anything -> non ./ * )
498             m/\G (?:[\.]) ( [^\\\.]* (?: \\(?:.|$) [^\\\.]* )* ) /gx;
499              
500             # Escapes removed later, can be used to avoid using as array index
501              
502 1778         5443 return ( $first, @segments );
503             }
504              
505 6631         12342 return ($name);
506             }
507              
508             # sub _merge_hashes originally copied from Catalyst::Utils::merge_hashes()
509             # redistributed under the same terms as Perl
510              
511             sub _merge_hashes {
512 626     626   671 my ( $lefthash, $righthash ) = @_;
513              
514 626 100 66     2296 return $lefthash if !defined $righthash || !keys %$righthash;
515              
516 117         248 my %merged = %$lefthash;
517              
518 117         354 while ( my ( $key, $right_value ) = each %$righthash ) {
519              
520 159         194 my $left_value = $lefthash->{$key};
521              
522 159 100       276 if ( exists $lefthash->{$key} ) {
523              
524             my $is_left_ref = exists $lefthash->{$key}
525 21   66     90 && ref $lefthash->{$key} eq 'HASH';
526              
527 21 100 100     210 if ( ref $left_value eq 'HASH' && ref $right_value eq 'ARRAY' ) {
    50 33        
    50 33        
    100 66        
528 1         4 $merged{$key} = _merge_hash_array( $left_value, $right_value );
529             }
530             elsif ( ref $left_value eq 'ARRAY' && ref $right_value eq 'HASH' ) {
531 0         0 $merged{$key} = _merge_array_hash( $left_value, $right_value );
532             }
533             elsif ( ref $left_value eq 'ARRAY' && ref $right_value eq 'ARRAY' )
534             {
535 0         0 $merged{$key} = _merge_array_array( $left_value, $right_value );
536             }
537             elsif ( ref $left_value eq 'HASH' && ref $right_value eq 'HASH' ) {
538 19         44 $merged{$key} = _merge_hashes( $left_value, $right_value );
539             }
540             else {
541 1         3 $merged{$key} = $right_value;
542             }
543             }
544             else {
545 138         390 $merged{$key} = $right_value;
546             }
547             }
548              
549 117         322 return \%merged;
550             }
551              
552             sub _merge_hash_array {
553 1     1   1 my ( $left, $right ) = @_;
554              
555 1         4 return [ $left, @$right ];
556             }
557              
558             sub _merge_array_hash {
559 0     0     my ( $left, $right ) = @_;
560              
561 0           return [ @$left, $right ];
562             }
563              
564             sub _merge_array_array {
565 0     0     my ( $left, $right ) = @_;
566              
567 0           return [ @$left, @$right ];
568             }
569              
570             1;