File Coverage

blib/lib/Form/Processor.pm
Criterion Covered Total %
statement 135 245 55.1
branch 41 120 34.1
condition 10 33 30.3
subroutine 26 44 59.0
pod 24 34 70.5
total 236 476 49.5


line stmt bran cond sub pod time code
1             package Form::Processor;
2             $Form::Processor::VERSION = '1.162360';
3 5     5   61449 use strict;
  5         6  
  5         111  
4 5     5   17 use warnings;
  5         5  
  5         118  
5 5     5   17 use base qw/ Rose::Object Form::Processor::Model /;
  5         3  
  5         1915  
6 5     5   21 use Carp;
  5         5  
  5         218  
7 5     5   1898 use UNIVERSAL::require;
  5         4252  
  5         34  
8 5     5   1837 use Locale::Maketext;
  5         32217  
  5         45  
9 5     5   1473 use Form::Processor::I18N; # base class for language files
  5         7  
  5         23  
10 5     5   88 use Scalar::Util;
  5         5  
  5         468  
11              
12              
13              
14              
15             # Define basic instance interface
16              
17             use Rose::Object::MakeMethods::Generic (
18              
19 5         99 boolean => [ qw(
20             ran_validation
21             validated
22             verbose
23             readonly
24             use_existing_values
25             ) ],
26              
27              
28             scalar => [
29             item_id => {}, # Can't init from item->id because of circular references
30             errors => {}, # total errors
31             name => { interface => 'get_set_init' }, # form name (in case using multiple forms?)
32             updated_or_created => {}, # silly message.
33             name_prefix => {}, # prefix used on all name fields.
34             init_object => {}, # provides a way to init from another object.
35             user_data => {}, # Just a place to store user data.
36             language_handle => { interface => 'get_set_init' }, # Locale::Maketext language handle
37             field_counter => { interface => 'get_set_init' }, # For numbering fields.
38              
39             # A field can be a form, and this is a reference to that field
40             # Causes all the sub-form error messages to be sent to the parent field.
41             # (implemented below)
42             # parent_field => {},
43             ],
44              
45              
46             hash => [
47              
48             # Stores CGI parameters -- init will populate hash from object
49             param => { hash_key => 'params' },
50             params => { interface => 'get_set_init' },
51             reset_params => { interface => 'reset', hash_key => 'params' },
52             delete_param => { interface => 'delete', hash_key => 'params' },
53              
54             profile => {},
55              
56             ],
57              
58              
59             array => [
60             fields => {},
61             clear_fields => { interface => 'clear', hash_key => 'fields' },
62             add_field => { interface => 'push', hash_key => 'fields' },
63              
64             requires => {},
65             clear_requires => { interface => 'clear', hash_key => 'requires' },
66             add_requires => { interface => 'push', hash_key => 'requires' },
67              
68             ],
69 5     5   21 );
  5         4  
70              
71              
72             # ABSTRACT: validate and process form data
73              
74              
75             sub init_name {
76 0     0 0 0 my $form = shift;
77 0         0 return 'form' . int( rand 1000 );
78             }
79              
80              
81             # Used by set_order call. See Field.pm
82 0     0 0 0 sub init_field_counter {1}
83              
84              
85              
86              
87              
88             sub new {
89 5     5 1 1983 my $class = shift;
90              
91 5         10 my $self = bless {}, $class;
92              
93 5 50       24 return unless $self->init( @_ );
94              
95 5 50       21 $self->dump_fields if $self->verbose;
96              
97 5         34 return $self;
98             }
99              
100              
101             sub load_form {
102 0     0 1 0 my $class = shift;
103              
104 0         0 my $self = bless {}, $class;
105              
106 0         0 $self->SUPER::init( @_ ); # load passed in parameters
107              
108 0         0 $self->build_form; # create the form fields
109              
110 0         0 return;
111             }
112              
113              
114              
115              
116             sub clear {
117 3     3 1 615 my $self = shift;
118 3         6 $self->validated( 0 );
119 3         12 $self->ran_validation( 0 );
120 3         9 $self->errors( 0 );
121 3         7 $self->clear_values;
122 3         58 $self->updated_or_created( undef );
123             }
124              
125              
126              
127              
128             sub init {
129 5     5 1 7 my $self = shift;
130              
131              
132             # Deal with passing a single value to new()
133             # which would be the item id or item object
134              
135 5 50       20 if ( 1 == @_ ) {
136 0         0 my $id = shift;
137              
138 0 0       0 if ( ref $id ) { # passed an existing item $$$ FIXME use Scalar::Util::blessed
139 0         0 @_ = ( item => $id, item_id => $id->id );
140             } else {
141 0         0 @_ = ( item_id => $id );
142             }
143             }
144              
145              
146 5         39 $self->SUPER::init( @_ ); # load passed in parameters
147              
148 5         43 $self->build_form; # create the form fields
149              
150              
151             # if an item id passed in try and load
152             # and return false if the item is not loaded.
153             # This just helps in validating the id passed in the controller
154              
155 5 50 33     41 return if defined $self->item_id && !$self->item;
156              
157              
158              
159 5         21 $self->init_from_object; # load values from object, if item exists;
160              
161 5         19 $self->load_options; # load options -- need to do after loading item
162              
163 5         13 return 1;
164             }
165              
166              
167              
168             sub build_form {
169 5     5 1 8 my $self = shift;
170              
171 5         1422 my $profile = $self->profile;
172              
173 5 50       552 croak "Please define 'profile' method in subclass" unless ref $profile eq 'HASH';
174              
175              
176             ### $$$ look at all keys in profile and allow keys to be Field names.
177              
178              
179 5         14 for my $group ( qw/ required optional / ) {
180 10         14 my $required = 'required' eq $group;
181              
182 10         37 $self->_build_fields( $profile->{$group}, $required );
183              
184 10   50     88 my $auto_fields = $profile->{ 'auto_' . $group } || next;
185              
186 0         0 $self->_build_fields( $auto_fields, $required );
187             }
188             }
189              
190              
191             sub load_options {
192 5     5 1 39 my $self = shift;
193              
194 5         19 $self->load_field_options( $_ ) for $self->fields;
195             }
196              
197             # Why are the options not loaded via the field? Because the Model class
198             # overrides this class (Form::Processor), not a field class. Makes is a bit
199             # harder to override the individual field classes that have options. This
200             # probably needs addressing.
201              
202             sub load_field_options {
203 17     17 0 79 my ( $self, $field, @options ) = @_;
204              
205             # Populate the field?
206 17 100       82 return unless $field->can( 'options' );
207              
208              
209 6         11 my $method = 'options_' . $field->name;
210              
211 6 100       42 @options = $self->can( $method )
    50          
212             ? $self->$method( $field )
213             : $self->lookup_options( $field ) unless @options;
214              
215 6 100       18 return unless @options;
216              
217              
218 1 50       2 @options = @{ $options[0] } if ref $options[0];
  0         0  
219              
220 1 50       2 croak "Options array must contain an even number of elements for field " . $field->name
221             if @options % 2;
222              
223 1         1 my @opts;
224 1         8 push @opts, { value => shift @options, label => shift @options }
225             while @options;
226              
227 1 50       4 $field->options( \@opts ) if @opts;
228              
229             }
230              
231              
232             sub dump_fields {
233 0     0 1 0 my $self = shift;
234 0         0 for my $field ( $self->fields ) {
235 0         0 $field->dump;
236 0 0       0 $field->form->dump_fields if $field->can( 'form' );
237             }
238             }
239              
240              
241             # create fields.
242              
243             sub _build_fields {
244 10     10   19 my ( $self, $fields, $required ) = @_;
245              
246 10 100       25 return unless $fields;
247              
248 6 50       17 if ( ref( $fields ) eq 'ARRAY' ) {
249 0         0 for ( @$fields ) {
250 0   0     0 my $field = $self->make_field( $_, 'Auto' ) || next;
251 0         0 $field->required( $required );
252 0         0 $self->add_field( $field );
253             }
254              
255 0         0 return;
256             }
257              
258             # otherwise, defined field types
259              
260 6         28 while ( my ( $name, $type ) = each %$fields ) {
261 17   50     246 my $field = $self->make_field( $name, $type ) || next;
262              
263 17         51 $field->required( $required );
264 17         81 $self->add_field( $field );
265             }
266             }
267              
268              
269              
270             sub make_field {
271 17     17 1 103 my ( $self, $name, $type_data ) = @_;
272              
273 17 50 33     326 croak 'Must pass name and type to make_field'
274             unless $name && $type_data;
275              
276 17 50       168 $type_data = { type => $type_data } unless ref $type_data eq 'HASH';
277              
278             # Grab field type and load the class
279              
280 17   50     112 my $type = $type_data->{type} || die 'Failed to provide field type to make_field()';
281 17 50       63 $type = $self->guess_field_type( $name ) if $type eq 'Auto';
282              
283 17 50       26 croak "Failed to set field type for field [$name]" unless $type;
284              
285 17 50       53 my $class = $type =~ s/^\+//
286             ? $type
287             : 'Form::Processor::Field::' . $type;
288              
289 17 50       76 $class->require or die "Failed to load field '$type': $UNIVERSAL::require::ERROR";
290              
291              
292             # Create instance
293              
294 17 50       394 $type_data->{name} = $self->name_prefix
295             ? $self->name_prefix . '.' . $name
296             : $name;
297              
298 17         21 $type_data->{form} = $self;
299              
300              
301 17         18 my $field = $class->new( %{$type_data} );
  17         102  
302              
303             # Define default field order
304 17 50       77 unless ( $field->order ) {
305 0         0 my $fields = $self->fields;
306 0 0       0 $field->order( $fields ? scalar @{ $self->fields } + 1 : 1 );
  0         0  
307             }
308              
309              
310 17         56 return $field;
311              
312             }
313              
314              
315              
316              
317              
318              
319             sub init_from_object {
320 5     5 1 7 my $self = shift;
321              
322              
323 5   50     54 my $item = $self->init_object || $self->item || return;
324              
325 0         0 for my $field ( $self->fields ) {
326              
327 0         0 my @values;
328              
329 0         0 my $method = 'init_value_' . $field->name;
330 0 0       0 if ( $self->can( $method ) ) {
331 0         0 @values = $self->$method( $field, $item );
332              
333             } else {
334 0         0 @values = $self->init_value( $field, $item );
335             }
336              
337 0 0       0 my $value = @values > 1 ? \@values : shift @values;
338              
339             # Handy for later compare
340 0         0 $field->init_value( $value );
341 0         0 $field->value( $value );
342             }
343              
344             }
345              
346              
347              
348              
349              
350              
351             sub init_params {
352 0     0 1 0 my $form = shift;
353 0         0 my %hash;
354 0         0 for my $field ( $form->fields ) {
355              
356 0 0       0 next if $field->writeonly;
357              
358 0         0 my %params = $field->format_value;
359              
360 0         0 while ( my ( $k, $v ) = each( %params ) ) {
361 0 0       0 $hash{$k} = $v if defined $v;
362             }
363             }
364              
365 0         0 return \%hash;
366             }
367              
368              
369             sub clear_values {
370 3     3 0 2 my $form = shift;
371              
372 3         6 for ( $form->fields ) {
373 9         27 $_->value( undef );
374 9         10 $_->input( undef );
375             }
376 3         16 $form->reset_params;
377             }
378              
379              
380              
381             sub fif {
382 0     0 1 0 my $self = shift;
383              
384 0         0 my %hash = $self->params;
385              
386             # remove password fields
387 0         0 for my $field ( $self->fields ) {
388 0 0       0 delete $hash{ $field->name } if $field->password;
389             }
390 0         0 return \%hash;
391             }
392              
393              
394             sub sorted_fields {
395 0     0 1 0 my $form = shift;
396              
397 0         0 my @fields = sort { $a->order <=> $b->order } $form->fields;
  0         0  
398              
399 0 0       0 return wantarray ? @fields : \@fields;
400             }
401              
402              
403              
404              
405             sub field {
406 3     3 1 283 my ( $self, $name, $no_die ) = @_;
407              
408 3 50       10 $name = $self->name_prefix . '.' . $name if $self->name_prefix;
409              
410 3         6 for my $field ( $self->fields ) {
411 6 100       33 return $field if $field->name eq $name;
412             }
413              
414 0 0       0 return if $no_die;
415              
416 0         0 croak "Failed to lookup field name [$name] in form [$self]";
417             }
418              
419              
420             sub exists {
421 0     0 1 0 my ( $self, $name ) = @_;
422 0         0 return $self->field( $name, 1 );
423             }
424              
425              
426              
427             sub init_language_handle {
428 3     3 0 21 my $self = shift;
429              
430 3   50     48 my $lh = $Form::Processor::LANGUAGE_HANDLE || $ENV{LANGUAGE_HANDLE} || Form::Processor::I18N->get_handle ||
431             die "Failed call to Text::Maketext->get_handle";
432              
433 3         89 return $lh;
434              
435             }
436              
437              
438              
439              
440              
441              
442             sub validate {
443 3     3 1 14 my ( $self, $params ) = @_;
444              
445 3   100     9 $params ||= {};
446              
447              
448 3 50       5 return $self->validated if $self->ran_validation;
449              
450             # Set params -- so can be used by fif later.
451 3         15 $self->params( $params );
452              
453 3 50       25 $self->set_existing_values if $self->use_existing_values;
454              
455 3         12 $self->set_dependency; # set required dependencies
456              
457              
458             # First pass: trim values and move to "input" slot
459              
460             $_->input( $_->trim_value( $params->{ $_->full_name } ) )
461 3         27 for $self->fields;
462              
463              
464              
465             # Second pass: Validate each field and "inflate" input -> value.
466              
467 3         7 for my $field ( $self->fields ) {
468 9 50       40 next if $field->clear; # Skip validation
469 9         67 $field->validate_field;
470             }
471              
472              
473             # Third pass: call local validation for all *defined* values.
474              
475 3         9 for my $field ( $self->fields ) {
476 9 50       27 next if $field->clear; # Skip validation
477 9 100       39 next unless defined $field->value;
478              
479             # these methods have access to the inflated values
480 4         6 my $method = 'validate_' . $field->name;
481 4 50       17 $self->$method( $field ) if $self->can( $method );
482             }
483              
484              
485             # only call if no errors? Only call on validated fields?
486 3         8 $self->cross_validate( $params );
487              
488              
489             # model specific validation (e.g. validation that requires database lookups)
490 3         10 $self->model_validate;
491              
492              
493 3         6 $self->clear_dependency;
494              
495             # should this be an init_errors method?
496 3         7 my $errors;
497 3         5 for ( $self->fields ) {
498 9 100       45 $errors++ if $_->errors;
499             }
500              
501 3         15 $self->errors( $errors );
502 3         4 $self->ran_validation( 1 );
503 3         14 $self->validated( !$errors );
504              
505 3 50       10 $self->dump_validated if $self->verbose;
506              
507 3         15 return $self->validated;
508              
509              
510             }
511              
512             sub dump_validated {
513 0     0 0 0 my $self = shift;
514 0         0 warn "-- validated --\n";
515             warn $_->name, ": ", ( $_->errors ? join( ' | ', $_->errors ) : 'validated!' ), "\n"
516 0 0       0 for $self->fields;
517             }
518              
519              
520              
521 3     3 1 3 sub cross_validate {1}
522              
523              
524             # here we get a bit more iffy.
525             # Remember, this is before white space is trimmed.
526             # and before any validation.
527              
528             sub set_dependency {
529 3     3 0 4 my $self = shift;
530              
531 3   50     4 my $depends = $self->profile->{dependency} || return;
532              
533 0         0 my $params = $self->params;
534              
535 0         0 for my $group ( @$depends ) {
536 0 0       0 next if @$group < 2;
537              
538             # process a group of fields
539              
540 0         0 for my $name ( @$group ) {
541              
542              
543             # is there a value?
544 0         0 my $value = $params->{$name};
545              
546 0 0       0 next unless defined $value;
547              
548              
549             # The exception is a boolean can be zero which we count as not set.
550             # This is to allow requiring a field when a boolean is true.
551 0 0 0     0 next if $self->field( $name )->type eq 'Boolean' && $value == 0;
552              
553              
554 0 0       0 if ( ref $value ) {
555 0 0       0 next unless grep {/\S/} @$value; # at least one value is non-blank
  0         0  
556             } else {
557 0 0       0 next unless $value =~ /\S/;
558             }
559              
560              
561             # one field was found non-blank, so set all to required
562 0         0 for ( @$group ) {
563 0         0 my $field = $self->field( $_ );
564 0 0 0     0 next unless $field && !$field->required;
565 0         0 $self->add_requires( $field ); # save for clearing later.
566 0         0 $field->required( 1 );
567             }
568 0         0 last;
569             }
570             }
571             }
572              
573             sub clear_dependency {
574 3     3 0 3 my $self = shift;
575              
576 3         6 $_->required( 0 ) for $self->requires;
577 3         19 $self->clear_requires;
578             }
579              
580              
581              
582              
583              
584             sub has_error {
585 0     0 1 0 my $self = shift;
586 0   0     0 return $self->ran_validation && !$self->validated;
587             }
588              
589             sub has_errors {
590 0     0 0 0 for ( shift->fields ) {
591 0 0       0 return 1 if $_->errors;
592             }
593 0         0 return 0;
594             }
595              
596              
597 0     0 1 0 sub error_fields { return grep { $_->errors } shift->sorted_fields }
  0         0  
598              
599              
600 0     0 0 0 sub error_field_names { return map { $_->name } shift->error_fields }
  0         0  
601              
602              
603              
604             sub required_text {
605 0     0 1 0 my ( $self, $name ) = @_;
606 0 0       0 return 'unknown' unless $name;
607 0 0       0 return 'unknown' unless my $field = $self->field( $name );
608 0         0 return $field->required_text;
609             }
610              
611              
612             sub value {
613 0     0 1 0 my ( $form, $name, $no_die ) = @_;
614 0   0     0 my $field = $form->field( $name, $no_die ) || return;
615 0         0 return $field->value;
616             }
617              
618              
619              
620             sub value_changed {
621 0     0 1 0 my ( $self, $name ) = @_;
622 0 0       0 croak "value_chagned requires a field name" unless $name;
623              
624 0 0       0 my $field = ref( $name ) ? $name : $self->field( $name );
625 0 0       0 croak "Failed to lookup field name [$name]\n" unless $field;
626              
627 0         0 return $field->value_changed;
628             }
629              
630              
631              
632             sub set_existing_values {
633 0     0 1 0 my $form = shift;
634              
635 0         0 my $params = $form->params;
636              
637 0         0 for my $field ( $form->fields ) {
638              
639             # Does the field insist that the value must be provided?
640 0 0       0 next if $field->must_submit;
641              
642 0         0 my $name = $field->name;
643 0 0       0 next if exists $params->{$name};
644              
645 0 0       0 next if $field->writeonly;
646              
647 0         0 my %hash_params = $field->format_value;
648              
649 0         0 $form->params( %hash_params );
650             }
651             }
652              
653              
654              
655              
656              
657             sub uuid {
658 0     0 1 0 my $form = shift;
659 0         0 require Data::UUID;
660 0         0 my $uuid = Data::UUID->new->create_str;
661 0         0 return qq[];
662             }
663              
664              
665              
666             sub parent_field {
667 23     23 1 26 my $self = shift;
668 23 100       40 return Scalar::Util::weaken( $self->{parent_field} = shift ) if ( @_ );
669 22         62 return $self->{parent_field};
670             }
671              
672              
673              
674              
675              
676             1;
677              
678             __END__