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

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

added a $parser->{debug} attribute to Class::ParseText::Base that controls $::RD_TRACE
added as_script and as_module methods that return the Perl code without writing to a file
changed required field message and markers

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