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