File Coverage

blib/lib/HTML/Widget/Result.pm
Criterion Covered Total %
statement 154 155 99.3
branch 55 68 80.8
condition 26 32 81.2
subroutine 25 25 100.0
pod 13 13 100.0
total 273 293 93.1


line stmt bran cond sub pod time code
1             package HTML::Widget::Result;
2              
3 88     88   1529 use warnings;
  88         189  
  88         3624  
4 88     88   452 use strict;
  88         149  
  88         3244  
5 88     88   1348 use base qw/HTML::Widget::Accessor/;
  88         160  
  88         8936  
6 88     88   83743 use HTML::Widget::Container;
  88         271  
  88         1760  
7 88     88   67356 use HTML::Widget::Error;
  88         250  
  88         644  
8 88     88   171874 use HTML::Element;
  88         3230647  
  88         950  
9 88     88   138022 use Storable 'dclone';
  88         455536  
  88         8378  
10 88     88   868 use Carp qw/carp croak/;
  88         199  
  88         9670  
11              
12             __PACKAGE__->mk_accessors(
13             qw/attributes container subcontainer strict submitted
14             element_container_class implicit_subcontainer/
15             );
16             __PACKAGE__->mk_attr_accessors(qw/action enctype id method empty_errors/);
17              
18 88     88   577 use overload '""' => sub { return shift->as_xml }, fallback => 1;
  88     70   189  
  88         1336  
  70         17005  
19              
20             *attrs = \&attributes;
21             *name = \&id;
22             *error = \&errors;
23             *has_error = \&has_errors;
24             *have_errors = \&has_errors;
25             *element = \&elements;
26             *parameters = \¶ms;
27             *tag = \&container;
28             *subtag = \&subcontainer;
29             *is_submitted = \&submitted;
30              
31             =head1 NAME
32              
33             HTML::Widget::Result - Result Class
34              
35             =head1 SYNOPSIS
36              
37             see L
38              
39             =head1 DESCRIPTION
40              
41             Result Class.
42              
43             =head1 METHODS
44              
45             =head2 action
46              
47             Arguments: $action
48              
49             Return Value: $action
50              
51             Contains the form action.
52              
53             =head2 as_xml
54              
55             Return Value: $xml
56              
57             Returns xml.
58              
59             =cut
60              
61             sub as_xml {
62 81     81 1 994 my $self = shift;
63              
64 81         725 my $element_container_class = $self->{element_container_class};
65              
66 81         628 my $c = HTML::Element->new( $self->container );
67              
68 81         520 $c->attr( $_ => ${ $self->attributes }{$_} )
  178         2949  
69 81         3327 for ( keys %{ $self->attributes } );
70              
71 81         8200 my $params = dclone $self->{_params};
72              
73 81         472 for my $element (
74             $self->_get_elements(
75             $self->{_elements}, $params, $element_container_class
76             ) )
77             {
78 83 50       582 $c->push_content( $element->as_list ) unless $element->passive;
79             }
80 81         4084 return $c->as_XML;
81             }
82              
83             =head2 container
84              
85             Arguments: $tag
86              
87             Return Value: $tag
88              
89             Contains the container tag.
90              
91             =head2 enctype
92              
93             Arguments: $enctype
94              
95             Return Value: $enctype
96              
97             Contains the form encoding type.
98              
99             =head2 errors
100              
101             =head2 error
102              
103             Arguments: $name, $type
104              
105             Return Value: @errors
106              
107             Returns a list of L objects.
108              
109             my @errors = $form->errors;
110             my @errors = $form->errors('foo');
111             my @errors = $form->errors( 'foo', 'ASCII' );
112              
113             L is an alias for L.
114              
115             =cut
116              
117             sub errors {
118 129     129 1 5802 my ( $self, $name, $type ) = @_;
119              
120 129 100 100     788 return 0 if $name && !$self->{_errors}->{$name};
121              
122 127         258 my $errors = [];
123 127   100     481 my @names = $name || keys %{ $self->{_errors} };
124 127         279 for my $n (@names) {
125 93         686 for my $error ( @{ $self->{_errors}->{$n} } ) {
  93         483  
126 100 100 100     1031 next if $type && $error->{type} ne $type;
127 96         357 push @$errors, $error;
128             }
129             }
130 127         758 return @$errors;
131             }
132              
133             =head2 elements
134              
135             =head2 element
136              
137             Arguments: $name (optional)
138              
139             Return Value: @elements
140              
141             If C<$name> argument is supplied, returns a L
142             object for the first element matching C<$name>. Otherwise, returns a list
143             of L objects for all elements.
144              
145             my @form = $f->elements;
146             my $age = $f->elements('age');
147              
148             L is an alias for L.
149              
150             =cut
151              
152             sub elements {
153 8     8 1 1114 my ( $self, $name ) = @_;
154              
155 8         672 my $params = dclone $self->{_params};
156              
157 8 100       42 if ( $self->implicit_subcontainer ) {
158 6         70 return $self->_get_elements(
159             $self->{_elements}->[0]->content, $params,
160             $self->{element_container_class}, $name
161             );
162             }
163              
164 2         24 return $self->_get_elements( $self->{_elements}, $params,
165             $self->{element_container_class}, $name );
166             }
167              
168             =head2 elements_ref
169              
170             Arguments: $name (optional)
171              
172             Return Value: \@elements
173              
174             Accepts the same arguments as L, but returns an arrayref
175             of results instead of a list.
176              
177             =cut
178              
179             sub elements_ref {
180 1     1 1 8 my $self = shift;
181              
182 1         4 return [ $self->elements(@_) ];
183             }
184              
185             =head2 find_result_element
186              
187             Arguments: $name
188              
189             Return Value: @elements
190              
191             Looks for the named element and returns a L
192             object for it if found.
193              
194             =cut
195              
196             sub find_result_element {
197 1     1 1 493 my ( $self, $name ) = @_;
198              
199 1         4 my @elements = $self->find_elements( name => $name );
200 1 50       18 return unless @elements;
201              
202 1         45 my $params = dclone $self->{_params};
203              
204 1         6 return $self->_get_elements( [ $elements[0] ],
205             $params, $self->{element_container_class}, $name );
206             }
207              
208             =head2 elements_for
209              
210             Arguments: $name
211              
212             Return Value: @elements
213              
214             If the named element is a Block or NullContainer element, return a list
215             of L objects for the contents of that element.
216              
217             =cut
218              
219             sub elements_for {
220 1     1 1 475 my ( $self, $name ) = @_;
221              
222 1         5 my @elements = $self->find_elements( name => $name );
223 1 50       7 return unless @elements;
224              
225 1         2 my $ble = $elements[0];
226 1 50       8 return unless $ble->isa('HTML::Widget::Element::NullContainer');
227              
228 1         25 my $params = dclone $self->{_params};
229              
230 1         6 return $self->_get_elements( $ble->content, $params,
231             $self->{element_container_class} );
232             }
233              
234             # code reuse++
235             sub _get_elements {
236 91     91   272 my ( $self, $elements, $params, $element_container_class, $name ) = @_;
237              
238 91         160 my %javascript;
239 91         172 for my $js_callback ( @{ $self->{_js_callbacks} } ) {
  91         317  
240 161         546 my $javascript = $js_callback->( $self->name );
241 161         611 for my $key ( keys %$javascript ) {
242 199 50       918 $javascript{$key} .= $javascript->{$key} if $javascript->{$key};
243             }
244             }
245              
246             # the hashref of args is carried through - recursively as necessary
247             # - to _containerize_elements().
248 91         525 return $self->_containerize_elements(
249             $elements,
250             { name => $name,
251             params => $params,
252             element_container_class => $element_container_class,
253             javascript => \%javascript,
254             toplevel => 1,
255             submitted => $self->submitted,
256             } );
257             }
258              
259             # also called by HTML::Element::Block, so code reuse++ again
260             sub _containerize_elements {
261 195     195   2121 my ( $self, $elements, $argsref ) = @_;
262              
263 195         7244 my $args = dclone $argsref; # make copy to pass on
264 195         998 my ( $element_container_class, $javascript, $name, $params, $toplevel )
265             = @$args{qw(element_container_class javascript name params toplevel)};
266 195         392 delete $args->{toplevel};
267              
268 195         265 my @content;
269 195         423 for my $element (@$elements) {
270 384 100       844 local $element->{container_class} = $element_container_class
271             if $element_container_class;
272 384 100 100     1414 local $element->{_anonymous} = 1
273             if ( $self->implicit_subcontainer and $toplevel );
274 384         3521 my ( $value, $error ) = ( undef, undef );
275 384         766 my $ename = $element->{name};
276 384 100 66     2108 $value = $params->{$ename} if ( defined($ename) && $params );
277 384 100 66     1106 next if ( defined($name) && defined($ename) && ( $ename ne $name ) );
      100        
278 376 100 66     1707 $value = $params->{$ename} if ( defined($ename) && $params );
279 376 100       1332 $error = $self->{_errors}->{$ename} if defined $ename;
280 376         2090 my $container = $element->containerize( $self, $value, $error, $args );
281 376   50     19829 $container->{javascript} ||= '';
282 376 50 66     1744 $container->{javascript} .= $javascript->{$ename}
283             if ( $ename and $javascript->{$ename} );
284 376 100       902 return $container if defined $name;
285 371         1095 push @content, $container;
286             }
287 190         1254 return @content;
288             }
289              
290             =head2 find_elements
291              
292             Return Value: @elements
293              
294             Exactly the same as L
295              
296             =cut
297              
298             sub find_elements {
299              
300             # WARNING: Not safe for subclassing
301 44     44 1 213 return shift->HTML::Widget::find_elements(@_);
302             }
303              
304             =head2 empty_errors
305              
306             Arguments: $bool
307              
308             Return Value: $bool
309              
310             Create spans for errors even when there's no errors.. (For AJAX validation validation)
311              
312             =head2 has_errors
313              
314             =head2 has_error
315              
316             =head2 have_errors
317              
318             Arguments: $name
319              
320             Return Value: $bool
321              
322             Returns a list of element names.
323              
324             my @names = $form->has_errors;
325             my $error = $form->has_errors($name);
326              
327             L and L are aliases for L.
328              
329             =cut
330              
331             sub has_errors {
332 249     249 1 1864 my ( $self, $name ) = @_;
333 249         384 my @names = keys %{ $self->{_errors} };
  249         9918  
334 249 100       1088 return @names unless $name;
335 9 100       23 return 1 if grep {/$name/} @names;
  9         182  
336 4         24 return 0;
337             }
338              
339             =head2 id
340              
341             Arguments: $id
342              
343             Return Value: $id
344              
345             Contains the widget id.
346              
347             =head2 legend
348              
349             Arguments: $legend
350              
351             Return Value: $legend
352              
353             Contains the legend.
354              
355             =head2 method
356              
357             Arguments: $method
358              
359             Return Value: $method
360              
361             Contains the form method.
362              
363             =head2 param
364              
365             Arguments: $name
366              
367             Return Value (scalar context): $value or \@values
368              
369             Return Value (list context): @values
370              
371             Returns valid parameters with a CGI.pm-compatible param method. (read-only)
372              
373             =cut
374              
375             sub param {
376 135     135 1 4826 my $self = shift;
377              
378 135 50       660 carp 'param method is readonly' if @_ > 1;
379              
380 135 50       414 if ( @_ == 1 ) {
381              
382 135         239 my $param = shift;
383              
384 135         463 my $valid = $self->valid($param);
385 135 100 66     899 if ( !$valid || ( !exists $self->{_params}->{$param} ) ) {
386 7 50       51 return wantarray ? () : undef;
387             }
388              
389 128 100       446 if ( ref $self->{_params}->{$param} eq 'ARRAY' ) {
390             return (wantarray)
391 17 50       120 ? @{ $self->{_params}->{$param} }
  17         110  
392             : $self->{_params}->{$param}->[0];
393             }
394             else {
395             return (wantarray)
396 111 100       828 ? ( $self->{_params}->{$param} )
397             : $self->{_params}->{$param};
398             }
399             }
400              
401 0         0 return $self->valid;
402             }
403              
404             =head2 params
405              
406             =head2 parameters
407              
408             Return Value: \%params
409              
410             Returns validated params as hashref.
411              
412             L is an alias for L.
413              
414             =cut
415              
416             sub params {
417 15     15 1 34 my $self = shift;
418 15         42 my @names = $self->valid;
419 15         26 my %params;
420 15         27 for my $name (@names) {
421 25         73 my @values = $self->param($name);
422 25 100       77 if ( @values > 1 ) {
423 1         10 $params{$name} = \@values;
424             }
425             else {
426 24         74 $params{$name} = $values[0];
427             }
428             }
429 15         111 return \%params;
430             }
431              
432             =head2 subcontainer
433              
434             Arguments: $tag
435              
436             Return Value: $tag
437              
438             Contains the subcontainer tag.
439              
440             =head2 strict
441              
442             Arguments: $bool
443              
444             Return Value: $bool
445              
446             Only consider parameters that pass at least one constraint valid.
447              
448             =head2 submitted
449              
450             =head2 is_submitted
451              
452             Return Value: $bool
453              
454             Returns true if C<< $widget->process >> received a C<$query> object.
455              
456             L is an alias for L.
457              
458             =head2 valid
459              
460             Return Value: @names
461              
462             Arguments: $name
463              
464             Return Value: $bool
465              
466             Returns a list of element names. Returns true/false if a name is given.
467              
468             my @names = $form->valid;
469             my $valid = $form->valid($name);
470              
471             =cut
472              
473             sub valid {
474 233     233 1 18061 my ( $self, $name ) = @_;
475 233         741 my @errors = $self->has_errors;
476 233         339 my @names;
477 233 100       858 if ( $self->strict ) {
478 17         111 for my $constraint ( @{ $self->{_constraints} } ) {
  17         41  
479 34         90 my $names = $constraint->names;
480 34 50       252 push @names, @$names if $names;
481             }
482             }
483             else {
484 216         1510 @names = keys %{ $self->{_params} };
  216         885  
485             }
486 233         2103 my %valid;
487 233         518 CHECK: for my $name (@names) {
488 576         1015 for my $error (@errors) {
489 265 100       1541 next CHECK if $name eq $error;
490             }
491 477         1115 $valid{$name}++;
492             }
493 233         721 my @valid = keys %valid;
494 233 100       734 return @valid unless $name;
495 215 100       418 return 1 if grep {/\Q$name/} @valid;
  448         4543  
496 33         212 return 0;
497             }
498              
499             =head2 add_valid
500              
501             Arguments: $key, $value
502              
503             Return Value: $value
504              
505             Adds another valid value to the hash.
506              
507             =cut
508              
509             sub add_valid {
510 2     2 1 7 my ( $self, $key, $value ) = @_;
511 2         9 $self->{_params}->{$key} = $value;
512 2         6 return $value;
513             }
514              
515             =head2 add_error
516              
517             Arguments: \%attributes
518              
519             Return Value: $error
520              
521             $result->add_error({ name => 'foo' });
522              
523             This allows you to add custom error messages after the widget has processed
524             the input params.
525              
526             Accepts 'name', 'type' and 'message' arguments.
527             The 'name' argument is required. The default value for 'type' is 'Custom'.
528             The default value for 'message' is 'Invalid Input'.
529              
530             An example of use.
531              
532             if ( ! $result->has_errors ) {
533             my $user = $result->valid('username');
534             my $pass = $result->valid('password');
535            
536             if ( ! $app->login( $user, $pass ) ) {
537             $result->add_error({
538             name => 'password',
539             message => 'Incorrect Password',
540             });
541             }
542             }
543              
544             In this example, the C<$result> initially contains no errors. If the login()
545             is unsuccessful though, add_error() is used to add an error to the password
546             Element. If the user is shown the form again using C<< $result->as_xml >>,
547             they will be shown an appropriate error message alongside the password
548             field.
549              
550             =cut
551              
552             sub add_error {
553 4     4 1 1506 my ( $self, $args ) = @_;
554              
555 4 50       20 croak "name argument required" unless defined $args->{name};
556              
557 4 100       12 $args->{type} = 'Custom' if not exists $args->{type};
558 4 50       14 $args->{message} = 'Invalid Input' if not exists $args->{message};
559              
560 4         32 my $error = HTML::Widget::Error->new($args);
561              
562 4         38 push @{ $self->{_errors}->{ $args->{name} } }, $error;
  4         171  
563              
564 4         12 return $error;
565             }
566              
567             =head1 AUTHOR
568              
569             Sebastian Riedel, C
570              
571             =head1 LICENSE
572              
573             This library is free software, you can redistribute it and/or modify it under
574             the same terms as Perl itself.
575              
576             =cut
577              
578             1;