File Coverage

blib/lib/CGI/FormBuilderX/More.pm
Criterion Covered Total %
statement 122 138 88.4
branch 50 78 64.1
condition 18 40 45.0
subroutine 20 20 100.0
pod 13 13 100.0
total 223 289 77.1


line stmt bran cond sub pod time code
1             package CGI::FormBuilderX::More;
2              
3 3     3   342711 use warnings;
  3         6  
  3         100  
4 3     3   18 use strict;
  3         5  
  3         180  
5              
6             =head1 NAME
7              
8             CGI::FormBuilderX::More - Additional input gathering/interrogating functionality for CGI::FormBuilder
9              
10             =head1 VERSION
11              
12             Version 0.020
13              
14             =cut
15              
16             our $VERSION = '0.020';
17              
18             =head1 SYNOPSIS
19              
20             use CGI::FormBuilderX::More;
21              
22             my $form = CGI::FormBuilderX::More( ... );
23              
24             if ($form->pressed("edit")) {
25             my $input = $form->input_slice(qw/title description/);
26             # $input is { title => ..., description => ... } *ONLY*
27             ...
28             }
29             elsif ($form->pressed("view") && ! $form->missing("section")) {
30             # The paramter "section" is defined and is not ''
31             ...
32             }
33              
34             print $form->render;
35              
36             ...
37              
38             # Using the alternative, subroutine-driven, validation
39              
40             my $form = CGI::FormBuilderX::More( ..., validate => sub {
41             my ($form, $error) = @_;
42              
43             if (! exists $_{username}) {
44             $error->("username is required"); # Register the error
45             }
46             elsif ($_{username} =~ m/\W/) {
47             $error->("username is malformed"); # A username was given but it's bad
48             }
49              
50             if (! exists $_{password}) {
51             $error->("password is required"); # Another error...
52             }
53              
54             return if $error->(); # See if we've accumulated any errors
55              
56             unless (&authenticate({ $form->input_slice(qw/username password/) })) {
57             $error->("no such username or incorrect password");
58             }
59             });
60              
61             if ($form->validate) {
62              
63             }
64             else {
65              
66             }
67              
68             =head1 DESCRIPTION
69              
70             CGI::FormBuilderX::More extends CGI::FormBuilder by adding some convenience methods. Specifically,
71             it adds methods for generating param lists, generating param hash slices, determining whether a param is "missing",
72             and finding out which submit button was pressed.
73              
74             =head1 EXPORT
75              
76             =head2 missing( )
77              
78             Returns 1 if is not defined or the empty string ('')
79             Returns 0 otherwise
80              
81             Note, the number 0 is NOT a "missing" value
82              
83             =cut
84              
85 3     3   14 use base qw/CGI::FormBuilder/;
  3         10  
  3         7545  
86              
87 3     3   112367 use CGI::FormBuilderX::More::InputTie;
  3         10  
  3         317  
88              
89             use Sub::Exporter -setup => {
90             exports => [
91             missing => sub { return sub {
92 0   0     0 return ! defined $_[0] || $_[0] eq '';
93 0         0 } },
94 3         47 ],
95 3     3   4654 };
  3         47633  
96              
97             sub _attribute($) {
98 49     49   244 return "_CGI_FBX_M_$_[0]";
99             }
100              
101             =head1 METHODS
102              
103             =head2 CGI::FormBuilderX::More->new( ... )
104              
105             Returns a new CGI::FormBuilderX::More object
106              
107             Configure exactly as you would a normal CGI::FormBuilder object
108              
109             =cut
110              
111             sub new {
112 3     3 1 11037 my $class = shift;
113              
114 3         8 my $hash;
115 3 50 33     34 if (@_ == 1 && ref $_[0] eq "HASH") {
    50          
116 0         0 $hash = $_[0];
117             }
118             elsif (@_ > 1) {
119 3         14 $hash = { @_ };
120             }
121              
122 3         7 my $self;
123 3 50       12 if ($hash) {
124 3         6 my $validate;
125 3 100 66     28 if ($hash->{validate} && ref $hash->{validate} eq "CODE") {
126 1         3 $validate = delete $hash->{validate};
127             }
128 3         39 $self = $class->SUPER::new($hash);
129 3         10798 $self->{_attribute("validate")} = $validate;
130             }
131             else {
132 0         0 $self = $class->SUPER::new(@_);
133             }
134            
135 3         29 return $self;
136             }
137              
138             =head2 pressed( )
139              
140             Returns the value of ->param(_submit_) if _submit_ exists and has a value
141              
142             If not, then returns the value of ->param("_submit_.x") if "_submit_.x" exists and has a value
143              
144             If is not given, then it will use the form's default submit name to check.
145              
146             To suppress the automatic prefixing of with "_submit", simply prefix a "+" to
147              
148             If already has a "_submit" prefix, then none will be applied.
149              
150             Otherwise, returns undef
151              
152             Essentially, you can use this method to find out which button the user pressed. This method does not require
153             any javascript on the client side to work
154              
155             It checks "_submit_.x" because for image buttons, some browsers only submit the .x and .y values of where the user
156             pressed.
157              
158             =cut
159              
160             sub pressed {
161 3     3 1 7 my $self = shift;
162              
163 3         5 my ($name, $default);
164 3 50       9 if (! @_) {
165 0         0 $name = $self->submitname;
166 0         0 $default = 1;
167             }
168             else {
169 3         6 $name = shift;
170 3 50 33     43 if (defined $name && length $name) {
171 3 50 33     30 $name = "_submit_$name" unless $name =~ m/^_submit/i || $name =~ s/^\+//;
172             }
173             else {
174 0         0 $name = $self->submitname;
175             }
176             }
177              
178 3         8 for ($name, "$name.x") {
179 5 100       11 if (defined (my $value = $self->input_fetch($_))) {
180 2   100     16 return $value || '0E0';
181             }
182             }
183              
184 1 50       5 return $self->submitted if $default;
185              
186 1         5 return undef;
187             }
188              
189             =head2 missing( )
190              
191             Returns 1 if value of the param is not defined or the empty string ('')
192             Returns 0 otherwise
193              
194             Note, the number 0 is NOT a "missing" value
195              
196             value missing
197             ===== =======
198             "xyzzy" no
199             0 no
200             1 no
201             "" yes
202             undef yes
203              
204             =cut
205              
206             sub missing {
207 9     9 1 17 my $self = shift;
208 9         18 my $name = shift;
209 9         28 my $value = $self->input_fetch($name);
210              
211 9 100       51 return 0 if $value;
212 5 50       42 return 1 if ! defined $value;
213 0 0       0 return 1 if $value eq '';
214 0         0 return 0; # value is 0
215             }
216              
217             =head2 input ( , , ..., )
218              
219             Returns a list of values based on the param names given
220              
221             By default, this method will "collapse" multi-value params into the first
222             value of the param. If you'd prefer an array reference of multi-value params
223             instead, pass the option { all => 1 } as the first argument (a hash reference).
224              
225             =cut
226              
227             sub input {
228 3     3 1 9 my $self = shift;
229 3 50 66     31 return $self->input_fetch(@_) if wantarray && 1 == @_ && ! ref $_[0];
      33        
230              
231 3         43 my $control = {};
232 3 100 66     25 $control = shift if ref $_[0] && ref $_[0] eq "HASH";
233 3         8 my $all = 0;
234 3 100       13 $all = $control->{all} if exists $control->{all};
235              
236 3 50       10 my @names = map { ref eq 'ARRAY' ? @$_ : $_ } @_;
  5         26  
237              
238 3         7 my @params;
239 3 100       8 if ($all) {
240 1         4 for (@names) {
241 2         7 my @param = $self->input_fetch($_);
242 2 100       11 push @params, 1 == @param ? $param[0] : \@param;
243             }
244             }
245             else {
246 2         7 for (@names) {
247 3         11 push @params, scalar $self->input_fetch($_);
248             }
249             }
250 3 100       34 return wantarray ? @params : $params[0];
251             }
252              
253             =head2 input_slice( , , ..., )
254              
255             Returns a hash of key/value pairs based on the param names given
256              
257             By default, this method will "collapse" multi-value params into the first
258             value of the param. If you'd prefer an array reference of multi-value params
259             instead, pass the option { all => 1 } as the first argument (a hash reference).
260              
261             =cut
262              
263             sub input_slice {
264 2     2 1 6 my $self = shift;
265 2         4 my $control = {};
266 2 50 33     20 $control = shift if ref $_[0] && ref $_[0] eq "HASH";
267 2         4 my $all = 0;
268 2 50       8 $all = $control->{all} if exists $control->{all};
269              
270 2 50       6 my @names = map { ref eq 'ARRAY' ? @$_ : $_ } @_;
  4         17  
271              
272 2         4 my %slice;
273 2 50       7 if ($all) {
274 0 0       0 %slice = map { my @param = $self->input_fetch($_); ($_ => 1 == @param ? $param[0] : \@param) } @names;
  0         0  
  0         0  
275             }
276             else {
277 2         3 %slice = map { ($_ => scalar $self->input_fetch($_)) } @names;
  4         13  
278             }
279              
280 2 100       20 return wantarray ? %slice : \%slice;
281             }
282              
283             =head2 input_slice_to( , , , ..., )
284              
285             The behavior of this method is similar to C, except instead of returning a new hash, it will modify
286             the hash passed in as the first argument.
287              
288             Returns the original hash passed in
289              
290             =cut
291              
292             sub input_slice_to {
293 1     1 1 3 my $self = shift;
294 1         2 my $hash = shift;
295 1         7 my $slice = { $self->input_slice(@_) };
296 1         9 $hash->{$_} = $slice->{$_} for keys %$slice;
297 1         9 return $hash;
298             }
299              
300             =head2 input_param( )
301              
302             In list context, returns the all the param values associated with
303             In scalar context, returns only the first param value associated with
304              
305             The main difference between C and C is that C only accepts a single argument
306             AND C addresses the param object directly, while C will access the internal C/C hash
307              
308             =cut
309              
310             sub input_param {
311 34     34 1 102 my $self = shift;
312 34         172 my @param = $self->{params}->param($_[0]);
313 34 100       1046 return wantarray ? @param : shift @param;
314             }
315              
316             =head2 validate( [] )
317              
318             In CGI::FormBuilderX::More, we overload to the validate method to offer different behavior. This different
319             behavior is conditional, and depends on the optional first argument, or the value of C passed in to C.
320              
321             If either the first argument or ->new( validate => ... ) is a code reference then $form->validate takes on different behavior:
322              
323             1. %_ is tied() to the form's input parameters
324             2. An error subroutine for recoding errors is passed through as the first argument to the validation subroutine
325             3. Any additional arguments to validate are passed through to the validation subroutine
326             4. The errors are available via $form->errors, which is a list reference
327             5. The errors are also available in the prepared version of $form (e.g. for template rendering)
328             6. $form->validate returns true or false depending on whether any errors were encountered
329              
330             Here is an example validation subroutine:
331              
332             sub {
333             my ($form, $error) = @_;
334              
335             if (! exists $_{username}) {
336             $error->("username is required"); # Register the error
337             }
338             elsif ($_{username} =~ m/\W/) {
339             $error->("username is malformed"); # A username was given but it's bad
340             }
341              
342             if (! exists $_{password}) {
343             $error->("password is required"); # Another error...
344             }
345              
346             return if $error->(); # See if we've accumulated any errors
347              
348             unless (&authenticate({ $form->input_slice(qw/username password/) })) {
349             $error->("no such username or incorrect password");
350             }
351             }
352              
353             =cut
354              
355             sub validate {
356 3     3 1 1810 my $self = shift;
357 3         6 my $code;
358 3 50 33     29 if ($_[0] && ref $_[0] eq "CODE") {
    0          
359 3         7 $code = shift;
360             }
361             elsif ($code = $self->{_attribute("validate")}) {
362             }
363             else {
364 0         0 return $self->SUPER::validate(@_);
365             }
366 3         8 local %_;
367 3         12 $self->input_tie(\%_);
368 3         6 my @errors;
369             my $error = sub {
370 2 0   2   14 return @errors ? 1 : 0 unless @_;
    50          
371 2         8 push @errors, @_;
372 3         18 };
373 3         8 eval {
374 3         13 $code->($self, $error, @_);
375             };
376             {
377 3         10 my $error = $@;
  3         5  
378 3         30 untie %_;
379 3 50       11 die $error if $error;
380             }
381 3         12 $self->{_attribute("errors")} = \@errors;
382 3 100       33 return scalar @errors ? 0 : 1;
383             }
384              
385             =head2 input_tie( )
386              
387             Given a hash reference, C will tie the hash to form input. That is,
388             accessing a hash entry is actually accessing the corresponding form param.
389             Currently, only STORE, FETCH, and EXISTS are implemented.
390              
391             my %hash;
392             $form->input_tie(\%hash);
393              
394             my $value = $hash{username}; # Actually does: $form->input_fetch("username");
395              
396             $hash{password} = "12345"; # Actually does: $form->input_store(password => "12345");
397              
398             return unless exists $hash{extra}; # Actually does: ! $form->missing("extra");
399             # Which checks to see if "extra" is defined and a non-empty string.
400              
401             =cut
402              
403             sub input_tie {
404 3     3 1 7 my $self = shift;
405 3         6 my $hash = shift;
406 3         33 tie %$hash, "CGI::FormBuilderX::More::InputTie", $self;
407 3         7 return $hash;
408             }
409              
410             =head2 input_fetch( )
411              
412             Given a key, C will return the value of first
413             an internal attribute stash, and then request paramters (via C).
414              
415             This allows you get/set values in the form without affecting the underlying request param.
416              
417             In array context, the entire value list is returned. In scalar context, only the first value is returned.
418              
419             =cut
420              
421             sub input_fetch {
422 31     31 1 60 my $self = shift;
423 31         46 my $key = shift;
424 31 100       81 if (exists $self->{_attribute("input")}->{$key}) {
425 3         6 my @param = @{ $self->{_attribute("input")}->{$key} };
  3         10  
426 3 100       22 return wantarray ? @param : shift @param;
427             }
428             else {
429 28         83 return $self->input_param($key);
430             }
431             }
432              
433             =head2 input_store( , , , ..., )
434              
435             Given a key and some values, C will store the values (as an array reference)
436             in an internal attribute stash.
437              
438             This allows you get/set values in the form without affecting the underlying request param.
439              
440             =cut
441              
442             sub input_store {
443 3     3 1 7 my $self = shift;
444 3         6 my $key = shift;
445 3 100 66     33 my @values = 1 == @_ && ref $_[0] eq 'ARRAY' ? @{ $_[0] } : @_;
  1         4  
446 3         10 $self->{_attribute("input")}->{$key} = \@values;
447             }
448              
449             =head2 errors
450              
451             In scalar context, returns an array reference of errors found during validation, if any.
452             In list context, returns the same, but as a list.
453              
454             =cut
455              
456             sub errors {
457 6     6 1 15 my $self = shift;
458 6 50       28 if (@_) {
459 0 0 0     0 my @errors = 1 == @_ && ref $_[0] eq 'ARRAY' ? @{ $_[0] } : @_;
  0         0  
460 0         0 $self->{_attribute("errors")} = \@errors;
461             }
462              
463 6   100     22 my $errors = $self->{_attribute("errors")} || [];
464 6 100       44 return wantarray ? @$errors : [ @$errors ];
465             }
466              
467             =head2 prepare
468              
469             Prepares a hash containing information about the state of the form and returns it.
470              
471             Essentially, returns the same as CGI::FormBuilder->prepare, with the addition of C, which is
472             a list of any errors found during validation.
473              
474             Returns a hash reference in scalar context, and a key/value list in array context.
475              
476             =cut
477              
478             sub prepare {
479 3     3 1 3849 my $self = shift;
480 3         40 my $prepare = $self->SUPER::prepare(@_);
481 3         12581 $prepare->{errors} = $self->errors;
482 3 100       38 return wantarray ? %$prepare : $prepare;
483             }
484              
485             =head1 AUTHOR
486              
487             Robert Krimen, C<< >>
488              
489             =head1 BUGS
490              
491             Please report any bugs or feature requests to C, or through
492             the web interface at L. I will be notified, and then you'll
493             automatically be notified of progress on your bug as I make changes.
494              
495              
496              
497              
498             =head1 SUPPORT
499              
500             You can find documentation for this module with the perldoc command.
501              
502             perldoc CGI::FormBuilderX::More
503              
504              
505             You can also look for information at:
506              
507             =over 4
508              
509             =item * RT: CPAN's request tracker
510              
511             L
512              
513             =item * AnnoCPAN: Annotated CPAN documentation
514              
515             L
516              
517             =item * CPAN Ratings
518              
519             L
520              
521             =item * Search CPAN
522              
523             L
524              
525             =back
526              
527              
528             =head1 ACKNOWLEDGEMENTS
529              
530              
531             =head1 COPYRIGHT & LICENSE
532              
533             Copyright 2007 Robert Krimen, all rights reserved.
534              
535             This program is free software; you can redistribute it and/or modify it
536             under the same terms as Perl itself.
537              
538              
539             =cut
540              
541             1; # End of CGI::FormBuilderX::More