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

Last change on this file since 43 was 42, checked in by peter, 20 years ago

added a create_form exported method to "do the right thing" in simple cases
added parse_array method to parse an array of lines making up a formspec
some internal cleanup of the lines structure

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