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

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

separated basic parser using code out into a separate Class::ParseText::Base base class; updated docs, manifest, and changelog

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