source: text-formbuilder/trunk/lib/Text/FormBuilder.pm @ 63

Last change on this file since 63 was 63, checked in by peichman, 19 years ago

field groups can be directly named as the type in a fieldspec

File size: 37.5 KB
Line 
1package Text::FormBuilder;
2
3use strict;
4use warnings;
5
6use base qw(Exporter Class::ParseText::Base);
7use vars qw($VERSION @EXPORT);
8
9$VERSION = '0.09_02';
10@EXPORT = qw(create_form);
11
12#$::RD_TRACE = 1;
13
14use Carp;
15use Text::FormBuilder::Parser;
16use CGI::FormBuilder;
17
18# the static default options passed to CGI::FormBuilder->new
19my %DEFAULT_OPTIONS = (
20    method => 'GET',
21    keepextras => 1,
22);
23
24# the built in CSS for the template
25my $DEFAULT_CSS = <<END;
26table { padding: 1em; }
27td table { padding: 0; } /* exclude the inner checkbox tables */
28#author, #footer { font-style: italic; }
29caption h2 { padding: .125em .5em; background: #ccc; text-align: left; }
30th { text-align: left; }
31th h3 { padding: .125em .5em; background: #eee; }
32th.label { font-weight: normal; text-align: right; vertical-align: top; }
33td ul { list-style: none; padding-left: 0; margin-left: 0; }
34.note { background: #eee; }
35.sublabel { color: #999; }
36.invalid { background: red; }
37END
38
39# default messages that can be localized
40my %DEFAULT_MESSAGES = (
41    text_author   => 'Created by %s',
42    text_madewith => 'Made with %s version %s',
43    text_required => '(Required fields are marked in <strong>bold</strong>.)',
44    text_invalid  => 'Missing or invalid value.',
45);
46
47my $DEFAULT_CHARSET = 'iso-8859-1';
48
49# options to clean up the code with Perl::Tidy
50my $TIDY_OPTIONS = '-nolq -ci=4 -ce';
51
52my $HTML_EXTS   = qr/\.html?$/;
53my $MODULE_EXTS = qr/\.pm$/;
54my $SCRIPT_EXTS = qr/\.(pl|cgi)$/;
55
56# superautomagical exported function
57sub create_form {
58    my ($source, $options, $destination) = @_;
59    my $parser = __PACKAGE__->parse($source);
60    $parser->build(%{ $options || {} });
61    if ($destination) {
62        if (ref $destination) {
63            croak '[' . (caller(0))[3] . "] Don't know what to do with a ref for $destination";
64            #TODO: what DO ref dests mean?
65        } else {
66            # write webpage, script, or module
67            if ($destination =~ $MODULE_EXTS) {
68                $parser->write_module($destination, 1);
69            } elsif ($destination =~ $SCRIPT_EXTS) {
70                $parser->write_script($destination, 1);
71            } else {
72                $parser->write($destination);
73            }
74        }
75    } else {
76        defined wantarray ? return $parser->form : $parser->write;
77    }
78}
79
80# subclass of Class::ParseText::Base
81sub init {
82    my $self = shift;
83    $self->{parser}         = Text::FormBuilder::Parser->new;
84    $self->{start_rule}     = 'form_spec';
85    $self->{ensure_newline} = 1;
86    return $self;
87}
88
89# this is where a lot of the magic happens
90sub build {
91    my ($self, %options) = @_;
92   
93    # our custom %options:
94    # form_only: use only the form part of the template
95    my $form_only = $options{form_only};
96   
97    # css, extra_css: allow for custom inline stylesheets
98    #   neat trick: css => '@import(my_external_stylesheet.css);'
99    #   will let you use an external stylesheet
100    #   CSS Hint: to get multiple sections to all line up their fields,
101    #   set a standard width for th.label
102    my $css;
103    $css = $options{css} || $DEFAULT_CSS;
104    $css .= $options{extra_css} if $options{extra_css};
105   
106    # messages
107    # code pulled (with modifications) from CGI::FormBuilder
108    if ($options{messages}) {
109        # if its a hashref, we'll just pass it on to CGI::FormBuilder
110       
111        if (my $ref = ref $options{messages}) {
112            # hashref pass on to CGI::FormBuilder
113            croak "[Text::FormBuilder] Argument to 'messages' option must be a filename or hashref" unless $ref eq 'HASH';
114            while (my ($key,$value) = each %DEFAULT_MESSAGES) {
115                $options{messages}{$key} ||= $DEFAULT_MESSAGES{$key};
116            }
117        } else {
118            # filename, just *warn* on missing, and use defaults
119            if (-f $options{messages} && -r _ && open(MESSAGES, "< $options{messages}")) {
120                $options{messages} = { %DEFAULT_MESSAGES };
121                while(<MESSAGES>) {
122                    next if /^\s*#/ || /^\s*$/;
123                    chomp;
124                    my($key,$value) = split ' ', $_, 2;
125                    ($options{messages}{$key} = $value) =~ s/\s+$//;
126                }
127                close MESSAGES;
128            } else {
129                carp '[' . (caller(0))[3] . "] Could not read messages file $options{messages}: $!";
130            }
131        }
132    } else {
133        $options{messages} = { %DEFAULT_MESSAGES };
134    }
135   
136    # character set
137    my $charset = $options{charset};
138   
139    # save the build options so they can be used from write_module
140    $self->{build_options} = { %options };
141   
142    # remove our custom options before we hand off to CGI::FormBuilder
143    delete $options{$_} foreach qw(form_only css extra_css charset);
144   
145    # expand groups
146    if (my %groups = %{ $self->{form_spec}{groups} || {} }) {
147        for my $section (@{ $self->{form_spec}{sections} || [] }) {
148            foreach (grep { $$_[0] eq 'group' } @{ $$section{lines} }) {
149                $$_[1]{group} =~ s/^\%//;       # strip leading % from group var name
150               
151                if (exists $groups{$$_[1]{group}}) {
152                    my @fields; # fields in the group
153                    push @fields, { %$_ } foreach @{ $groups{$$_[1]{group}} };
154                    for my $field (@fields) {
155                        $$field{label} ||= ucfirst $$field{name};
156                        $$field{name} = "$$_[1]{name}_$$field{name}";               
157                    }
158                    $_ = [ 'group', { label => $$_[1]{label} || ucfirst(join(' ',split('_',$$_[1]{name}))), group => \@fields } ];
159                }
160            }
161        }
162    }
163   
164    # the actual fields that are given to CGI::FormBuilder
165    # make copies so that when we trim down the sections
166    # we don't lose the form field information
167    $self->{form_spec}{fields} = [];
168   
169    for my $section (@{ $self->{form_spec}{sections} || [] }) {
170        for my $line (@{ $$section{lines} }) {
171            if ($$line[0] eq 'group') {
172                push @{ $self->{form_spec}{fields} }, { %{$_} } foreach @{ $$line[1]{group} };
173            } elsif ($$line[0] eq 'field') {
174                push @{ $self->{form_spec}{fields} }, { %{$$line[1]} };
175            }
176        }
177    }
178   
179    # substitute in custom validation subs and pattern definitions for field validation
180    my %patterns = %{ $self->{form_spec}{patterns} || {} };
181    my %subs = %{ $self->{form_spec}{subs} || {} };
182   
183    foreach (@{ $self->{form_spec}{fields} }) {
184        if ($$_{validate}) {
185            if (exists $patterns{$$_{validate}}) {
186                $$_{validate} = $patterns{$$_{validate}};
187            # TODO: need the Data::Dumper code to work for this
188            # for now, we just warn that it doesn't work
189            } elsif (exists $subs{$$_{validate}}) {
190                warn '[' . (caller(0))[3] . "] validate coderefs don't work yet";
191                delete $$_{validate};
192##                 $$_{validate} = $subs{$$_{validate}};
193            }
194        }
195    }
196   
197    # get user-defined lists; can't make this conditional because
198    # we need to be able to fall back to CGI::FormBuilder's lists
199    # even if the user didn't define any
200    my %lists = %{ $self->{form_spec}{lists} || {} };
201   
202    # substitute in list names
203    foreach (@{ $self->{form_spec}{fields} }) {
204        next unless $$_{list};
205       
206        $$_{list} =~ s/^\@//;   # strip leading @ from list var name
207       
208        # a hack so we don't get screwy reference errors
209        if (exists $lists{$$_{list}}) {
210            my @list;
211            push @list, { %$_ } foreach @{ $lists{$$_{list}} };
212            $$_{options} = \@list;
213        } else {
214            # assume that the list name is a builtin
215            # and let it fall through to CGI::FormBuilder
216            $$_{options} = $$_{list};
217        }
218    } continue {
219        delete $$_{list};
220    }
221   
222    # special case single-value checkboxes
223    foreach (grep { $$_{type} && $$_{type} eq 'checkbox' } @{ $self->{form_spec}{fields} }) {
224        unless ($$_{options}) {
225            $$_{options} = [ { $$_{name} => $$_{label} || ucfirst join(' ',split(/_/,$$_{name})) } ];
226        }
227    }
228   
229    # use columns for displaying checkbox fields larger than 2 items
230    foreach (@{ $self->{form_spec}{fields} }) {
231        if (ref $$_{options} and @{ $$_{options} } >= 3) {
232            $$_{columns} = int(@{ $$_{options} } / 8) + 1;
233        }
234    }
235   
236    # remove extraneous undefined values
237    for my $field (@{ $self->{form_spec}{fields} }) {
238        defined $$field{$_} or delete $$field{$_} foreach keys %{ $field };
239    }
240   
241    # remove false $$_{required} params because this messes up things at
242    # the CGI::FormBuilder::field level; it seems to be marking required
243    # based on the existance of a 'required' param, not whether it is
244    # true or defined
245    $$_{required} or delete $$_{required} foreach @{ $self->{form_spec}{fields} };
246
247    foreach (@{ $self->{form_spec}{sections} }) {
248        #for my $line (grep { $$_[0] eq 'field' } @{ $$_{lines} }) {
249        for my $line (@{ $$_{lines} }) {
250            if ($$line[0] eq 'field') {
251                $$line[1] = $$line[1]{name};
252##                 $_ eq 'name' or delete $$line[1]{$_} foreach keys %{ $$line[1] };
253##             } elsif ($$line[0] eq 'group') {
254##                 $$line[1] = [ map { $$_{name} } @{ $$line[1]{group} } ];
255            }
256        }
257    }
258   
259    $self->{form} = CGI::FormBuilder->new(
260        %DEFAULT_OPTIONS,
261        # need to explicity set the fields so that simple text fields get picked up
262        fields   => [ map { $$_{name} } @{ $self->{form_spec}{fields} } ],
263        required => [ map { $$_{name} } grep { $$_{required} } @{ $self->{form_spec}{fields} } ],
264        title => $self->{form_spec}{title},
265        text  => $self->{form_spec}{description},
266        template => {
267            type => 'Text',
268            engine => {
269                TYPE       => 'STRING',
270                SOURCE     => $form_only ? $self->_form_template : $self->_template($css, $charset),
271                DELIMITERS => [ qw(<% %>) ],
272            },
273            data => {
274                sections    => $self->{form_spec}{sections},
275                author      => $self->{form_spec}{author},
276                description => $self->{form_spec}{description},
277            },
278        },
279        %options,
280    );
281    $self->{form}->field(%{ $_ }) foreach @{ $self->{form_spec}{fields} };
282   
283    # mark structures as built
284    $self->{built} = 1;
285   
286    return $self;
287}
288
289sub write {
290    my ($self, $outfile) = @_;
291   
292    # automatically call build if needed to
293    # allow the new->parse->write shortcut
294    $self->build unless $self->{built};
295   
296    if ($outfile) {
297        open FORM, "> $outfile";
298        print FORM $self->form->render;
299        close FORM;
300    } else {
301        print $self->form->render;
302    }
303}
304
305# generates the core code to create the $form object
306# the generated code assumes that you have a CGI.pm
307# object named $q
308sub _form_code {
309    my $self = shift;
310   
311    # automatically call build if needed to
312    # allow the new->parse->write shortcut
313    $self->build unless $self->{built};
314   
315    # conditionally use Data::Dumper
316    eval 'use Data::Dumper;';
317    die "Can't write module; need Data::Dumper. $@" if $@;
318   
319    $Data::Dumper::Terse = 1;           # don't dump $VARn names
320    $Data::Dumper::Quotekeys = 0;       # don't quote simple string keys
321   
322    my $css;
323    $css = $self->{build_options}{css} || $DEFAULT_CSS;
324    $css .= $self->{build_options}{extra_css} if $self->{build_options}{extra_css};
325   
326    my %options = (
327        %DEFAULT_OPTIONS,
328        title => $self->{form_spec}{title},
329        text  => $self->{form_spec}{description},
330        fields   => [ map { $$_{name} } @{ $self->{form_spec}{fields} } ],
331        required => [ map { $$_{name} } grep { $$_{required} } @{ $self->{form_spec}{fields} } ],
332        template => {
333            type => 'Text',
334            engine => {
335                TYPE       => 'STRING',
336                SOURCE     => $self->{build_options}{form_only} ? 
337                                $self->_form_template : 
338                                $self->_template($css, $self->{build_options}{charset}),
339                DELIMITERS => [ qw(<% %>) ],
340            },
341            data => {
342                sections    => $self->{form_spec}{sections},
343                author      => $self->{form_spec}{author},
344                description => $self->{form_spec}{description},
345            },
346        }, 
347        %{ $self->{build_options} },
348    );
349   
350    # remove our custom options
351    delete $options{$_} foreach qw(form_only css extra_css);
352   
353    my %module_subs;
354    my $d = Data::Dumper->new([ \%options ], [ '*options' ]);
355   
356    use B::Deparse;
357    my $deparse = B::Deparse->new;
358##     
359##     #TODO: need a workaround/better solution since Data::Dumper doesn't like dumping coderefs
360##     foreach (@{ $self->{form_spec}{fields} }) {
361##         if (ref $$_{validate} eq 'CODE') {
362##             my $body = $deparse->coderef2text($$_{validate});
363##             #$d->Seen({ "*_validate_$$_{name}" => $$_{validate} });
364##             #$module_subs{$$_{name}} = "sub _validate_$$_{name} $$_{validate}";
365##         }
366##     }   
367##     my $sub_code = join("\n", each %module_subs);
368   
369    my $form_options = keys %options > 0 ? $d->Dump : '';
370   
371    my $field_setup = join(
372        "\n", 
373        map { '$form->field' . Data::Dumper->Dump([$_],['*field']) . ';' } @{ $self->{form_spec}{fields} }
374    );
375   
376    return <<END;
377my \$form = CGI::FormBuilder->new(
378    params => \$q,
379    $form_options
380);
381
382$field_setup
383END
384}
385
386sub write_module {
387    my ($self, $package, $use_tidy) = @_;
388
389    croak '[' . (caller(0))[3] . '] Expecting a package name' unless $package;
390   
391    # remove a trailing .pm
392    $package =~ s/\.pm$//;
393##     warn  "[Text::FromBuilder::write_module] Removed extra '.pm' from package name\n" if $package =~ s/\.pm$//;
394   
395    my $form_code = $self->_form_code;
396   
397    my $module = <<END;
398package $package;
399use strict;
400use warnings;
401
402use CGI::FormBuilder;
403
404sub get_form {
405    my \$q = shift;
406
407    $form_code
408   
409    return \$form;
410}
411
412# module return
4131;
414END
415
416    _write_output_file($module, (split(/::/, $package))[-1] . '.pm', $use_tidy);
417    return $self;
418}
419
420sub write_script {
421    my ($self, $script_name, $use_tidy) = @_;
422
423    croak '[' . (caller(0))[3] . '] Expecting a script name' unless $script_name;
424   
425    my $form_code = $self->_form_code;
426   
427    my $script = <<END;
428#!/usr/bin/perl
429use strict;
430use warnings;
431
432use CGI;
433use CGI::FormBuilder;
434
435my \$q = CGI->new;
436
437$form_code
438   
439unless (\$form->submitted && \$form->validate) {
440    print \$form->render;
441} else {
442    # do something with the entered data
443}
444END
445   
446    _write_output_file($script, $script_name, $use_tidy);   
447    return $self;
448}
449
450sub _write_output_file {
451    my ($source_code, $outfile, $use_tidy) = @_;
452    if ($use_tidy) {
453        # clean up the generated code, if asked
454        eval 'use Perl::Tidy';
455        unless ($@) {
456            Perl::Tidy::perltidy(source => \$source_code, destination => $outfile, argv => $TIDY_OPTIONS);
457        } else {
458            carp '[' . (caller(0))[3] . "] Can't tidy the code: $@" if $@;
459            # fallback to just writing it as-is
460            open OUT, "> $outfile" or die $!;
461            print OUT $source_code;
462            close OUT;
463        }
464    } else {
465        # otherwise, just print as is
466        open OUT, "> $outfile" or die $!;
467        print OUT $source_code;
468        close OUT;
469    }
470}
471
472
473sub form {
474    my $self = shift;
475   
476    # automatically call build if needed to
477    # allow the new->parse->write shortcut
478    $self->build unless $self->{built};
479
480    return $self->{form};
481}
482
483sub _form_template {
484    my $self = shift;
485    my $msg_required = $self->{build_options}{messages}{text_required};
486    my $msg_invalid = $self->{build_options}{messages}{text_invalid};
487    return q{<% $description ? qq[<p id="description">$description</p>] : '' %>
488<% (grep { $_->{required} } @fields) ? qq[<p id="instructions">} . $msg_required . q{</p>] : '' %>
489<% $start %>
490<%
491    # drop in the hidden fields here
492    $OUT = join("\n", map { $$_{field} } grep { $$_{type} eq 'hidden' } @fields);
493%>} .
494q[
495<%
496    SECTION: while (my $section = shift @sections) {
497        $OUT .= qq[<table id="] . ($$section{id} || '_default') . qq[">\n];
498        $OUT .= qq[  <caption><h2 class="sectionhead">$$section{head}</h2></caption>] if $$section{head};
499        TABLE_LINE: for my $line (@{ $$section{lines} }) {
500            if ($$line[0] eq 'head') {
501                $OUT .= qq[  <tr><th class="subhead" colspan="2"><h3>$$line[1]</h3></th></tr>\n]
502            } elsif ($$line[0] eq 'note') {
503                $OUT .= qq[  <tr><td class="note" colspan="2">$$line[1]</td></tr>\n]
504            } elsif ($$line[0] eq 'field') {
505                local $_ = $field{$$line[1]};
506               
507                # skip hidden fields in the table
508                next TABLE_LINE if $$_{type} eq 'hidden';
509               
510                $OUT .= $$_{invalid} ? qq[  <tr class="invalid">] : qq[  <tr>];
511               
512                # special case single value checkboxes
513                if ($$_{type} eq 'checkbox' && @{ $$_{options} } == 1) {
514                    $OUT .= qq[<th></th>];
515                } else {
516                    $OUT .= '<th class="label">' . ($$_{required} ? qq[<strong class="required">$$_{label}:</strong>] : "$$_{label}:") . '</th>';
517                }
518               
519                # mark invalid fields
520                if ($$_{invalid}) {
521                    $OUT .= "<td>$$_{field} $$_{comment} ] . $msg_invalid . q[</td>";
522                } else {
523                    $OUT .= qq[<td>$$_{field} $$_{comment}</td>];
524                }
525               
526                $OUT .= qq[</tr>\n];
527               
528            } elsif ($$line[0] eq 'group') {
529                my @group_fields = map { $field{$_} } map { $$_{name} } @{ $$line[1]{group} };
530                $OUT .= (grep { $$_{invalid} } @group_fields) ? qq[  <tr class="invalid">\n] : qq[  <tr>\n];
531               
532                $OUT .= '    <th class="label">';
533                $OUT .= (grep { $$_{required} } @group_fields) ? qq[<strong class="required">$$line[1]{label}:</strong>] : "$$line[1]{label}:";
534                $OUT .= qq[</th>\n];
535               
536                $OUT .= qq[    <td><span class="fieldgroup">];
537                $OUT .= join(' ', map { qq[<small class="sublabel">$$_{label}</small> $$_{field} $$_{comment}] } @group_fields);
538                #TODO: allow comments on field groups
539                $OUT .= " ] . $msg_invalid . q[" if grep { $$_{invalid} } @group_fields;
540               
541                $OUT .= qq[    </span></td>\n];
542                $OUT .= qq[  </tr>\n];
543            }   
544        }
545        # close the table if there are sections remaining
546        # but leave the last one open for the submit button
547        $OUT .= qq[</table>\n] if @sections;
548    }
549%>
550  <tr><th></th><td style="padding-top: 1em;"><% $submit %></td></tr>
551</table>
552<% $end %>
553];
554}
555
556# usage: $self->_pre_template($css, $charset)
557sub _pre_template {
558    my $self = shift;
559    my $css = shift || $DEFAULT_CSS;
560    my $charset = shift || $DEFAULT_CHARSET;
561    my $msg_author = 'sprintf("' . quotemeta($self->{build_options}{messages}{text_author}) . '", $author)';
562    return 
563q[<html>
564<head>
565  <meta http-equiv="Content-Type" content="text/html; charset=] . $charset . q[" />
566  <title><% $title %><% $author ? ' - ' . ucfirst $author : '' %></title>
567  <style type="text/css">
568] . $css . q[  </style>
569  <% $jshead %>
570</head>
571<body>
572
573<h1><% $title %></h1>
574<% $author ? qq[<p id="author">] . ] . $msg_author . q{ . q[</p>] : '' %>
575};
576}
577
578sub _post_template {
579    my $self = shift;
580    my $msg_madewith = 'sprintf("' . quotemeta($self->{build_options}{messages}{text_madewith}) .
581        '", q[<a href="http://formbuilder.org/">CGI::FormBuilder</a>], CGI::FormBuilder->VERSION)';
582   
583    return qq[<hr />
584<div id="footer">
585  <p id="creator"><% $msg_madewith %></p>
586</div>
587</body>
588</html>
589];
590}
591
592# usage: $self->_template($css, $charset)
593sub _template {
594    my $self = shift;
595    return $self->_pre_template(@_) . $self->_form_template . $self->_post_template;
596}
597
598sub dump { 
599    eval "use YAML;";
600    unless ($@) {
601        print YAML::Dump(shift->{form_spec});
602    } else {
603        warn '[' . (caller(0))[3] . "] Can't dump form spec structure using YAML: $@";
604    }
605}
606
607
608# module return
6091;
610
611=head1 NAME
612
613Text::FormBuilder - Create CGI::FormBuilder objects from simple text descriptions
614
615=head1 SYNOPSIS
616
617    use Text::FormBuilder;
618   
619    my $parser = Text::FormBuilder->new;
620    $parser->parse($src_file);
621   
622    # returns a new CGI::FormBuilder object with
623    # the fields from the input form spec
624    my $form = $parser->form;
625   
626    # write a My::Form module to Form.pm
627    $parser->write_module('My::Form');
628
629=head1 REQUIRES
630
631L<Parse::RecDescent>,
632L<CGI::FormBuilder>,
633L<Text::Template>,
634L<Class::Base>
635
636=head1 DESCRIPTION
637
638This module is intended to extend the idea of making it easy to create
639web forms by allowing you to describe them with a simple langauge. These
640I<formspecs> are then passed through this module's parser and converted
641into L<CGI::FormBuilder> objects that you can easily use in your CGI
642scripts. In addition, this module can generate code for standalone modules
643which allow you to separate your form design from your script code.
644
645A simple formspec looks like this:
646
647    name//VALUE
648    email//EMAIL
649    langauge:select{English,Spanish,French,German}
650    moreinfo|Send me more information:checkbox
651    interests:checkbox{Perl,karate,bass guitar}
652
653This will produce a required C<name> test field, a required C<email> text
654field that must look like an email address, an optional select dropdown
655field C<langauge> with the choices English, Spanish, French, and German,
656an optional C<moreinfo> checkbox labeled ``Send me more information'', and
657finally a set of checkboxes named C<interests> with the choices Perl,
658karate, and bass guitar.
659
660=head1 METHODS
661
662=head2 new
663
664    my $parser = Text::FormBuilder->new;
665
666=head2 parse
667
668    # parse a file (regular scalar)
669    $parser->parse($filename);
670   
671    # or pass a scalar ref for parse a literal string
672    $parser->parse(\$string);
673   
674    # or an array ref to parse lines
675    $parser->parse(\@lines);
676
677Parse the file or string. Returns the parser object. This method,
678along with all of its C<parse_*> siblings, may be called as a class
679method to construct a new object.
680
681=head2 parse_file
682
683    $parser->parse_file($src_file);
684   
685    # or as a class method
686    my $parser = Text::FormBuilder->parse($src_file);
687
688=head2 parse_text
689
690    $parser->parse_text($src);
691
692Parse the given C<$src> text. Returns the parser object.
693
694=head2 parse_array
695
696    $parser->parse_array(@lines);
697
698Concatenates and parses C<@lines>. Returns the parser object.
699
700=head2 build
701
702    $parser->build(%options);
703
704Builds the CGI::FormBuilder object. Options directly used by C<build> are:
705
706=over
707
708=item C<form_only>
709
710Only uses the form portion of the template, and omits the surrounding html,
711title, author, and the standard footer. This does, however, include the
712description as specified with the C<!description> directive.
713
714=item C<css>, C<extra_css>
715
716These options allow you to tell Text::FormBuilder to use different
717CSS styles for the built in template. A value given a C<css> will
718replace the existing CSS, and a value given as C<extra_css> will be
719appended to the CSS. If both options are given, then the CSS that is
720used will be C<css> concatenated with C<extra_css>.
721
722If you want to use an external stylesheet, a quick way to get this is
723to set the C<css> parameter to import your file:
724
725    css => '@import(my_external_stylesheet.css);'
726
727=item C<messages>
728
729This works the same way as the C<messages> parameter to
730C<< CGI::FormBuilder->new >>; you can provide either a hashref of messages
731or a filename.
732
733The default messages used by Text::FormBuilder are:
734
735    text_author       Created by %s
736    text_madewith     Made with %s version %s
737    text_required     (Required fields are marked in <strong>bold</strong>.)
738    text_invalid      Missing or invalid value.
739
740Any messages you set here get passed on to CGI::FormBuilder, which means
741that you should be able to put all of your customization messages in one
742big file.
743
744=item C<charset>
745
746Sets the character encoding for the generated page. The default is ISO-8859-1.
747
748=back
749
750All other options given to C<build> are passed on verbatim to the
751L<CGI::FormBuilder> constructor. Any options given here override the
752defaults that this module uses.
753
754The C<form>, C<write>, and C<write_module> methods will all call
755C<build> with no options for you if you do not do so explicitly.
756This allows you to say things like this:
757
758    my $form = Text::FormBuilder->new->parse('formspec.txt')->form;
759
760However, if you need to specify options to C<build>, you must call it
761explictly after C<parse>.
762
763=head2 form
764
765    my $form = $parser->form;
766
767Returns the L<CGI::FormBuilder> object. Remember that you can modify
768this object directly, in order to (for example) dynamically populate
769dropdown lists or change input types at runtime.
770
771=head2 write
772
773    $parser->write($out_file);
774    # or just print to STDOUT
775    $parser->write;
776
777Calls C<render> on the FormBuilder form, and either writes the resulting
778HTML to a file, or to STDOUT if no filename is given.
779
780=head2 write_module
781
782    $parser->write_module($package, $use_tidy);
783
784Takes a package name, and writes out a new module that can be used by your
785CGI script to render the form. This way, you only need CGI::FormBuilder on
786your server, and you don't have to parse the form spec each time you want
787to display your form. The generated module has one function (not exported)
788called C<get_form>, that takes a CGI object as its only argument, and returns
789a CGI::FormBuilder object.
790
791First, you parse the formspec and write the module, which you can do as a one-liner:
792
793    $ perl -MText::FormBuilder -e"Text::FormBuilder->parse('formspec.txt')->write_module('My::Form')"
794
795And then, in your CGI script, use the new module:
796
797    #!/usr/bin/perl -w
798    use strict;
799   
800    use CGI;
801    use My::Form;
802   
803    my $q = CGI->new;
804    my $form = My::Form::get_form($q);
805   
806    # do the standard CGI::FormBuilder stuff
807    if ($form->submitted && $form->validate) {
808        # process results
809    } else {
810        print $q->header;
811        print $form->render;
812    }
813
814If you pass a true value as the second argument to C<write_module>, the parser
815will run L<Perl::Tidy> on the generated code before writing the module file.
816
817    # write tidier code
818    $parser->write_module('My::Form', 1);
819
820=head2 write_script
821
822    $parser->write_script($filename, $use_tidy);
823
824If you don't need the reuseability of a separate module, you can have
825Text::FormBuilder write the form object to a script for you, along with
826the simplest framework for using it, to which you can add your actual
827form processing code.
828
829The generated script looks like this:
830
831    #!/usr/bin/perl
832    use strict;
833    use warnings;
834   
835    use CGI;
836    use CGI::FormBuilder;
837   
838    my $q = CGI->new;
839   
840    my $form = CGI::FormBuilder->new(
841        params => $q,
842        # ... lots of other stuff to set up the form ...
843    );
844   
845    $form->field( name => 'month' );
846    $form->field( name => 'day' );
847   
848    unless ( $form->submitted && $form->validate ) {
849        print $form->render;
850    } else {
851        # do something with the entered data ...
852        # this is where your form processing code should go
853    }
854
855Like C<write_module>, you can optionally pass a true value as the second
856argument to have Perl::Tidy make the generated code look nicer.
857
858=head2 dump
859
860Uses L<YAML> to print out a human-readable representation of the parsed
861form spec.
862
863=head1 EXPORTS
864
865There is one exported function, C<create_form>, that is intended to ``do the
866right thing'' in simple cases.
867
868=head2 create_form
869
870    # get a CGI::FormBuilder object
871    my $form = create_form($source, $options, $destination);
872   
873    # or just write the form immediately
874    create_form($source, $options, $destination);
875
876C<$source> accepts any of the types of arguments that C<parse> does. C<$options>
877is a hashref of options that should be passed to C<build>. Finally, C<$destination>
878is a simple scalar that determines where and what type of output C<create_form>
879should generate.
880
881    /\.pm$/             ->write_module($destination)
882    /\.(cgi|pl)$/       ->write_script($destination)
883    everything else     ->write($destination)
884
885For anything more than simple, one-off cases, you are usually better off using the
886object-oriented interface, since that gives you more control over things.
887
888=head1 DEFAULTS
889
890These are the default settings that are passed to C<< CGI::FormBuilder->new >>:
891
892    method => 'GET'
893    javascript => 0
894    keepextras => 1
895
896Any of these can be overriden by the C<build> method:
897
898    # use POST instead
899    $parser->build(method => 'POST')->write;
900
901=head1 LANGUAGE
902
903    field_name[size]|descriptive label[hint]:type=default{option1[display string],...}//validate
904   
905    !title ...
906   
907    !author ...
908   
909    !description {
910        ...
911    }
912   
913    !pattern NAME /regular expression/
914   
915    !list NAME {
916        option1[display string],
917        option2[display string],
918        ...
919    }
920   
921    !list NAME &{ CODE }
922   
923    !group NAME {
924        field1
925        field2
926        ...
927    }
928   
929    !section id heading
930   
931    !head ...
932   
933    !note {
934        ...
935    }
936
937=head2 Directives
938
939=over
940
941=item C<!pattern>
942
943Defines a validation pattern.
944
945=item C<!list>
946
947Defines a list for use in a C<radio>, C<checkbox>, or C<select> field.
948
949=item C<!group>
950
951Define a named group of fields that are displayed all on one line. Use with
952the C<!field> directive.
953
954=item C<!field>
955
956Include a named instance of a group defined with C<!group>.
957
958=item C<!title>
959
960Title of the form.
961
962=item C<!author>
963
964Author of the form.
965
966=item C<!description>
967
968A brief description of the form. Suitable for special instructions on how to
969fill out the form.
970
971=item C<!section>
972
973Starts a new section. Each section has its own heading and id, which are
974written by default into spearate tables.
975
976=item C<!head>
977
978Inserts a heading between two fields. There can only be one heading between
979any two fields; the parser will warn you if you try to put two headings right
980next to each other.
981
982=item C<!note>
983
984A text note that can be inserted as a row in the form. This is useful for
985special instructions at specific points in a long form.
986
987=back
988
989=head2 Fields
990
991First, a note about multiword strings in the fields. Anywhere where it says
992that you may use a multiword string, this means that you can do one of two
993things. For strings that consist solely of alphanumeric characters (i.e.
994C<\w+>) and spaces, the string will be recognized as is:
995
996    field_1|A longer label
997
998If you want to include non-alphanumerics (e.g. punctuation), you must
999single-quote the string:
1000
1001    field_2|'Dept./Org.'
1002
1003To include a literal single-quote in a single-quoted string, escape it with
1004a backslash:
1005
1006    field_3|'\'Official\' title'
1007
1008Now, back to the beginning. Form fields are each described on a single line.
1009The simplest field is just a name (which cannot contain any whitespace):
1010
1011    color
1012
1013This yields a form with one text input field of the default size named `color'.
1014The generated label for this field would be ``Color''. To add a longer or more\
1015descriptive label, use:
1016
1017    color|Favorite color
1018
1019The descriptive label can be a multiword string, as described above. So if you
1020want punctuation in the label, you should single quote it:
1021
1022    color|'Fav. color'
1023
1024To use a different input type:
1025
1026    color|Favorite color:select{red,blue,green}
1027
1028Recognized input types are the same as those used by CGI::FormBuilder:
1029
1030    text        # the default
1031    textarea
1032    password
1033    file
1034    checkbox
1035    radio
1036    select
1037    hidden
1038    static
1039
1040To change the size of the input field, add a bracketed subscript after the
1041field name (but before the descriptive label):
1042
1043    # for a single line field, sets size="40"
1044    title[40]:text
1045   
1046    # for a multiline field, sets rows="4" and cols="30"
1047    description[4,30]:textarea
1048
1049To also set the C<maxlength> attribute for text fields, add a C<!> after
1050the size:
1051
1052    # ensure that all titles entered are 40 characters or less
1053    title[40!]:text
1054
1055This currently only works for single line text fields.
1056
1057To create a growable field, add a C<*> after the name (and size, if
1058given). Growable fields have a button that allows the user to add a
1059copy of the field input. Currently, this only works for C<text> and
1060C<file> fields. Growable fields also require JavaScript to function
1061correctly.
1062
1063    # you can have as many people as you like
1064    person*:text
1065
1066For the input types that can have options (C<select>, C<radio>, and
1067C<checkbox>), here's how you do it:
1068
1069    color|Favorite color:select{red,blue,green}
1070
1071Values are in a comma-separated list of single words or multiword strings
1072inside curly braces. Whitespace between values is irrelevant.
1073
1074To add more descriptive display text to a value in a list, add a square-bracketed
1075``subscript,'' as in:
1076
1077    ...:select{red[Scarlet],blue[Azure],green[Olive Drab]}
1078
1079If you have a list of options that is too long to fit comfortably on one line,
1080you should use the C<!list> directive:
1081
1082    !list MONTHS {
1083        1[January],
1084        2[February],
1085        3[March],
1086        # and so on...
1087    }
1088   
1089    month:select@MONTHS
1090
1091There is another form of the C<!list> directive: the dynamic list:
1092
1093    !list RANDOM &{ map { rand } (0..5) }
1094
1095The code inside the C<&{ ... }> is C<eval>ed by C<build>, and the results
1096are stuffed into the list. The C<eval>ed code can either return a simple
1097list, as the example does, or the fancier C<< ( { value1 => 'Description 1'},
1098{ value2 => 'Description 2}, ... ) >> form.
1099
1100I<B<NOTE:> This feature of the language may go away unless I find a compelling
1101reason for it in the next few versions. What I really wanted was lists that
1102were filled in at run-time (e.g. from a database), and that can be done easily
1103enough with the CGI::FormBuilder object directly.>
1104
1105If you want to have a single checkbox (e.g. for a field that says ``I want to
1106recieve more information''), you can just specify the type as checkbox without
1107supplying any options:
1108
1109    moreinfo|I want to recieve more information:checkbox
1110
1111In this case, the label ``I want to recieve more information'' will be
1112printed to the right of the checkbox.
1113
1114You can also supply a default value to the field. To get a default value of
1115C<green> for the color field:
1116
1117    color|Favorite color:select=green{red,blue,green}
1118
1119Default values can also be either single words or multiword strings.
1120
1121To validate a field, include a validation type at the end of the field line:
1122
1123    email|Email address//EMAIL
1124
1125Valid validation types include any of the builtin defaults from L<CGI::FormBuilder>,
1126or the name of a pattern that you define with the C<!pattern> directive elsewhere
1127in your form spec:
1128
1129    !pattern DAY /^([1-3][0-9])|[1-9]$/
1130   
1131    last_day//DAY
1132
1133If you just want a required value, use the builtin validation type C<VALUE>:
1134
1135    title//VALUE
1136
1137By default, adding a validation type to a field makes that field required. To
1138change this, add a C<?> to the end of the validation type:
1139
1140    contact//EMAIL?
1141
1142In this case, you would get a C<contact> field that was optional, but if it
1143were filled in, would have to validate as an C<EMAIL>.
1144
1145=head2 Field Groups
1146
1147You can define groups of fields using the C<!group> directive:
1148
1149    !group DATE {
1150        month:select@MONTHS//INT
1151        day[2]//INT
1152        year[4]//INT
1153    }
1154
1155You can then include instances of this group using the C<!field> directive:
1156
1157    !field %DATE birthday
1158
1159This will create a line in the form labeled ``Birthday'' which contains
1160a month dropdown, and day and year text entry fields. The actual input field
1161names are formed by concatenating the C<!field> name (e.g. C<birthday>) with
1162the name of the subfield defined in the group (e.g. C<month>, C<day>, C<year>).
1163Thus in this example, you would end up with the form fields C<birthday_month>,
1164C<birthday_day>, and C<birthday_year>.
1165
1166You can also use groups in normal field lines:
1167   
1168    birthday|Your birthday:DATE
1169
1170The only (currently) supported pieces of a fieldspec that may be used with a
1171group in this notation are name and label.
1172
1173=head2 Comments
1174
1175    # comment ...
1176
1177Any line beginning with a C<#> is considered a comment.
1178
1179=head1 TODO
1180
1181Document the commmand line tool
1182
1183Allow renaming of the submit button; allow renaming and inclusion of a
1184reset button
1185
1186Allow comments on group fields (rendered after the all the fields)
1187
1188Pieces that wouldn't make sense in a group field: size, row/col, options,
1189validate. These should cause C<build> to emit a warning before ignoring them.
1190
1191Make the generated modules into subclasses of CGI::FormBuilder
1192
1193Allow for custom wrappers around the C<form_template>
1194
1195Maybe use HTML::Template instead of Text::Template for the built in template
1196(since CGI::FormBuilder users may be more likely to already have HTML::Template)
1197
1198C<!include> directive to include external formspec files
1199
1200Better tests!
1201
1202=head1 BUGS
1203
1204Creating two $parsers in the same script causes the second one to get the data
1205from the first one.
1206
1207I'm sure there are more in there, I just haven't tripped over any new ones lately. :-)
1208
1209Suggestions on how to improve the (currently tiny) test suite would be appreciated.
1210
1211=head1 SEE ALSO
1212
1213L<http://textformbuilder.berlios.de>
1214
1215L<CGI::FormBuilder>, L<http://formbuilder.org>
1216
1217=head1 THANKS
1218
1219Thanks to eszpee for pointing out some bugs in the default value parsing,
1220as well as some suggestions for i18n/l10n and splitting up long forms into
1221sections.
1222
1223And of course, to Nathan Wiger, for giving use CGI::FormBuilder in the
1224first place. Thanks Nate!
1225
1226=head1 AUTHOR
1227
1228Peter Eichman C<< <peichman@cpan.org> >>
1229
1230=head1 COPYRIGHT AND LICENSE
1231
1232Copyright E<copy>2004-2005 by Peter Eichman.
1233
1234This program is free software; you can redistribute it and/or
1235modify it under the same terms as Perl itself.
1236
1237=cut
Note: See TracBrowser for help on using the repository browser.