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   92 use strict;
  12         102  
  12         776  
4              
5 12     12   65 use Carp();
  12         31  
  12         250  
6 12     12   135 use Scalar::Util qw(refaddr);
  12         31  
  12         648  
7              
8 12     12   5517 use Rose::HTML::Form::Field::Hidden;
  12         34  
  12         69  
9              
10 12     12   85 use base 'Rose::HTML::Object';
  12         26  
  12         1422  
11              
12 12     12   93 use Rose::HTML::Form::Constants qw(FF_SEPARATOR);
  12         23  
  12         1492  
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         197 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   87 );
  12         23  
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 106 my($self) = shift;
53              
54 56         151 foreach my $hook ($self->before_prepare_hooks)
55             {
56 0         0 $hook->($self, @_);
57             }
58              
59 56         478 my %args = @_;
60              
61 56 100       120 unless($args{'form_only'})
62             {
63 31         88 foreach my $field ($self->fields)
64             {
65 103         280 $field->prepare(@_);
66             }
67             }
68              
69 56         148 foreach my $form ($self->forms)
70             {
71 25         148 $form->prepare(form_only => 1, @_);
72             }
73              
74 56         260 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   4360 no strict 'refs';
  12         35  
  12         2099  
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   76 *add_field_type_classes = \&Rose::HTML::Object::add_object_type_classes;
118 12         45 *field_type_classes = \&Rose::HTML::Object::object_type_classes;
119 12         29 *field_type_class = \&Rose::HTML::Object::object_type_class;
120 12         590 *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 4 my($self) = shift;
130              
131 12     12   87 no warnings 'uninitialized';
  12         41  
  12         2581  
132              
133 1 50 33     4 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         34  
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   92 no warnings 'uninitialized';
  12         37  
  12         15781  
155              
156 1 50 33     7 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       4 $_->isa('Rose::HTML::Form::Field') ?
  4         25  
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 1363 sub init_field_rank_counter { 1 }
174              
175             sub increment_field_rank_counter
176             {
177 471     471 0 847 my($self) = shift;
178 471         1452 my $rank = $self->field_rank_counter;
179 471         2850 $self->field_rank_counter($rank + 1);
180 471         2301 return $rank;
181             }
182              
183             sub make_field
184             {
185 622     622 0 1387 my($self, $name, $value) = @_;
186              
187 622 100       2148 return $value if(UNIVERSAL::isa($value, 'Rose::HTML::Form::Field'));
188              
189 401         774 my($type, $args);
190              
191 401 100       1150 if(ref $value eq 'HASH')
    50          
192             {
193 329 50       1068 $type = delete $value->{'type'} or Carp::croak "Missing field type";
194 329         615 $args = $value;
195             }
196             elsif(!ref $value)
197             {
198 72         158 $type = $value;
199 72         144 $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     1026 my $class = ref $self || $self;
207              
208 401 50       1518 my $field_class = $class->field_type_class($type)
209             or Carp::croak "No field class found for field type '$type'";
210              
211 401 100       17874 unless($field_class->can('new'))
212             {
213 8         21 my $error;
214              
215             TRY:
216             {
217 8         16 local $@;
  8         26  
218 8         744 eval "require $field_class";
219 8         71 $error = $@;
220             }
221              
222 8 50       45 Carp::croak "Failed to load field class $field_class - $error" if($error);
223             }
224              
225             # Compound fields require a name
226 401 100       1834 if(UNIVERSAL::isa($field_class, 'Rose::HTML::Form::Field::Compound'))
227             {
228 46         156 $args->{'name'} = $name;
229             }
230              
231 401         2054 return $field_class->new(%$args);
232             }
233              
234             sub invalidate_field_caches
235             {
236 624     624 0 1076 my($self) = shift;
237              
238 624         1848 $self->{'field_cache'} = {};
239             }
240              
241             sub field
242             {
243 4000     4000 0 11652 my($self, $name, $field) = @_;
244              
245 4000 100       8210 if(@_ == 3)
246             {
247 254 50       928 unless(UNIVERSAL::isa($field, 'Rose::HTML::Form::Field'))
248             {
249 0         0 $field = $self->make_field($name, $field);
250             }
251              
252 254         732 $field->local_moniker($name);
253              
254 254 50       1771 if($self->isa('Rose::HTML::Form'))
255             {
256 0         0 $field->parent_form($self);
257             }
258             else
259             {
260 254         815 $field->parent_field($self);
261             }
262              
263 254         836 $self->_clear_field_generated_values;
264              
265 254 50       777 unless(defined $field->rank)
266             {
267 0         0 $field->rank($self->increment_field_rank_counter);
268             }
269              
270 254         1051 return $self->{'fields'}{$name} = $self->{'field_cache'}{$name} = $field;
271             }
272              
273 3746 100       8253 if($self->{'fields'}{$name})
274             {
275 3713         11133 return $self->{'fields'}{$name};
276             }
277              
278 33         71 my $sep_pos;
279              
280             # Non-hierarchical name
281 33 50       106 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         74 my $prefix = substr($name, 0, $sep_pos);
288 33         75 my $rest = substr($name, $sep_pos + 1);
289 33         83 $field = $self->field($prefix);
290              
291 33 50       147 if(UNIVERSAL::isa($field, 'Rose::HTML::Form::Field::Compound'))
292             {
293 33         78 $field = $field->field($rest);
294 33 50       260 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 2406 my($self) = shift;
325              
326 198         741 my @added_fields;
327              
328 198 100 100     758 @_ = @{$_[0]} if(@_ == 1 && ref $_[0] eq 'ARRAY');
  4         14  
329              
330 198         527 while(@_)
331             {
332 476         887 my $arg = shift;
333              
334 476 100       2443 if(UNIVERSAL::isa($arg, 'Rose::HTML::Form::Field'))
335             {
336 5         15 my $field = $arg;
337              
338 5 50       38 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     36 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       40 unless(defined $field->rank)
352             {
353 4         20 $field->rank($self->increment_field_rank_counter);
354             }
355              
356 5         20 $self->field($field->local_name => $field);
357 5         31 push(@added_fields, $field);
358             }
359             else
360             {
361 471         887 my $field = shift;
362              
363 471 100 100     1466 if($self->can('form') && $self->form($arg))
364             {
365 1         246 Carp::croak "Cannot add field with the same name as an existing sub-form: $arg";
366             }
367              
368 470 100       5977 if(UNIVERSAL::isa($field, 'Rose::HTML::Form::Field'))
369             {
370 105 50       483 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         1247 $field = $self->make_field($arg, $field);
378             }
379              
380 470         2023 $field->local_moniker($arg);
381              
382 470 100       3492 unless(defined $field->rank)
383             {
384 467         1430 $field->rank($self->increment_field_rank_counter);
385             }
386              
387 470         1677 $self->field($arg => $field);
388 470         1578 push(@added_fields, $field);
389             }
390             }
391              
392 197         778 $self->_clear_field_generated_values;
393 197         812 $self->resync_field_names;
394              
395 197 100       855 return unless(defined wantarray);
396 38 100       367 return wantarray ? @added_fields : $added_fields[0];
397             }
398              
399 23     23 0 293 sub add_field { shift->add_fields(@_) }
400              
401             sub compare_fields
402             {
403 3581     3581 0 7081 my($self, $one, $two) = @_;
404 12     12   96 no warnings 'uninitialized';
  12         50  
  12         14055  
405 3581         8317 $one->name cmp $two->name;
406             }
407              
408             sub resync_field_names
409             {
410 876     876 0 1522 my($self) = shift;
411              
412 876         1893 foreach my $field ($self->fields)
413             {
414 2544         7883 $field->resync_name;
415 2544 100       12761 $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 63 my($self, $name) = (shift, shift);
429              
430 8 50       29 my $field = $self->field($name)
431             or Carp::croak "No field named '$name' in $self";
432              
433 8 100       43 return $field->input_value(@_) if(@_);
434 7         35 return $field->internal_value;
435             }
436              
437             *subfield_value = \&field_value;
438              
439             sub subfield_names
440             {
441 20     20 0 43 my($self) = shift;
442              
443 20         42 my @names;
444              
445 20         48 foreach my $field ($self->fields)
446             {
447 41 100       159 push(@names, $field->name, ($field->can('_subfield_names') ? $field->_subfield_names : ()));
448             }
449              
450 20 50       116 return wantarray ? @names : \@names;
451             }
452              
453             sub _subfield_names
454             {
455 38 50   38   94 map { $_->can('subfield_names') ? $_->subfield_names : $_->name } shift->fields;
  133         322  
456             }
457              
458             sub fields
459             {
460 2721     2721 0 4350 my($self) = shift;
461              
462 2721 100       6393 if(my $fields = $self->{'field_list'})
463             {
464 2553 100       8342 return wantarray ? @$fields : $fields;
465             }
466              
467 168         301 my $fields = $self->{'fields'};
468              
469 168         565 $self->{'field_list'} = [ grep { defined } map { $fields->{$_} } $self->field_monikers ];
  254         546  
  254         500  
470              
471 168 50       404 return wantarray ? @{$self->{'field_list'}} : $self->{'field_list'};
  168         497  
472             }
473              
474             sub fields_as_children
475             {
476 13     13 0 26 my($self) = shift;
477              
478 13 50       37 Carp::croak "Cannot directly set children() for a ", ref($self),
479             ". Use fields(), push_children(), pop_children(), etc." if(@_);
480              
481 13         24 my @children;
482              
483 13         43 foreach my $field ($self->fields)
484             {
485 12 100       49 if($field->is_flat_group)
486             {
487 1         4 push(@children, $field->items);
488             }
489             else
490             {
491 11         25 push(@children, $field);
492             }
493             }
494              
495 13 50       57 return wantarray ? @children : \@children;
496             }
497              
498             *immutable_children = \&fields_as_children;
499              
500             sub num_fields
501             {
502 5     5 0 26 my $fields = shift->fields;
503 5 50 33     50 return $fields && @$fields ? scalar @$fields : 0;
504             }
505              
506             sub field_monikers
507             {
508 216     216 0 406 my($self) = shift;
509              
510 216 100       549 if(my $names = $self->{'field_monikers'})
511             {
512 48 50       233 return wantarray ? @$names : $names;
513             }
514              
515 168         261 my @info;
516              
517 168         286 while(my($name, $field) = each %{$self->{'fields'}})
  422         1422  
518             {
519 254         697 push(@info, [ $name, $field ]);
520             }
521              
522             $self->{'field_monikers'} =
523 168         815 [ map { $_->[0] } sort { $self->compare_fields($a->[1], $b->[1]) } @info ];
  254         787  
  239         774  
524              
525 168 50       439 return wantarray ? @{$self->{'field_monikers'}} : $self->{'field_monikers'};
  168         644  
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 1222 my($self) = shift;
552              
553 584         1302 foreach my $field ($self->fields)
554             {
555 2123         9796 $field->clear();
556             }
557             }
558              
559             sub reset_fields
560             {
561 16     16 0 34 my($self) = shift;
562              
563 16         42 foreach my $field ($self->fields)
564             {
565 54         242 $field->reset();
566             }
567             }
568              
569             sub _clear_field_generated_values
570             {
571 624     624   1101 my($self) = shift;
572 624         1270 $self->{'field_list'} = undef;
573 624         1249 $self->{'field_monikers'} = undef;
574 624         1833 $self->invalidate_field_caches;
575              
576             # XXX: This is super-incestuous
577 624 100       1527 if(my $parent_form = $self->parent_form)
578             {
579 69         173 $parent_form->_clear_field_generated_values;
580             }
581             }
582              
583             sub hidden_field
584             {
585 12     12 0 62 my($self) = shift;
586              
587 12     12   95 no warnings 'uninitialized';
  12         67  
  12         5449  
588 12         37 my $name = $self->fq_name;
589              
590             return
591 12         67 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         36 my @hidden;
601              
602 22 100       89 if($self->coalesce_hidden_fields)
603             {
604 6         49 foreach my $field ($self->fields)
605             {
606 24         109 push(@hidden, $field->hidden_field);
607             }
608             }
609             else
610             {
611 16         116 foreach my $field ($self->fields)
612             {
613 48         192 push(@hidden, $field->hidden_fields);
614             }
615             }
616              
617 22 50       119 return (wantarray) ? @hidden : \@hidden;
618             }
619              
620             sub html_hidden_field
621             {
622 4     4 0 16 my($self) = shift;
623              
624 4 100       25 if(defined $self->output_value)
625             {
626 3         38 return $self->hidden_field->html_field;
627             }
628              
629 1         5 return $self->html_hidden_fields;
630             }
631              
632             sub xhtml_hidden_field
633             {
634 4     4 0 39 my($self) = shift;
635              
636 4 100       15 if(defined $self->output_value)
637             {
638 3         16 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 43 my($self) = shift;
647              
648 10         29 my @html;
649              
650 10         40 foreach my $field ($self->hidden_fields(@_))
651             {
652 46         155 push(@html, $field->html_field);
653             }
654              
655 10 50       300 return (wantarray) ? @html : join("\n", @html);
656             }
657              
658             sub xhtml_hidden_fields
659             {
660 6     6 0 19 my($self) = shift;
661              
662 6         17 my @xhtml;
663              
664 6         21 foreach my $field ($self->hidden_fields(@_))
665             {
666 24         106 push(@xhtml, $field->xhtml_field);
667             }
668              
669 6 50       128 return (wantarray) ? @xhtml : join("\n", @xhtml);
670             }
671              
672             1;