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

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

added !fb directive to hold FB parameters as YAML serialized values
updated and expanded the documentation

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