File Coverage

blib/lib/Gantry/Utils/FormMunger.pm
Criterion Covered Total %
statement 118 123 95.9
branch 14 20 70.0
condition 1 2 50.0
subroutine 16 16 100.0
pod 13 13 100.0
total 162 174 93.1


line stmt bran cond sub pod time code
1             package Gantry::Utils::FormMunger;
2 1     1   62779 use strict; use warnings;
  1     1   2  
  1         44  
  1         5  
  1         2  
  1         1884  
3              
4             sub new {
5 1     1 1 11 my $class = shift;
6 1   50     8 my $form = shift || { fields => [] };
7              
8 1         3 my $self = { form => $form };
9 1         2 bless $self, $class;
10              
11 1         5 $self->{ sync } = $self->_sync();
12              
13 1         3 return $self;
14             } # END of new
15              
16             sub _sync {
17 9     9   15 my $self = shift;
18              
19 9         12 my %sync;
20 9         13 my $count = 0;
21              
22 9         12 foreach my $field ( @{ $self->{ form }{ fields } } ) {
  9         29  
23 23         91 $sync{ $field->{ name } } = { field => $field, order => $count++ };
24             }
25              
26 9         34 return \%sync;
27             }
28              
29             sub clear_props {
30 1     1 1 6 my $self = shift;
31 1         1 my $field_name = shift;
32              
33 1         3 my $field = $self->{ sync }{ $field_name }{ field };
34              
35             DOOMED_PROP:
36 1         2 foreach my $doomed_prop ( @_ ) {
37 2 50       5 if ( $doomed_prop eq 'name' ) {
38 0         0 warn "cowardly refusing to delete field name\n";
39 0         0 next DOOMED_PROP;
40             }
41 2         6 delete $field->{ $doomed_prop };
42             }
43             } # END of clear_props
44              
45             sub clear_all_props {
46 1     1 1 640 my $self = shift;
47 1         2 my $field_name = shift;
48              
49 1         4 my $index = $self->{ sync }{ $field_name }{ order };
50 1         3 my $field = $self->{ form }{ fields }[ $index ];
51              
52 1         4 PROP:
53 1         2 foreach my $key ( keys %{ $field } ) {
54 2 100       8 next PROP if $key eq 'name';
55 1         4 delete $field->{ $key };
56             }
57              
58             } # END of clear_all_props
59              
60             sub set_props {
61 2     2 1 1910 my $self = shift;
62 2         4 my $name = shift;
63 2         3 my $new_props = shift;
64 2         4 my $replace = shift;
65              
66 2         3 my $resync = 0;
67              
68 2         5 my $field = $self->{ sync }{ $name }{ field };
69 2         4 my $old_name = $field->{ name };
70              
71 2 100       10 if ( $replace ) {
72 1         3 foreach my $key ( keys %{ $field } ) {
  1         4  
73 3         5 delete $field->{ $key };
74             }
75 1         2 $resync = 1;
76             }
77              
78 2         3 foreach my $new_prop ( keys %{ $new_props } ) {
  2         6  
79 4         10 $field->{ $new_prop } = $new_props->{ $new_prop };
80 4 100       11 $resync = 1 if $new_prop eq 'name';
81             }
82              
83 2 50       7 $field->{ name } = $old_name unless $field->{ name };
84              
85 2 50       9 $self->_sync if $resync;
86              
87             } # END of set_props
88              
89             sub set_props_for_fields {
90 1     1 1 1136 my $self = shift;
91 1         2 my $field_names = shift;
92 1         2 my $new_props = shift;
93              
94 1         2 foreach my $field_name ( @{ $field_names } ) {
  1         1  
95 2         4 my $field = $self->{ sync }{ $field_name }{ field };
96              
97 2         2 foreach my $new_prop ( keys %{ $new_props } ) {
  2         6  
98 4         10 $field->{ $new_prop } = $new_props->{ $new_prop };
99             }
100             }
101             } # END of set_props_for
102              
103             sub set_props_except_for {
104 1     1 1 1168 my $self = shift;
105 1         2 my $skip_names = shift;
106 1         1 my $new_props = shift;
107              
108 1         2 my %skip = map { $_ => 1 } @{ $skip_names };
  2         6  
  1         2  
109              
110             FIELD:
111 1         2 foreach my $field ( @{ $self->{ form }{ fields } } ) {
  1         2  
112 4 100       14 next FIELD if $skip{ $field->{ name } };
113              
114 2         3 foreach my $new_prop ( keys %{ $new_props } ) {
  2         5  
115 2         5 $field->{ $new_prop } = $new_props->{ $new_prop };
116             }
117             }
118             } # END of set_props_except_for
119              
120             sub set_props_all {
121 1     1 1 883 my $self = shift;
122 1         3 my $new_props = shift;
123              
124 1         2 foreach my $field ( @{ $self->{ form }{ fields } } ) {
  1         3  
125 3         4 foreach my $new_prop ( keys %{ $new_props } ) {
  3         8  
126 3         10 $field->{ $new_prop } = $new_props->{ $new_prop };
127             }
128             }
129             } # END of set_props_all
130              
131             sub get_field {
132 1     1 1 7811 my $self = shift;
133 1         3 my $requested_name = shift;
134              
135 1         5 return $self->{ sync }{ $requested_name }{ field };
136             } # END of get_field
137              
138             sub drop_field {
139 1     1 1 4378 my $self = shift;
140 1         4 my $doomed_name = shift;
141 1         4 my $splice_pos = $self->{ sync }{ $doomed_name }{ order };
142 1         3 my $doomed;
143              
144 1 50       5 if ( defined $splice_pos ) {
145 1         2 $doomed = splice @{ $self->{ form }{ fields } }, $splice_pos, 1;
  1         5  
146 1         5 $self->{ sync } = $self->_sync();
147             }
148             else {
149 0         0 die "Invalid form field specified.";
150             }
151              
152 1         6 return $doomed;
153             } # END of drop_field
154              
155             sub append_field {
156 2     2 1 513 my $self = shift;
157 2         3 my $field = shift;
158              
159 2         3 push @{ $self->{ form }{ fields } }, $field;
  2         6  
160              
161 2         5 $self->{ sync } = $self->_sync();
162             } # END of append_field
163              
164             sub unshift_field {
165 1     1 1 1375 my $self = shift;
166 1         3 my $field = shift;
167              
168 1         1 unshift @{ $self->{ form }{ fields } }, $field;
  1         4  
169              
170 1         2 $self->{ sync } = $self->_sync();
171             } # END of unshift_field
172              
173             sub add_field_after {
174 1     1 1 1336 my $self = shift;
175 1         3 my $target_name = shift;
176 1         3 my $field = shift;
177 1         4 my $splice_pos = $self->{ sync }{ $target_name }{ order };
178              
179 1 50       5 if ( defined $splice_pos ) {
180 1         2 $splice_pos += 1;
181 1         1 splice @{ $self->{ form }{ fields } }, $splice_pos, 0, $field;
  1         5  
182 1         3 $self->{ sync } = $self->_sync();
183             }
184             else {
185 0         0 die "Invalid form field specified.";
186             }
187             } # END of add_field_after
188              
189             sub add_field_before {
190 1     1 1 1369 my $self = shift;
191 1         4 my $target_name = shift;
192 1         3 my $field = shift;
193 1         6 my $splice_pos = $self->{ sync }{ $target_name }{ order };
194              
195 1 50       6 if ( defined $splice_pos ) {
196 1         3 splice @{ $self->{ form }{ fields } }, $splice_pos, 0, $field;
  1         6  
197 1         5 $self->{ sync } = $self->_sync();
198             }
199             else {
200 0           die "Invalid form field specified.";
201             }
202             } # END of add_field_before
203              
204             1;
205              
206             =head1 NAME
207              
208             Gantry::Utils::FormMunger - Munges form hashes like the ones bigtop makes.
209              
210             =head1 SYNOPSIS
211              
212             use Gantry::Utils::FormMunger;
213              
214             my $form = ...; # make a form hash
215              
216             my $munger = Gantry::Utils::FormMunger->new( $form );
217              
218             # change properties of existing fields:
219             $munger->clear_props( 'field_name', qw( name keys to delete) );
220              
221             $munger->clear_all_props( 'field_name' );
222             # removes all keys except name
223              
224             $munger->set_props(
225             'field_name',
226             { prop => 'value', ... },
227             $replace_props
228             ); # modifies only the keys you pass
229              
230             $munger->set_props_for_fields(
231             [ 'field1', 'field2', ... ],
232             { prop => 'value', ... },
233             ); # like set_props but for all listed fields
234              
235             $munger->set_props_except_for(
236             [ 'skip_this_one', 'and_this_one' ],
237             { prop => 'value', ... },
238             ); # like set_props_for, but negated listed fields are skipped
239              
240             $munger->set_props_all( { prop => 'value', ... } );
241              
242             # get the field so you can work it yourself:
243             my $field = $munger->get_field( 'name' );
244              
245             # modify the field list:
246             my $deceased = $munger->drop_field( 'name' ); # removes it from the form
247              
248             $munger->append_field( { name => 'name', ... } ); # add at end
249             $munger->unshift_field( { name => 'name', ... } ); # add at beginning
250              
251             $munger->add_field_after( 'target', { name => 'name', ... } );
252             $munger->add_field_before( 'target', { name => 'name', ... } );
253              
254             =head1 DESCRIPTION
255              
256             This module is designed to simplify work with Gantry form.tt form hash
257             data structures. If makes modifications to the fields array in that
258             hash. Usually, bigtop generates that hash. If you are in a standard
259             CRUD situation, the generated form is all you need. But, if you need
260             to share the form in different contexts, it may be necessary to modify
261             it to suit those contexts. That is what this module does.
262              
263             If you want, you could even use this module to build your entire form
264             hash, but that might be painful. Instead, you usually pass a form hash
265             to its constructor. Usually, you get that hash from a GEN module's form
266             method which was generated by bigtop.
267              
268             Once you have the object, you can call any of the methods below to
269             modify its fields array. Most of the methods return nothing useful.
270             The exceptions are noted below.
271              
272             All methods are instance methods unless marked.
273              
274             =head1 METHODs
275              
276             =over 4
277              
278             =item new (class method)
279              
280             Parameters: a form hash. If you don't already have one try:
281              
282             my $munger = Gantry::Utils::FormMunger->new( { fields => [] } );
283              
284             It is better to use one that already has fields.
285              
286             Returns: a munger object upon which you may call the rest of the methods.
287              
288             =item clear_props
289              
290             Selectively removes specified properties from one field. This is
291             done by using delete on the fields subhash.
292              
293             Parameters: name of field to work on, list of properties to remove from its
294             fields hash
295              
296             =item clear_all_props
297              
298             Given the name of a field, this method deletes all of its properties except
299             its name.
300              
301             Parameters: name of field
302              
303             =item set_props
304              
305             Given a field name, and a list of properties, sets those properties on that
306             field.
307              
308             Parameters:
309              
310             =over 4
311              
312             =item field_name
313              
314             name of field to work on
315              
316             =item props
317              
318             hash reference of properties to assign on the field
319              
320             =item replace
321              
322             Flag. If true, all keys are deleted prior to application of props.
323             Note that you must supply a name property, or the field will have no
324             name and everyone Will Be Upset.
325              
326             =back
327              
328             =item set_props_for_fields
329              
330             Like C, but works for several named fields at once. This
331             is more efficient than separate calls, since the fields array is
332             only traversed once.
333              
334             Do not change field names with this method. Use C for that.
335             Trying to use this method will leave all fields involved with the
336             same name, confusing everyone including this module.
337              
338             Parameters:
339              
340             =over 4
341              
342             =item fields
343              
344             Array reference, listing fields to work on.
345              
346             =item props
347              
348             Hash reference of properties to assign on each field.
349              
350             =back
351              
352             =item set_props_except_for
353              
354             Like C, but you list fields to skip, instead of fields to
355             work on. Every field not mentioned is affected. The parameters
356             are the same as for C.
357              
358             Note that it is extremely unwise to consider changing field names with this
359             method, since that would make the field names of all fields modified
360             the same.
361              
362             =item set_props_all
363              
364             Like C, but it works on all fields.
365              
366             Note that it is extremely unwise to consider changing field names with this
367             method, since that would make all field names the same.
368              
369             Parameters:
370              
371             =over 4
372              
373             =item props
374              
375             Hash reference of properties to assign on each field.
376              
377             =back
378              
379             =item get_field
380              
381             Returns the subhash for a given field.
382              
383             Parameters: name of field to return
384              
385             Returns: subhash for the named field (if there is one)
386              
387             =item drop_field
388              
389             Deletes a field from the fields array.
390              
391             Parameters: name of doomed field
392              
393             Returns: the hash reference for the dearly departed.
394              
395             =item append_field
396              
397             Adds a new field at the end of the fields array (so it will appear last
398             on the form).
399              
400             Parameters: a hash reference for a new field
401              
402             =item unshift_field
403              
404             Just like C, except the new field becomes the first field.
405              
406             =item add_field_after
407              
408             Adds a new field to the fields array immediately after a named field.
409             If the named field is not found, the new field goes at the end.
410              
411             Parameters:
412              
413             =over 4
414              
415             =item target
416              
417             Name of field immediately before new field.
418              
419             =item props
420              
421             Hash reference of props for new field.
422              
423             =back
424              
425             =item add_field_before
426              
427             Just like C, except that the new field goes immediately
428             before the named field. (If the name is not found, the new field still
429             goes at the end.)
430              
431             =back
432              
433             =head1 AUTHOR
434              
435             Phil Crow, Ecrow.phil@gmail.com
436              
437             =head1 COPYRIGHT AND LICENSE
438              
439             Copyright (C) 2007 Phil Crow
440              
441             This library is free software; you can redistribute it and/or modify
442             it under the same terms as Perl itself, either Perl version 5.8.6 or,
443             at your option, any later version of Perl 5 you may have available.
444              
445             =cut
446