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.26';
3 54     54   136065 use v5.10;
  54         201  
4 54     54   289 use strict;
  54         86  
  54         1501  
5 54     54   321 use warnings;
  54         135  
  54         3563  
6 54     54   360 use Exporter qw(import);
  54         158  
  54         2576  
7 54     54   274 use Carp qw(croak);
  54         122  
  54         3620  
8 54     54   312 use Scalar::Util qw(blessed);
  54         113  
  54         73869  
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 103     103 0 167746 my ($sub) = @_;
37              
38 103         156 local $@;
39 103         158 my $ret = not eval {
40 103         217 $sub->();
41 93         13611 return 1;
42             };
43              
44 103 100 66     779 if ($@ && $ret) {
45 10         19 $ret = $@;
46             }
47              
48 103         367 return $ret;
49             }
50              
51             sub trim
52             {
53 23     23 0 51 my ($value) = @_;
54 23         195 $value =~ s/\A\s+//;
55 23         85 $value =~ s/\s+\z//;
56              
57 23         104 return $value;
58             }
59              
60             sub uniq
61             {
62 5     5 0 9 my %seen;
63 5         51 return grep { !$seen{$_}++ } @_;
  8         145  
64             }
65              
66             # FORM METADATA
67             my $meta_class = 'Form::Tiny::Meta';
68             my %meta;
69              
70             sub create_anon_form_meta
71             {
72 125     125 0 322 my (@roles) = @_;
73 125         31018 require Form::Tiny::Meta;
74 125         3146 my $meta = $meta_class->new;
75 125         5959 $meta->set_meta_roles([@roles]);
76              
77 125         3648 return $meta;
78             }
79              
80             sub create_form_meta
81             {
82 71     71 0 206 my ($package, @roles) = @_;
83              
84             croak "form meta for $package already exists"
85 71 50       290 if exists $meta{$package};
86              
87 71         250 $meta{$package} = create_anon_form_meta(@roles);
88 71         463 $meta{$package}->set_package($package);
89              
90 71         372 return $meta{$package};
91             }
92              
93             sub has_form_meta
94             {
95 209   66 209 0 2521 return exists $meta{ref $_[0] || $_[0]}
96             || blessed $_[0] && $_[0]->DOES('Form::Tiny::Form');
97             }
98              
99             sub get_package_form_meta
100             {
101 981   66 981 0 4069 my $package_name = ref $_[0] || $_[0];
102 981         2004 my $form_meta = $meta{$package_name};
103              
104 981 100 66     6612 if (!$form_meta || !$form_meta->complete) {
105             croak "no form meta declared for $package_name"
106 70 50       319 unless exists $meta{$package_name};
107              
108 70 100       271 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 68         360 $form_meta->bootstrap;
114             }
115              
116 979         8918 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 1497     1497   2392 my ($fields, $field_def) = @_;
137              
138 1497         1812 my @path = @{$field_def->get_name_path->path};
  1497         28042  
139 1497         33572 my $arrays = $field_def->get_name_path->meta_arrays;
140              
141             # the result goes here
142 1497         8901 my @found;
143             my $traverser;
144             $traverser = sub {
145 1661     1661   2794 my ($curr_path, $index, $value) = @_;
146              
147 1661         3384 while ($index < @path) {
148 1745         3167 my $current_ref = ref $value;
149              
150 1745 100       3141 if ($arrays->[$index]) {
151              
152             # It's an array, make sure the actual ref type does not mismatch the spec
153 120 100       272 return 0 unless $current_ref eq 'ARRAY';
154              
155 106 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       33 return 0 unless $index == $#path;
159              
160             # we had aref here, so we want it back in resulting hash
161 8         40 push @found, [$curr_path, [], 1];
162             }
163             else {
164 96         218 for my $ind (0 .. $#$value) {
165 164 100       601 return 0 # may be an error, exit early
166             unless $traverser->([@$curr_path, $ind], $index + 1, $value->[$ind]);
167             }
168             }
169              
170 82         214 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 1625         2309 my $next = $path[$index];
176 1625 100 100     6708 return 0 unless $current_ref eq 'HASH' && exists $value->{$next};
177              
178 254         370 $index += 1;
179 254         418 $value = $value->{$next};
180 254         695 push @$curr_path, $next;
181             }
182             }
183              
184 170         393 push @found, [$curr_path, $value];
185 170         459 return 1; # all ok
186 1497         5869 };
187              
188             # manually free traverser after it's done (memory leak)
189 1497         2835 my $result = $traverser->([], 0, $fields);
190 1497         8175 $traverser = undef;
191              
192 1497 100       2801 return \@found if $result;
193 1387         3505 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 300     300   681 my ($fields, $field_def, $path_values) = @_;
201              
202 300         6173 my $arrays = $field_def->get_name_path->meta_arrays;
203 300         2499 for my $path_value (@$path_values) {
204 358         479 my @parts = @{$path_value->[0]};
  358         897  
205 358         614 my $current = \$fields;
206              
207 358         928 for my $i (0 .. $#parts) {
208 687 100       1254 if ($arrays->[$i]) {
209 188         222 $current = \${$current}->[$parts[$i]];
  188         425  
210             }
211             else {
212 499         676 $current = \${$current}->{$parts[$i]};
  499         1539  
213             }
214             }
215              
216 358         1452 $$current = $path_value->[1];
217             }
218             }
219              
220             1;
221