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

Last change on this file since 62 was 61, checked in by peichman, 20 years ago

added growable field support (release pending release of FB 3.002)
upped FB required version to 3.001
added a belated thank you to Nate

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