File Coverage

blib/lib/Data/NestedParams.pm
Criterion Covered Total %
statement 68 72 94.4
branch 27 30 90.0
condition 3 6 50.0
subroutine 9 9 100.0
pod 0 4 0.0
total 107 121 88.4


line stmt bran cond sub pod time code
1             package Data::NestedParams;
2 4     4   110668 use 5.008005;
  4         14  
  4         240  
3 4     4   23 use strict;
  4         6  
  4         133  
4 4     4   30 use warnings FATAL => 'all';
  4         7  
  4         330  
5              
6             our $VERSION = "0.07";
7              
8 4     4   6176 use parent qw(Exporter);
  4         749  
  4         22  
9              
10             our @EXPORT = qw(expand_nested_params collapse_nested_params);
11              
12             # https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L90
13             # 9a74ba3b04f2dabe4741d2d82eae723d440c3aa2
14              
15             sub parse {
16 74     74 0 94 my $k = shift;
17              
18 74         80 my @keys;
19 74         71 while (1) {
20 165 100       664 if ($k =~ s/\[([a-zA-Z0-9_-]+)\]\z//) {
    100          
21 49         164 unshift @keys, ['%', $1];
22             } elsif ($k =~ s/\[\]\z//) {
23 42         110 unshift @keys, ['@'];
24             } else {
25 74         465 unshift @keys, ['$', $k];
26 74         111 last;
27             }
28             }
29 74         193 return @keys;
30             }
31              
32             sub set_value {
33 74     74 0 111 my ($ret, $k, $v) = @_;
34              
35 74         132 my @keys = parse($k);
36              
37 74         106 my $r = \$ret;
38 74         156 while (@keys) {
39 165         209 my $key = shift @keys;
40 165 100       427 if ($key->[0] eq '%') {
    100          
    50          
41 49 100       79 if (@keys) {
42 28         32 $r = \(${$r}->{$key->[1]});
  28         124  
43             } else { # last
44 21         24 ${$r}->{$key->[1]} = $v;
  21         155  
45             }
46             } elsif ($key->[0] eq '@') {
47 42 100       72 if (@keys) {
48 13 100       27 if (defined($$r)) {
49 6 100 33     67 if (ref($$r->[@$$r-1]) eq 'HASH' && $keys[0]->[0] eq '%' && not exists $$r->[@$$r-1]->{$keys[0]->[1]}) {
      66        
50 4         6 $r = \(${$r}->[@$$r-1]);
  4         151  
51             } else {
52 2         3 $r = \(${$r}->[0+@$$r]);
  2         9  
53             }
54             } else {
55 7         9 $r = \(${$r}->[0]);
  7         35  
56             }
57             } else { # last
58 29         32 push @{$$r}, $v;
  29         198  
59             }
60             } elsif ($key->[0] eq '$') {
61 74 100       119 if (@keys) {
62 50         48 $r = \(${$r}->{$key->[1]});
  50         219  
63             } else {
64 24         24 ${$r}->{$key->[1]} = $v;
  24         179  
65             }
66             } else {
67 0         0 die "ABORT: $key->[0]";
68             }
69             }
70             }
71              
72             sub expand_nested_params {
73 42     42 0 81887 my $ary = shift;
74 42         82 my $ret = +{};
75 42         175 while (my ($k, $v) = splice @$ary, 0, 2) {
76 74         132 set_value($ret, $k, $v);
77             }
78 42         92 return $ret;
79             }
80              
81             our $COLLAPSE_KEY;
82              
83             sub _collapse {
84 21     21   27 my ($v, $r) = @_;
85              
86 21 100       51 if (ref $v eq 'HASH') {
    100          
    50          
87 9         30 while (my ($k, $v) = each %$v) {
88 11 100       30 local $COLLAPSE_KEY = length($COLLAPSE_KEY) ? sprintf('%s[%s]', $COLLAPSE_KEY, $k) : $k;
89 11         21 _collapse($v, $r);
90             }
91             } elsif (ref $v eq 'ARRAY') {
92 2         3 for (@$v) {
93 5         9 local $COLLAPSE_KEY = $COLLAPSE_KEY . '[]';
94 5         13 _collapse($_, $r);
95             }
96             } elsif (!ref $v) {
97 10         48 $r->{$COLLAPSE_KEY} = $v;
98             } else {
99 0         0 my $ref = ref $v;
100 0         0 Carp::confess("${ref} is not supported by collapse_nested_params");
101             }
102             }
103              
104             sub collapse_nested_params {
105 5     5 0 14 my $dat = shift;
106 5 50       18 if (ref $dat ne 'HASH') {
107 0         0 Carp::croak("The argument should be HashRef");
108             }
109              
110 5         8 local $COLLAPSE_KEY = '';
111 5         7 my $r = +{};
112 5         11 _collapse($dat, $r);
113 5         37 return $r;
114             }
115              
116             1;
117             __END__