File Coverage

blib/lib/Form/Tiny/Utils.pm
Criterion Covered Total %
statement 91 95 95.7
branch 24 28 85.7
condition 11 15 73.3
subroutine 16 17 94.1
pod 0 8 0.0
total 142 163 87.1


line stmt bran cond sub pod time code
1             package Form::Tiny::Utils;
2             $Form::Tiny::Utils::VERSION = '2.21';
3 53     53   1195 use v5.10;
  53         188  
4 53     53   294 use strict;
  53         122  
  53         1125  
5 53     53   264 use warnings;
  53         129  
  53         1607  
6 53     53   312 use Exporter qw(import);
  53         149  
  53         1917  
7 53     53   315 use Carp qw(croak);
  53         131  
  53         3076  
8 53     53   367 use Scalar::Util qw(blessed);
  53         109  
  53         62225  
9              
10             our @EXPORT;
11             our @EXPORT_OK = qw(
12             try
13             trim
14             uniq
15             create_anon_form_meta
16             create_form_meta
17             get_package_form_meta
18             set_form_meta_class
19             has_form_meta
20             );
21              
22             our %EXPORT_TAGS = (
23             meta_handlers => [
24             qw(
25             create_anon_form_meta
26             create_form_meta
27             get_package_form_meta
28             set_form_meta_class
29             has_form_meta
30             )
31             ],
32             );
33              
34             sub try
35             {
36 102     102 0 9186 my ($sub) = @_;
37              
38 102         150 local $@;
39 102         175 my $ret = not eval {
40 102         236 $sub->();
41 93         12785 return 1;
42             };
43              
44 102 100 66     479 if ($@ && $ret) {
45 9         16 $ret = $@;
46             }
47              
48 102         376 return $ret;
49             }
50              
51             sub trim
52             {
53 23     23 0 42 my ($value) = @_;
54 23         85 $value =~ s/\A\s+//;
55 23         71 $value =~ s/\s+\z//;
56              
57 23         80 return $value;
58             }
59              
60             sub uniq
61             {
62 5     5 0 10 my %seen;
63 5         36 return grep { !$seen{$_}++ } @_;
  8         135  
64             }
65              
66             # FORM METADATA
67             my $meta_class = 'Form::Tiny::Meta';
68             my %meta;
69              
70             sub create_anon_form_meta
71             {
72 122     122 0 275 my (@roles) = @_;
73 122         24629 require Form::Tiny::Meta;
74 122         1976 my $meta = $meta_class->new;
75 122         5192 $meta->set_meta_roles([@roles]);
76              
77 122         3108 return $meta;
78             }
79              
80             sub create_form_meta
81             {
82 68     68 0 220 my ($package, @roles) = @_;
83              
84             croak "form meta for $package already exists"
85 68 50       249 if exists $meta{$package};
86              
87 68         245 $meta{$package} = create_anon_form_meta(@roles);
88 68         323 $meta{$package}->set_package($package);
89              
90 68         346 return $meta{$package};
91             }
92              
93             sub has_form_meta
94             {
95 196   66 196 0 2250 return exists $meta{ref $_[0] || $_[0]}
96             || blessed $_[0] && $_[0]->DOES('Form::Tiny::Form');
97             }
98              
99             sub get_package_form_meta
100             {
101 942   66 942 0 3296 my $package_name = ref $_[0] || $_[0];
102 942         1744 my $form_meta = $meta{$package_name};
103              
104 942 100 66     5095 if (!$form_meta || !$form_meta->complete) {
105             croak "no form meta declared for $package_name"
106 67 50       275 unless exists $meta{$package_name};
107              
108 67 100       251 croak "Form $package_name seems to be empty. "
109             . 'Please implement the form or call __PACKAGE__->form_meta explicitly. '
110             . 'See Form::Tiny::Manual::Cookbook "Empty forms" section for details'
111             if ref $_[0];
112              
113 65         267 $form_meta->bootstrap;
114             }
115              
116 940         3781 return $form_meta;
117             }
118              
119             sub set_form_meta_class
120             {
121 0     0 0 0 my ($class) = @_;
122              
123 0 0       0 croak 'form meta class must extend Form::Tiny::Meta'
124             unless $class->DOES('Form::Tiny::Meta');
125              
126 0         0 $meta_class = $class;
127 0         0 return;
128             }
129              
130             # internal use functions (not exported)
131              
132             # returns arrayref of subarraysrefs, each in format:
133             # [path_aref, $value, $is_structure]
134             sub _find_field
135             {
136 1387     1387   2237 my ($fields, $field_def) = @_;
137              
138 1387         1743 my @path = @{$field_def->get_name_path->path};
  1387         23076  
139 1387         30185 my $arrays = $field_def->get_name_path->meta_arrays;
140              
141             # the result goes here
142 1387         9470 my @found;
143             my $traverser;
144             $traverser = sub {
145 1547     1547   2590 my ($curr_path, $index, $value) = @_;
146              
147 1547         3105 while ($index < @path) {
148 1621         3074 my $current_ref = ref $value;
149              
150 1621 100       3010 if ($arrays->[$index]) {
151              
152             # It's an array, make sure the actual ref type does not mismatch the spec
153 116 100       271 return 0 unless $current_ref eq 'ARRAY';
154              
155 102 100       197 if (@$value == 0) {
156              
157             # we wanted to have a deeper structure, but its not there, so clearly an error
158 10 100       29 return 0 unless $index == $#path;
159              
160             # we had aref here, so we want it back in resulting hash
161 8         20 push @found, [$curr_path, [], 1];
162             }
163             else {
164 92         209 for my $ind (0 .. $#$value) {
165 160 100       577 return 0 # may be an error, exit early
166             unless $traverser->([@$curr_path, $ind], $index + 1, $value->[$ind]);
167             }
168             }
169              
170 82         210 return 1; # exit early, looping continued in recursive calls
171             }
172              
173             else {
174             # it's not the leaf of the tree yet, so we require a hash
175 1505         2215 my $next = $path[$index];
176 1505 100 100     6093 return 0 unless $current_ref eq 'HASH' && exists $value->{$next};
177              
178 244         915 $index += 1;
179 244         371 $value = $value->{$next};
180 244         670 push @$curr_path, $next;
181             }
182             }
183              
184 170         407 push @found, [$curr_path, $value];
185 170         413 return 1; # all ok
186 1387         5048 };
187              
188             # manually free traverser after it's done (memory leak)
189 1387         2994 my $result = $traverser->([], 0, $fields);
190 1387         6139 $traverser = undef;
191              
192 1387 100       2769 return \@found if $result;
193 1277         2951 return;
194             }
195              
196             # takes the same format as _find_field returns (in $path_values), and fills it
197             # into $fields according to $field_def
198             sub _assign_field
199             {
200 296     296   684 my ($fields, $field_def, $path_values) = @_;
201              
202 296         5086 my $arrays = $field_def->get_name_path->meta_arrays;
203 296         2570 for my $path_value (@$path_values) {
204 354         495 my @parts = @{$path_value->[0]};
  354         843  
205 354         595 my $current = \$fields;
206              
207 354         834 for my $i (0 .. $#parts) {
208 683 100       1188 if ($arrays->[$i]) {
209 188         235 $current = \${$current}->[$parts[$i]];
  188         393  
210             }
211             else {
212 495         622 $current = \${$current}->{$parts[$i]};
  495         1349  
213             }
214             }
215              
216 354         1187 $$current = $path_value->[1];
217             }
218             }
219              
220             1;
221