File Coverage

blib/lib/Rose/HTML/Form/Field/Collection.pm
Criterion Covered Total %
statement 223 269 82.9
branch 83 132 62.8
condition 11 26 42.3
subroutine 41 47 87.2
pod 3 32 9.3
total 361 506 71.3


line stmt bran cond sub pod time code
1             package Rose::HTML::Form::Field::Collection;
2              
3 12     12   99 use strict;
  12         105  
  12         385  
4              
5 12     12   61 use Carp();
  12         27  
  12         228  
6 12     12   142 use Scalar::Util qw(refaddr);
  12         26  
  12         656  
7              
8 12     12   5429 use Rose::HTML::Form::Field::Hidden;
  12         45  
  12         66  
9              
10 12     12   89 use base 'Rose::HTML::Object';
  12         27  
  12         1357  
11              
12 12     12   100 use Rose::HTML::Form::Constants qw(FF_SEPARATOR);
  12         37  
  12         1539  
13              
14             # Variables for use in regexes
15             our $FF_SEPARATOR_RE = quotemeta FF_SEPARATOR;
16              
17             our $VERSION = '0.606';
18              
19             #
20             # Object data
21             #
22              
23             use Rose::Object::MakeMethods::Generic
24             (
25 12         179 boolean => 'coalesce_hidden_fields',
26              
27             'scalar --get_set_init' =>
28             [
29             'field_rank_counter',
30             ],
31              
32             array =>
33             [
34             'before_prepare_hooks' => {},
35             'add_before_prepare_hooks' => { interface => 'push', hash_key => 'before_prepare_hooks' },
36              
37             'after_prepare_hooks' => {},
38             'add_after_prepare_hooks' => { interface => 'push', hash_key => 'after_prepare_hooks' },
39             'clear_prepare_hooks' => { interface => 'clear', hash_key => 'after_prepare_hooks' },
40             ],
41 12     12   101 );
  12         35  
42              
43             *add_before_prepare_hook = \&add_before_prepare_hooks;
44             *add_after_prepare_hook = \&add_after_prepare_hooks;
45              
46             #
47             # Class methods
48             #
49              
50             sub prepare
51             {
52 56     56 0 115 my($self) = shift;
53              
54 56         199 foreach my $hook ($self->before_prepare_hooks)
55             {
56 0         0 $hook->($self, @_);
57             }
58              
59 56         512 my %args = @_;
60              
61 56 100       157 unless($args{'form_only'})
62             {
63 31         97 foreach my $field ($self->fields)
64             {
65 103         294 $field->prepare(@_);
66             }
67             }
68              
69 56         151 foreach my $form ($self->forms)
70             {
71 25         124 $form->prepare(form_only => 1, @_);
72             }
73              
74 56         285 foreach my $hook ($self->after_prepare_hooks)
75             {
76 0         0 $hook->($self, @_);
77             }
78             }
79              
80             sub add_prepare_hook
81             {
82 0     0 0 0 my($self) = shift;
83              
84 0 0       0 if(@_ == 1)
    0          
85             {
86 0         0 $self->add_before_prepare_hook(@_);
87             }
88             elsif(@_ == 2)
89             {
90 0         0 my $where = shift;
91              
92 0 0 0     0 unless($where eq 'before' || $where eq 'after')
93             {
94 0         0 Carp::croak "Illegal prepare hook position: $where";
95             }
96              
97 0         0 my $method = "add_${where}_prepare_hook";
98              
99 12     12   4236 no strict 'refs';
  12         30  
  12         2030  
100 0         0 $self->$method(@_);
101             }
102             else
103             {
104 0         0 Carp::croak "Incorrect number of arguments to add_prepare_hook()";
105             }
106             }
107              
108             sub prepare_hook
109             {
110 0     0 0 0 my($self) = shift;
111 0         0 $self->clear_prepare_hooks;
112 0         0 $self->add_prepare_hook(@_);
113             }
114              
115             BEGIN
116             {
117 12     12   80 *add_field_type_classes = \&Rose::HTML::Object::add_object_type_classes;
118 12         37 *field_type_classes = \&Rose::HTML::Object::object_type_classes;
119 12         34 *field_type_class = \&Rose::HTML::Object::object_type_class;
120 12         580 *delete_field_type_class = \&Rose::HTML::Object::delete_object_type_class;
121             }
122              
123             #
124             # Object methods
125             #
126              
127             sub html
128             {
129 1     1 1 3 my($self) = shift;
130              
131 12     12   90 no warnings 'uninitialized';
  12         32  
  12         2512  
132              
133 1 50 33     6 if($self->has_children || !$self->is_self_closing)
134             {
135             return '<' . $self->html_element . $self->html_attrs_string . '>' .
136             join('', map
137             {
138 1 100       5 $_->isa('Rose::HTML::Form::Field') ?
  4         32  
139             '<div class="field-with-label">' . $_->html_label .
140             '<div class="field">' . $_->html . '</div></div>' :
141             $_->html
142             }
143             $self->children) .
144             '</' . $self->html_element . '>';
145             }
146              
147 0         0 return '<' . $self->html_element . $self->html_attrs_string . '>';
148             }
149              
150             sub xhtml
151             {
152 1     1 1 3 my($self) = shift;
153              
154 12     12   91 no warnings 'uninitialized';
  12         27  
  12         16510  
155              
156 1 50 33     10 if($self->has_children || !$self->is_self_closing)
157             {
158             return '<' . $self->xhtml_element . $self->xhtml_attrs_string . '>' .
159             join('', map
160             {
161 1 100       6 $_->isa('Rose::HTML::Form::Field') ?
  4         26  
162             '<div class="field-with-label">' . $_->xhtml_label .
163             '<div class="field">' . $_->xhtml . '</div></div>' :
164             $_->xhtml
165             }
166             $self->children) .
167             '</' . $self->xhtml_element . '>';
168             }
169              
170 0         0 return '<' . $self->xhtml_element . $self->xhtml_attrs_string . ' />';
171             }
172              
173 150     150 0 1405 sub init_field_rank_counter { 1 }
174              
175             sub increment_field_rank_counter
176             {
177 471     471 0 1014 my($self) = shift;
178 471         1515 my $rank = $self->field_rank_counter;
179 471         2920 $self->field_rank_counter($rank + 1);
180 471         2370 return $rank;
181             }
182              
183             sub make_field
184             {
185 622     622 0 1376 my($self, $name, $value) = @_;
186              
187 622 100       2104 return $value if(UNIVERSAL::isa($value, 'Rose::HTML::Form::Field'));
188              
189 401         689 my($type, $args);
190              
191 401 100       1212 if(ref $value eq 'HASH')
    50          
192             {
193 329 50       1008 $type = delete $value->{'type'} or Carp::croak "Missing field type";
194 329         637 $args = $value;
195             }
196             elsif(!ref $value)
197             {
198 72         120 $type = $value;
199 72         176 $args = {};
200             }
201             else
202             {
203 0         0 Carp::croak "Not a Rose::HTML::Form::Field object or hash ref: $value";
204             }
205              
206 401   33     1144 my $class = ref $self || $self;
207              
208 401 50       1641 my $field_class = $class->field_type_class($type)
209             or Carp::croak "No field class found for field type '$type'";
210              
211 401 100       18077 unless($field_class->can('new'))
212             {
213 8         23 my $error;
214              
215             TRY:
216             {
217 8         30 local $@;
  8         22  
218 8         767 eval "require $field_class";
219 8         74 $error = $@;
220             }
221              
222 8 50       43 Carp::croak "Failed to load field class $field_class - $error" if($error);
223             }
224              
225             # Compound fields require a name
226 401 100       1868 if(UNIVERSAL::isa($field_class, 'Rose::HTML::Form::Field::Compound'))
227             {
228 46         157 $args->{'name'} = $name;
229             }
230              
231 401         2158 return $field_class->new(%$args);
232             }
233              
234             sub invalidate_field_caches
235             {
236 624     624 0 1036 my($self) = shift;
237              
238 624         1820 $self->{'field_cache'} = {};
239             }
240              
241             sub field
242             {
243 4001     4001 0 11171 my($self, $name, $field) = @_;
244              
245 4001 100       8680 if(@_ == 3)
246             {
247 254 50       930 unless(UNIVERSAL::isa($field, 'Rose::HTML::Form::Field'))
248             {
249 0         0 $field = $self->make_field($name, $field);
250             }
251              
252 254         743 $field->local_moniker($name);
253              
254 254 50       1753 if($self->isa('Rose::HTML::Form'))
255             {
256 0         0 $field->parent_form($self);
257             }
258             else
259             {
260 254         799 $field->parent_field($self);
261             }
262              
263 254         910 $self->_clear_field_generated_values;
264              
265 254 50       785 unless(defined $field->rank)
266             {
267 0         0 $field->rank($self->increment_field_rank_counter);
268             }
269              
270 254         995 return $self->{'fields'}{$name} = $self->{'field_cache'}{$name} = $field;
271             }
272              
273 3747 100       8389 if($self->{'fields'}{$name})
274             {
275 3714         11126 return $self->{'fields'}{$name};
276             }
277              
278 33         65 my $sep_pos;
279              
280             # Non-hierarchical name
281 33 50       109 if(($sep_pos = index($name, FF_SEPARATOR)) < 0)
282             {
283 0         0 return undef; # $self->local_field($name, @_);
284             }
285              
286             # Check if it's a local compound field
287 33         80 my $prefix = substr($name, 0, $sep_pos);
288 33         67 my $rest = substr($name, $sep_pos + 1);
289 33         83 $field = $self->field($prefix);
290              
291 33 50       136 if(UNIVERSAL::isa($field, 'Rose::HTML::Form::Field::Compound'))
292             {
293 33         77 $field = $field->field($rest);
294 33 50       240 return ($self->{'field_cache'}{$name} = $field) if($field);
295             }
296              
297 0         0 return undef;
298             }
299              
300             sub find_parent_field
301             {
302 0     0 0 0 my($self, $name) = @_;
303              
304             # Non-hierarchical name
305 0 0       0 if(index($name, FF_SEPARATOR) < 0)
306             {
307 0 0       0 return $self->local_form($name) ? ($self, $name) : undef;
308             }
309              
310 0         0 my $parent_form;
311              
312 0         0 while($name =~ s/^([^$FF_SEPARATOR_RE]+)$FF_SEPARATOR_RE//o)
313             {
314 0         0 my $parent_name = $1;
315 0 0       0 last if($parent_form = $self->local_form($parent_name));
316             }
317              
318 0 0       0 return unless(defined $parent_form);
319 0 0       0 return wantarray ? ($parent_form, $name) : $parent_form;
320             }
321              
322             sub add_fields
323             {
324 198     198 0 2360 my($self) = shift;
325              
326 198         402 my @added_fields;
327              
328 198 100 100     790 @_ = @{$_[0]} if(@_ == 1 && ref $_[0] eq 'ARRAY');
  4         13  
329              
330 198         536 while(@_)
331             {
332 476         926 my $arg = shift;
333              
334 476 100       2503 if(UNIVERSAL::isa($arg, 'Rose::HTML::Form::Field'))
335             {
336 5         11 my $field = $arg;
337              
338 5 50       37 if(refaddr($field) eq refaddr($self))
339             {
340 0         0 Carp::croak "Cannot nest a field within itself";
341             }
342              
343 5         18 $field->local_name($field->name);
344              
345 5 50 33     26 if($self->can('form') && $self->form($field->local_name))
346             {
347 0         0 Carp::croak "Cannot add field with the same name as an existing sub-form: ",
348             $field->local_name;
349             }
350              
351 5 100       31 unless(defined $field->rank)
352             {
353 4         17 $field->rank($self->increment_field_rank_counter);
354             }
355              
356 5         18 $self->field($field->local_name => $field);
357 5         22 push(@added_fields, $field);
358             }
359             else
360             {
361 471         846 my $field = shift;
362              
363 471 100 100     1545 if($self->can('form') && $self->form($arg))
364             {
365 1         223 Carp::croak "Cannot add field with the same name as an existing sub-form: $arg";
366             }
367              
368 470 100       6156 if(UNIVERSAL::isa($field, 'Rose::HTML::Form::Field'))
369             {
370 105 50       496 if(refaddr($field) eq refaddr($self))
371             {
372 0         0 Carp::croak "Cannot nest a field within itself";
373             }
374             }
375             else
376             {
377 365         1218 $field = $self->make_field($arg, $field);
378             }
379              
380 470         1991 $field->local_moniker($arg);
381              
382 470 100       3480 unless(defined $field->rank)
383             {
384 467         1468 $field->rank($self->increment_field_rank_counter);
385             }
386              
387 470         1669 $self->field($arg => $field);
388 470         1680 push(@added_fields, $field);
389             }
390             }
391              
392 197         859 $self->_clear_field_generated_values;
393 197         841 $self->resync_field_names;
394              
395 197 100       940 return unless(defined wantarray);
396 38 100       414 return wantarray ? @added_fields : $added_fields[0];
397             }
398              
399 23     23 0 303 sub add_field { shift->add_fields(@_) }
400              
401             sub compare_fields
402             {
403 3612     3612 0 7068 my($self, $one, $two) = @_;
404 12     12   126 no warnings 'uninitialized';
  12         29  
  12         13875  
405 3612         8747 $one->name cmp $two->name;
406             }
407              
408             sub resync_field_names
409             {
410 744     744 0 1342 my($self) = shift;
411              
412 744         1739 foreach my $field ($self->fields)
413             {
414 2110         6623 $field->resync_name;
415 2110 100       10858 $field->resync_field_names if($field->isa('Rose::HTML::Form::Field::Compound'));
416             #$field->name; # Pull the new name through to the name HTML attribute
417             }
418             }
419              
420             sub children
421             {
422 0 0   0 1 0 Carp::croak "Cannot set children() for a pseudo-group ($_[0])" if(@_ > 1);
423 0 0 0     0 return wantarray ? shift->fields() : (shift->fields() || []);
424             }
425              
426             sub field_value
427             {
428 8     8 0 57 my($self, $name) = (shift, shift);
429              
430 8 50       33 my $field = $self->field($name)
431             or Carp::croak "No field named '$name' in $self";
432              
433 8 100       44 return $field->input_value(@_) if(@_);
434 7         41 return $field->internal_value;
435             }
436              
437             *subfield_value = \&field_value;
438              
439             sub subfield_names
440             {
441 20     20 0 41 my($self) = shift;
442              
443 20         33 my @names;
444              
445 20         50 foreach my $field ($self->fields)
446             {
447 41 100       165 push(@names, $field->name, ($field->can('_subfield_names') ? $field->_subfield_names : ()));
448             }
449              
450 20 50       125 return wantarray ? @names : \@names;
451             }
452              
453             sub _subfield_names
454             {
455 38 50   38   89 map { $_->can('subfield_names') ? $_->subfield_names : $_->name } shift->fields;
  133         332  
456             }
457              
458             sub fields
459             {
460 2589     2589 0 4470 my($self) = shift;
461              
462 2589 100       6160 if(my $fields = $self->{'field_list'})
463             {
464 2421 100       7738 return wantarray ? @$fields : $fields;
465             }
466              
467 168         323 my $fields = $self->{'fields'};
468              
469 168         544 $self->{'field_list'} = [ grep { defined } map { $fields->{$_} } $self->field_monikers ];
  254         560  
  254         526  
470              
471 168 50       400 return wantarray ? @{$self->{'field_list'}} : $self->{'field_list'};
  168         514  
472             }
473              
474             sub fields_as_children
475             {
476 13     13 0 30 my($self) = shift;
477              
478 13 50       35 Carp::croak "Cannot directly set children() for a ", ref($self),
479             ". Use fields(), push_children(), pop_children(), etc." if(@_);
480              
481 13         25 my @children;
482              
483 13         52 foreach my $field ($self->fields)
484             {
485 12 100       40 if($field->is_flat_group)
486             {
487 1         6 push(@children, $field->items);
488             }
489             else
490             {
491 11         28 push(@children, $field);
492             }
493             }
494              
495 13 50       62 return wantarray ? @children : \@children;
496             }
497              
498             *immutable_children = \&fields_as_children;
499              
500             sub num_fields
501             {
502 5     5 0 14 my $fields = shift->fields;
503 5 50 33     39 return $fields && @$fields ? scalar @$fields : 0;
504             }
505              
506             sub field_monikers
507             {
508 216     216 0 397 my($self) = shift;
509              
510 216 100       573 if(my $names = $self->{'field_monikers'})
511             {
512 48 50       242 return wantarray ? @$names : $names;
513             }
514              
515 168         268 my @info;
516              
517 168         290 while(my($name, $field) = each %{$self->{'fields'}})
  422         1307  
518             {
519 254         705 push(@info, [ $name, $field ]);
520             }
521              
522             $self->{'field_monikers'} =
523 168         791 [ map { $_->[0] } sort { $self->compare_fields($a->[1], $b->[1]) } @info ];
  254         817  
  237         754  
524              
525 168 50       463 return wantarray ? @{$self->{'field_monikers'}} : $self->{'field_monikers'};
  168         678  
526             }
527              
528             sub delete_fields
529             {
530 0     0 0 0 my($self) = shift;
531 0         0 $self->_clear_field_generated_values;
532 0         0 $self->{'fields'} = {};
533 0         0 $self->field_rank_counter(undef);
534 0         0 return;
535             }
536              
537             sub delete_field
538             {
539 0     0 0 0 my($self, $name) = @_;
540              
541 0 0       0 $name = $name->name if(UNIVERSAL::isa($name, 'Rose::HTML::Form::Field'));
542              
543 0         0 $self->_clear_field_generated_values;
544              
545 0         0 delete $self->{'field_cache'}{$name};
546 0         0 delete $self->{'fields'}{$name};
547             }
548              
549             sub clear_fields
550             {
551 584     584 0 1007 my($self) = shift;
552              
553 584         1396 foreach my $field ($self->fields)
554             {
555 2123         9975 $field->clear();
556             }
557             }
558              
559             sub reset_fields
560             {
561 16     16 0 32 my($self) = shift;
562              
563 16         48 foreach my $field ($self->fields)
564             {
565 54         263 $field->reset();
566             }
567             }
568              
569             sub _clear_field_generated_values
570             {
571 624     624   1154 my($self) = shift;
572 624         1381 $self->{'field_list'} = undef;
573 624         1209 $self->{'field_monikers'} = undef;
574 624         1909 $self->invalidate_field_caches;
575              
576             # XXX: This is super-incestuous
577 624 100       1589 if(my $parent_form = $self->parent_form)
578             {
579 69         218 $parent_form->_clear_field_generated_values;
580             }
581             }
582              
583             sub hidden_field
584             {
585 12     12 0 27 my($self) = shift;
586              
587 12     12   125 no warnings 'uninitialized';
  12         28  
  12         5397  
588 12         38 my $name = $self->fq_name;
589              
590             return
591 12         72 ref($self)->object_type_class_loaded('hidden')->new(
592             name => $name,
593             value => $self->output_value);
594             }
595              
596             sub hidden_fields
597             {
598 22     22 0 46 my($self) = shift;
599              
600 22         37 my @hidden;
601              
602 22 100       95 if($self->coalesce_hidden_fields)
603             {
604 6         53 foreach my $field ($self->fields)
605             {
606 24         100 push(@hidden, $field->hidden_field);
607             }
608             }
609             else
610             {
611 16         119 foreach my $field ($self->fields)
612             {
613 48         181 push(@hidden, $field->hidden_fields);
614             }
615             }
616              
617 22 50       93 return (wantarray) ? @hidden : \@hidden;
618             }
619              
620             sub html_hidden_field
621             {
622 4     4 0 18 my($self) = shift;
623              
624 4 100       23 if(defined $self->output_value)
625             {
626 3         29 return $self->hidden_field->html_field;
627             }
628              
629 1         4 return $self->html_hidden_fields;
630             }
631              
632             sub xhtml_hidden_field
633             {
634 4     4 0 13 my($self) = shift;
635              
636 4 100       17 if(defined $self->output_value)
637             {
638 3         15 return $self->hidden_field->xhtml_field;
639             }
640              
641 1         5 return $self->xhtml_hidden_fields;
642             }
643              
644             sub html_hidden_fields
645             {
646 10     10 0 47 my($self) = shift;
647              
648 10         19 my @html;
649              
650 10         45 foreach my $field ($self->hidden_fields(@_))
651             {
652 46         164 push(@html, $field->html_field);
653             }
654              
655 10 50       258 return (wantarray) ? @html : join("\n", @html);
656             }
657              
658             sub xhtml_hidden_fields
659             {
660 6     6 0 22 my($self) = shift;
661              
662 6         18 my @xhtml;
663              
664 6         79 foreach my $field ($self->hidden_fields(@_))
665             {
666 24         80 push(@xhtml, $field->xhtml_field);
667             }
668              
669 6 50       118 return (wantarray) ? @xhtml : join("\n", @xhtml);
670             }
671              
672             1;