File Coverage

blib/lib/Form/Tiny/Utils.pm
Criterion Covered Total %
statement 94 98 95.9
branch 28 32 87.5
condition 11 15 73.3
subroutine 16 17 94.1
pod 0 8 0.0
total 149 170 87.6


line stmt bran cond sub pod time code
1             package Form::Tiny::Utils;
2             $Form::Tiny::Utils::VERSION = '2.19';
3 52     52   1235 use v5.10;
  52         216  
4 52     52   283 use strict;
  52         137  
  52         1153  
5 52     52   278 use warnings;
  52         110  
  52         1629  
6 52     52   349 use Exporter qw(import);
  52         151  
  52         1953  
7 52     52   326 use Carp qw(croak);
  52         219  
  52         3099  
8 52     52   404 use Scalar::Util qw(blessed);
  52         155  
  52         67171  
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 9213 my ($sub) = @_;
37              
38 102         155 local $@;
39 102         182 my $ret = not eval {
40 102         262 $sub->();
41 93         12127 return 1;
42             };
43              
44 102 100 66     455 if ($@ && $ret) {
45 9         16 $ret = $@;
46             }
47              
48 102         346 return $ret;
49             }
50              
51             sub trim
52             {
53 23     23 0 58 my ($value) = @_;
54 23         89 $value =~ s/\A\s+//;
55 23         73 $value =~ s/\s+\z//;
56              
57 23         87 return $value;
58             }
59              
60             sub uniq
61             {
62 5     5 0 10 my %seen;
63 5         37 return grep { !$seen{$_}++ } @_;
  8         126  
64             }
65              
66             # FORM METADATA
67             my $meta_class = 'Form::Tiny::Meta';
68             my %meta;
69              
70             sub create_anon_form_meta
71             {
72 120     120 0 280 my (@roles) = @_;
73 120         23904 require Form::Tiny::Meta;
74 120         2404 my $meta = $meta_class->new;
75 120         4832 $meta->set_meta_roles([@roles]);
76              
77 120         3202 return $meta;
78             }
79              
80             sub create_form_meta
81             {
82 66     66 0 225 my ($package, @roles) = @_;
83              
84             croak "form meta for $package already exists"
85 66 50       264 if exists $meta{$package};
86              
87 66         240 $meta{$package} = create_anon_form_meta(@roles);
88 66         337 $meta{$package}->set_package($package);
89              
90 66         494 return $meta{$package};
91             }
92              
93             sub has_form_meta
94             {
95 191   66 191 0 2364 return exists $meta{ref $_[0] || $_[0]}
96             || blessed $_[0] && $_[0]->DOES('Form::Tiny::Form');
97             }
98              
99             sub get_package_form_meta
100             {
101 927   66 927 0 3251 my $package_name = ref $_[0] || $_[0];
102 927         1734 my $form_meta = $meta{$package_name};
103              
104 927 100 66     5276 if (!$form_meta || !$form_meta->complete) {
105             croak "no form meta declared for $package_name"
106 65 50       238 unless exists $meta{$package_name};
107              
108 65 100       245 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 63         277 $form_meta->bootstrap;
114             }
115              
116 925         3858 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             sub _find_field
133             {
134 1379     1379   2191 my ($fields, $field_def) = @_;
135              
136 1379         1708 my @path = @{$field_def->get_name_path->path};
  1379         25836  
137 1379         12101 my @arrays = map { $_ eq 'ARRAY' } @{$field_def->get_name_path->meta};
  2462         13727  
  1379         21133  
138              
139             # the result goes here
140 1379         2404 my @found;
141             my $traverser;
142             $traverser = sub {
143 1771     1771   3070 my ($curr_path, $index, $value) = @_;
144              
145 1771 100       3267 if ($index == @path) {
146              
147             # we reached the end of the tree
148 162         340 push @found, [$curr_path, $value];
149             }
150             else {
151 1609         2966 my $is_array = $arrays[$index];
152 1609         2951 my $current_ref = ref $value;
153              
154             # make sure the actual ref type does not mismatch the spec
155 1609 100       3353 return unless $is_array eq ($current_ref eq 'ARRAY');
156              
157             # it's not the leaf of the tree yet, so we require an array or a hash
158 1591 100 100     5014 return if !$is_array && $current_ref ne 'HASH';
159              
160 1580 100       2371 if ($is_array) {
161 98 100       202 if (@$value == 0) {
162              
163             # we wanted to have a deeper structure, but its not there, so clearly an error
164 10 100       32 return unless $index == $#path;
165              
166             # we had aref here, so we want it back in resulting hash
167 8         23 push @found, [$curr_path, [], 1];
168             }
169             else {
170 88         242 for my $ind (0 .. $#$value) {
171             return # may be an error, exit early
172 155 100       699 unless $traverser->([@$curr_path, $ind], $index + 1, $value->[$ind]);
173             }
174             }
175             }
176              
177             else {
178 1482         2388 my $next = $path[$index];
179 1482 100       4538 return unless exists $value->{$next};
180 237         1762 return $traverser->([@$curr_path, $next], $index + 1, $value->{$next});
181             }
182             }
183              
184 240         813 return 1; # all ok
185 1379         8239 };
186              
187 1379 100       3394 if ($traverser->([], 0, $fields)) {
188             return [
189             map {
190 103         215 {
191 160         815 path => $_->[0],
192             value => $_->[1],
193             structure => $_->[2]
194             }
195             } @found
196             ];
197             }
198 1276         3499 return;
199             }
200              
201             sub _assign_field
202             {
203 289     289   674 my ($fields, $field_def, $path_values) = @_;
204              
205 289         391 my @arrays = map { $_ eq 'ARRAY' } @{$field_def->get_name_path->meta};
  499         3376  
  289         5009  
206 289         676 for my $path_value (@$path_values) {
207 346         549 my @parts = @{$path_value->{path}};
  346         769  
208 346         594 my $current = \$fields;
209 346         846 for my $i (0 .. $#parts) {
210              
211             # array_path will contain array indexes for each array marker
212 670 100       1187 if ($arrays[$i]) {
213 183         246 $current = \${$current}->[$parts[$i]];
  183         383  
214             }
215             else {
216 487         617 $current = \${$current}->{$parts[$i]};
  487         1381  
217             }
218             }
219              
220 346         1296 $$current = $path_value->{value};
221             }
222             }
223              
224             1;
225