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

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

added a !reset directive to name and include a reset button

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