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

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

failure to load Perl::Tidy is no longer fatal
create_form tries to emit tidy code
expanded documentation for bin/fb.pl

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