File Coverage

blib/lib/Data/MuForm/Params.pm
Criterion Covered Total %
statement 34 59 57.6
branch 12 30 40.0
condition 2 6 33.3
subroutine 4 7 57.1
pod 0 4 0.0
total 52 106 49.0


line stmt bran cond sub pod time code
1             package Data::MuForm::Params;
2             # ABSTRACT: params handling
3              
4 82     82   311 use Moo;
  82         105  
  82         554  
5 82     82   18203 use Carp;
  82         127  
  82         54528  
6              
7             has 'separator' => ( is => 'rw', default => '.' );
8              
9             sub split_name {
10 293     293 0 330 my ( $self, $name, $sep ) = @_;
11              
12 293   33     457 $sep ||= $self->separator;
13 293         369 $sep = "\Q$sep";
14              
15 293 50       490 if ( $sep eq '[]' ) {
16 0         0 return grep { defined } (
  0         0  
17             $name =~ /
18             ^ (\w+) # root param
19             | \[ (\w+) \] # nested
20             /gx
21             );
22             }
23              
24             # These next two regexes are the escaping aware equivalent
25             # to the following:
26             # my ($first, @segments) = split(/\./, $name, -1);
27              
28             # m// splits on unescaped '.' chars. Can't fail b/c \G on next
29             # non ./ * -> escaped anything -> non ./ *
30 293         3205 $name =~ m/^ ( [^\\$sep]* (?: \\(?:.|$) [^\\$sep]* )* ) /gx;
31 293         450 my $first = $1;
32 293         325 $first =~ s/\\(.)/$1/g; # remove escaping
33              
34 293         1802 my (@segments) = $name =~
35             # . -> ( non ./ * -> escaped anything -> non ./ * )
36             m/\G (?:[$sep]) ( [^\\$sep]* (?: \\(?:.|$) [^\\$sep]* )* ) /gx;
37             # Escapes removed later, can be used to avoid using as array index
38              
39 293         1537 return ( $first, @segments );
40             }
41              
42             sub expand_hash {
43 146     146 0 216 my ( $self, $flat, $sep ) = @_;
44              
45 146         189 my $deep = {};
46 146   33     834 $sep ||= $self->separator;
47              
48 146         425 for my $name ( keys %$flat ) {
49              
50 293         548 my ( $first, @segments ) = $self->split_name( $name, $sep );
51              
52 293         467 my $box_ref = \$deep->{$first};
53 293         443 for (@segments) {
54 148 100       322 if ( /^(0|[1-9]\d*)$/ ) {
55 59 100       103 $$box_ref = [] unless defined $$box_ref;
56 59 50       104 croak "HFH: param clash for $name=$_"
57             unless ref $$box_ref eq 'ARRAY';
58 59         116 $box_ref = \( $$box_ref->[$1] );
59             }
60             else {
61 89 50       156 s/\\(.)/$1/g if $sep; # remove escaping
62 89 100       159 $$box_ref = {} unless defined $$box_ref;
63 89 50       156 $$box_ref = { '' => $$box_ref } if ( !ref $$box_ref );
64 89 50       144 croak "HFH: param clash for $name=$_"
65             unless ref $$box_ref eq 'HASH';
66 89         180 $box_ref = \( $$box_ref->{$_} );
67             }
68             }
69 293 50       505 if ( defined $$box_ref ) {
70 0 0       0 croak "HFH: param clash for $name value $flat->{$name}"
71             if ref $$box_ref ne 'HASH';
72 0         0 $box_ref = \( $$box_ref->{''} );
73             }
74 293         442 $$box_ref = $flat->{$name};
75             }
76 146         350 return $deep;
77             }
78              
79             sub collapse_hash {
80 0     0 0   my $self = shift;
81 0           my $deep = shift;
82 0           my $flat = {};
83              
84 0           $self->_collapse_hash( $deep, $flat, () );
85 0           return $flat;
86             }
87              
88             sub join_name {
89 0     0 0   my ( $self, @array ) = @_;
90 0           my $sep = substr( $self->separator, 0, 1 );
91 0           return join $sep, @array;
92             }
93              
94             sub _collapse_hash {
95 0     0     my ( $self, $deep, $flat, @segments ) = @_;
96              
97 0 0         if ( !ref $deep ) {
    0          
    0          
98 0           my $name = $self->join_name(@segments);
99 0           $flat->{$name} = $deep;
100             }
101             elsif ( ref $deep eq 'HASH' ) {
102 0           for ( keys %$deep ) {
103             # escape \ and separator chars (once only, at this level)
104 0           my $name = $_;
105 0 0         if ( defined( my $sep = $self->separator ) ) {
106 0           $sep = "\Q$sep";
107 0           $name =~ s/([\\$sep])/\\$1/g;
108             }
109 0           $self->_collapse_hash( $deep->{$_}, $flat, @segments, $name );
110             }
111             }
112             elsif ( ref $deep eq 'ARRAY' ) {
113 0           for ( 0 .. $#$deep ) {
114 0 0         $self->_collapse_hash( $deep->[$_], $flat, @segments, $_ )
115             if defined $deep->[$_];
116             }
117             }
118             else {
119 0           croak "Unknown reference type for ", $self->join_name(@segments), ":", ref $deep;
120             }
121             }
122              
123             1;
124              
125             __END__
126              
127             =pod
128              
129             =encoding UTF-8
130              
131             =head1 NAME
132              
133             Data::MuForm::Params - params handling
134              
135             =head1 VERSION
136              
137             version 0.04
138              
139             =head1 AUTHOR
140              
141             Gerda Shank
142              
143             =head1 COPYRIGHT AND LICENSE
144              
145             This software is copyright (c) 2017 by Gerda Shank.
146              
147             This is free software; you can redistribute it and/or modify it under
148             the same terms as the Perl 5 programming language system itself.
149              
150             =cut