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

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

fixed some typos in the docs
rearranged internals in the grammar, plus a minor bugfix for the skip pattern of inline lists (now allows padding space around the comma)
added new test scripts to the MANIFEST

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