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   96 use strict;
  12         35  
  12         558  
4              
5 12     12   74 use Carp();
  12         25  
  12         337  
6 12     12   64 use Scalar::Util qw(refaddr);
  12         25  
  12         1045  
7              
8 12     12   7054 use Rose::HTML::Form::Field::Hidden;
  12         71  
  12         118  
9              
10 12     12   112 use base 'Rose::HTML::Object';
  12         32  
  12         2034  
11              
12 12     12   134 use Rose::HTML::Form::Constants qw(FF_SEPARATOR);
  12         80  
  12         2195  
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         290 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   114 );
  12         30  
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         309 foreach my $hook ($self->before_prepare_hooks)
55             {
56 0         0 $hook->($self, @_);
57             }
58              
59 56         640 my %args = @_;
60              
61 56 100       169 unless($args{'form_only'})
62             {
63 31         153 foreach my $field ($self->fields)
64             {
65 103         313 $field->prepare(@_);
66             }
67             }
68              
69 56         195 foreach my $form ($self->forms)
70             {
71 25         189 $form->prepare(form_only => 1, @_);
72             }
73              
74 56         268 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   5141 no strict 'refs';
  12         43  
  12         2657  
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   72 *add_field_type_classes = \&Rose::HTML::Object::add_object_type_classes;
118 12         43 *field_type_classes = \&Rose::HTML::Object::object_type_classes;
119 12         40 *field_type_class = \&Rose::HTML::Object::object_type_class;
120 12         702 *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 5 my($self) = shift;
130              
131 12     12   102 no warnings 'uninitialized';
  12         25  
  12         3091  
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       9 $_->isa('Rose::HTML::Form::Field') ?
  4         56  
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 4 my($self) = shift;
153              
154 12     12   99 no warnings 'uninitialized';
  12         41  
  12         22488  
155              
156 1 50 33     12 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       7 $_->isa('Rose::HTML::Form::Field') ?
  4         43  
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 151     151 0 1849 sub init_field_rank_counter { 1 }
174              
175             sub increment_field_rank_counter
176             {
177 472     472 0 901 my($self) = shift;
178 472         2060 my $rank = $self->field_rank_counter;
179 472         3146 $self->field_rank_counter($rank + 1);
180 472         3018 return $rank;
181             }
182              
183             sub make_field
184             {
185 624     624 0 1554 my($self, $name, $value) = @_;
186              
187 624 100       2529 return $value if(UNIVERSAL::isa($value, 'Rose::HTML::Form::Field'));
188              
189 402         796 my($type, $args);
190              
191 402 100       1381 if(ref $value eq 'HASH')
    50          
192             {
193 330 50       1187 $type = delete $value->{'type'} or Carp::croak "Missing field type";
194 330         684 $args = $value;
195             }
196             elsif(!ref $value)
197             {
198 72         151 $type = $value;
199 72         178 $args = {};
200             }
201             else
202             {
203 0         0 Carp::croak "Not a Rose::HTML::Form::Field object or hash ref: $value";
204             }
205              
206 402   33     1147 my $class = ref $self || $self;
207              
208 402 50       2128 my $field_class = $class->field_type_class($type)
209             or Carp::croak "No field class found for field type '$type'";
210              
211 402 100       21576 unless($field_class->can('new'))
212             {
213 8         32 my $error;
214              
215             TRY:
216             {
217 8         13 local $@;
  8         21  
218 8         760 eval "require $field_class";
219 8         53 $error = $@;
220             }
221              
222 8 50       32 Carp::croak "Failed to load field class $field_class - $error" if($error);
223             }
224              
225             # Compound fields require a name
226 402 100       2230 if(UNIVERSAL::isa($field_class, 'Rose::HTML::Form::Field::Compound'))
227             {
228 46         175 $args->{'name'} = $name;
229             }
230              
231 402         2190 return $field_class->new(%$args);
232             }
233              
234             sub invalidate_field_caches
235             {
236 625     625 0 1172 my($self) = shift;
237              
238 625         2445 $self->{'field_cache'} = {};
239             }
240              
241             sub field
242             {
243 3539     3539 0 14384 my($self, $name, $field) = @_;
244              
245 3539 100       8166 if(@_ == 3)
246             {
247 254 50       1138 unless(UNIVERSAL::isa($field, 'Rose::HTML::Form::Field'))
248             {
249 0         0 $field = $self->make_field($name, $field);
250             }
251              
252 254         855 $field->local_moniker($name);
253              
254 254 50       2205 if($self->isa('Rose::HTML::Form'))
255             {
256 0         0 $field->parent_form($self);
257             }
258             else
259             {
260 254         955 $field->parent_field($self);
261             }
262              
263 254         986 $self->_clear_field_generated_values;
264              
265 254 50       976 unless(defined $field->rank)
266             {
267 0         0 $field->rank($self->increment_field_rank_counter);
268             }
269              
270 254         1284 return $self->{'fields'}{$name} = $self->{'field_cache'}{$name} = $field;
271             }
272              
273 3285 100       9465 if($self->{'fields'}{$name})
274             {
275 3252         12215 return $self->{'fields'}{$name};
276             }
277              
278 33         80 my $sep_pos;
279              
280             # Non-hierarchical name
281 33 50       113 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         86 my $prefix = substr($name, 0, $sep_pos);
288 33         104 my $rest = substr($name, $sep_pos + 1);
289 33         96 $field = $self->field($prefix);
290              
291 33 50       150 if(UNIVERSAL::isa($field, 'Rose::HTML::Form::Field::Compound'))
292             {
293 33         83 $field = $field->field($rest);
294 33 50       235 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 199     199 0 2572 my($self) = shift;
325              
326 199         408 my @added_fields;
327              
328 199 100 100     1011 @_ = @{$_[0]} if(@_ == 1 && ref $_[0] eq 'ARRAY');
  4         20  
329              
330 199         634 while(@_)
331             {
332 477         1309 my $arg = shift;
333              
334 477 100       2841 if(UNIVERSAL::isa($arg, 'Rose::HTML::Form::Field'))
335             {
336 5         12 my $field = $arg;
337              
338 5 50       21 if(refaddr($field) eq refaddr($self))
339             {
340 0         0 Carp::croak "Cannot nest a field within itself";
341             }
342              
343 5         17 $field->local_name($field->name);
344              
345 5 50 33     30 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       48 unless(defined $field->rank)
352             {
353 4         19 $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 472         1114 my $field = shift;
362              
363 472 100 100     2004 if($self->can('form') && $self->form($arg))
364             {
365 1         275 Carp::croak "Cannot add field with the same name as an existing sub-form: $arg";
366             }
367              
368 471 100       7808 if(UNIVERSAL::isa($field, 'Rose::HTML::Form::Field'))
369             {
370 105 50       374 if(refaddr($field) eq refaddr($self))
371             {
372 0         0 Carp::croak "Cannot nest a field within itself";
373             }
374             }
375             else
376             {
377 366         1546 $field = $self->make_field($arg, $field);
378             }
379              
380 471         2525 $field->local_moniker($arg);
381              
382 471 100       4305 unless(defined $field->rank)
383             {
384 468         2095 $field->rank($self->increment_field_rank_counter);
385             }
386              
387 471         2051 $self->field($arg => $field);
388 471         1830 push(@added_fields, $field);
389             }
390             }
391              
392 198         1127 $self->_clear_field_generated_values;
393 198         987 $self->resync_field_names;
394              
395 198 100       1221 return unless(defined wantarray);
396 38 100       651 return wantarray ? @added_fields : $added_fields[0];
397             }
398              
399 23     23 0 286 sub add_field { shift->add_fields(@_) }
400              
401             sub compare_fields
402             {
403 3495     3495 0 8045 my($self, $one, $two) = @_;
404 12     12   112 no warnings 'uninitialized';
  12         27  
  12         17000  
405 3495         12145 $one->name cmp $two->name;
406             }
407              
408             sub resync_field_names
409             {
410 704     704 0 1310 my($self) = shift;
411              
412 704         2087 foreach my $field ($self->fields)
413             {
414 2002         7880 $field->resync_name;
415 2002 100       13887 $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 58 my($self, $name) = (shift, shift);
429              
430 8 50       42 my $field = $self->field($name)
431             or Carp::croak "No field named '$name' in $self";
432              
433 8 100       39 return $field->input_value(@_) if(@_);
434 7         51 return $field->internal_value;
435             }
436              
437             *subfield_value = \&field_value;
438              
439             sub subfield_names
440             {
441 20     20 0 37 my($self) = shift;
442              
443 20         40 my @names;
444              
445 20         68 foreach my $field ($self->fields)
446             {
447 41 100       184 push(@names, $field->name, ($field->can('_subfield_names') ? $field->_subfield_names : ()));
448             }
449              
450 20 50       149 return wantarray ? @names : \@names;
451             }
452              
453             sub _subfield_names
454             {
455 38 50   38   109 map { $_->can('subfield_names') ? $_->subfield_names : $_->name } shift->fields;
  133         416  
456             }
457              
458             sub fields
459             {
460 2229     2229 0 3961 my($self) = shift;
461              
462 2229 100       6854 if(my $fields = $self->{'field_list'})
463             {
464 2061 100       8524 return wantarray ? @$fields : $fields;
465             }
466              
467 168         828 my $fields = $self->{'fields'};
468              
469 168         642 $self->{'field_list'} = [ grep { defined } map { $fields->{$_} } $self->field_monikers ];
  254         643  
  254         639  
470              
471 168 50       466 return wantarray ? @{$self->{'field_list'}} : $self->{'field_list'};
  168         887  
472             }
473              
474             sub fields_as_children
475             {
476 13     13 0 27 my($self) = shift;
477              
478 13 50       56 Carp::croak "Cannot directly set children() for a ", ref($self),
479             ". Use fields(), push_children(), pop_children(), etc." if(@_);
480              
481 13         23 my @children;
482              
483 13         61 foreach my $field ($self->fields)
484             {
485 12 100       52 if($field->is_flat_group)
486             {
487 1         5 push(@children, $field->items);
488             }
489             else
490             {
491 11         26 push(@children, $field);
492             }
493             }
494              
495 13 50       78 return wantarray ? @children : \@children;
496             }
497              
498             *immutable_children = \&fields_as_children;
499              
500             sub num_fields
501             {
502 5     5 0 19 my $fields = shift->fields;
503 5 50 33     47 return $fields && @$fields ? scalar @$fields : 0;
504             }
505              
506             sub field_monikers
507             {
508 216     216 0 475 my($self) = shift;
509              
510 216 100       774 if(my $names = $self->{'field_monikers'})
511             {
512 48 50       384 return wantarray ? @$names : $names;
513             }
514              
515 168         308 my @info;
516              
517 168         431 while(my($name, $field) = each %{$self->{'fields'}})
  422         2060  
518             {
519 254         969 push(@info, [ $name, $field ]);
520             }
521              
522             $self->{'field_monikers'} =
523 168         916 [ map { $_->[0] } sort { $self->compare_fields($a->[1], $b->[1]) } @info ];
  254         832  
  236         952  
524              
525 168 50       529 return wantarray ? @{$self->{'field_monikers'}} : $self->{'field_monikers'};
  168         795  
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 493     493 0 1055 my($self) = shift;
552              
553 493         1660 foreach my $field ($self->fields)
554             {
555 1787         13208 $field->clear();
556             }
557             }
558              
559             sub reset_fields
560             {
561 16     16 0 36 my($self) = shift;
562              
563 16         99 foreach my $field ($self->fields)
564             {
565 54         359 $field->reset();
566             }
567             }
568              
569             sub _clear_field_generated_values
570             {
571 625     625   1193 my($self) = shift;
572 625         1778 $self->{'field_list'} = undef;
573 625         1676 $self->{'field_monikers'} = undef;
574 625         2238 $self->invalidate_field_caches;
575              
576             # XXX: This is super-incestuous
577 625 100       2010 if(my $parent_form = $self->parent_form)
578             {
579 69         273 $parent_form->_clear_field_generated_values;
580             }
581             }
582              
583             sub hidden_field
584             {
585 12     12 0 33 my($self) = shift;
586              
587 12     12   119 no warnings 'uninitialized';
  12         31  
  12         6406  
588 12         68 my $name = $self->fq_name;
589              
590             return
591 12         109 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 51 my($self) = shift;
599              
600 22         45 my @hidden;
601              
602 22 100       179 if($self->coalesce_hidden_fields)
603             {
604 6         88 foreach my $field ($self->fields)
605             {
606 24         174 push(@hidden, $field->hidden_field);
607             }
608             }
609             else
610             {
611 16         159 foreach my $field ($self->fields)
612             {
613 48         274 push(@hidden, $field->hidden_fields);
614             }
615             }
616              
617 22 50       153 return (wantarray) ? @hidden : \@hidden;
618             }
619              
620             sub html_hidden_field
621             {
622 4     4 0 20 my($self) = shift;
623              
624 4 100       53 if(defined $self->output_value)
625             {
626 3         29 return $self->hidden_field->html_field;
627             }
628              
629 1         8 return $self->html_hidden_fields;
630             }
631              
632             sub xhtml_hidden_field
633             {
634 4     4 0 18 my($self) = shift;
635              
636 4 100       25 if(defined $self->output_value)
637             {
638 3         21 return $self->hidden_field->xhtml_field;
639             }
640              
641 1         7 return $self->xhtml_hidden_fields;
642             }
643              
644             sub html_hidden_fields
645             {
646 10     10 0 45 my($self) = shift;
647              
648 10         24 my @html;
649              
650 10         65 foreach my $field ($self->hidden_fields(@_))
651             {
652 46         209 push(@html, $field->html_field);
653             }
654              
655 10 50       343 return (wantarray) ? @html : join("\n", @html);
656             }
657              
658             sub xhtml_hidden_fields
659             {
660 6     6 0 23 my($self) = shift;
661              
662 6         19 my @xhtml;
663              
664 6         37 foreach my $field ($self->hidden_fields(@_))
665             {
666 24         96 push(@xhtml, $field->xhtml_field);
667             }
668              
669 6 50       188 return (wantarray) ? @xhtml : join("\n", @xhtml);
670             }
671              
672             1;