NAME
Text::FormBuilder - Create CGI::FormBuilder objects from simple text descriptions
SYNOPSIS
use Text::FormBuilder;
my $parser = Text::FormBuilder->new;
$parser->parse($src_file);
# returns a new CGI::FormBuilder object with
# the fields from the input form spec
my $form = $parser->form;
# write a My::Form module to Form.pm
$parser->write_module('My::Form');
REQUIRES
Parse::RecDescent, CGI::FormBuilder, Text::Template, Class::Base
You will also need YAML, if you want to use the dump
method, or the !fb
directive in your formspec files.
DESCRIPTION
This module is intended to extend the idea of making it easy to create web forms by allowing you to describe them with a simple langauge. These formspecs are then passed through this module's parser and converted into CGI::FormBuilder objects that you can easily use in your CGI scripts. In addition, this module can generate code for standalone modules which allow you to separate your form design from your script code.
A simple formspec looks like this:
name//VALUE
email//EMAIL
language:select{English,Spanish,French,German}
moreinfo|Send me more information:checkbox
interests:checkbox{Perl,karate,bass guitar}
This will produce a required name
text field, a required email
text field that must look like an email address, an optional select dropdown field language
with the choices English, Spanish, French, and German, an optional moreinfo
checkbox labeled ``Send me more information'', and finally a set of checkboxes named interests
with the choices Perl, karate, and bass guitar.
METHODS
new
my $parser = Text::FormBuilder->new;
parse
# parse a file (regular scalar)
$parser->parse($filename);
# or pass a scalar ref to parse a literal string
$parser->parse(\$string);
# or an array ref to parse lines
$parser->parse(\@lines);
Parse the file or string. Returns the parser object. This method, along with all of its parse_*
siblings, may be called as a class method to construct a new object.
parse_file
$parser->parse_file($src_file);
# or as a class method
my $parser = Text::FormBuilder->parse($src_file);
parse_text
$parser->parse_text($src);
Parse the given $src
text. Returns the parser object.
parse_array
$parser->parse_array(@lines);
Concatenates and parses @lines
. Returns the parser object.
build
$parser->build(%options);
Builds the CGI::FormBuilder object. Options directly used by build
are:
form_only
-
Only uses the form portion of the template, and omits the surrounding html, title, author, and the standard footer. This does, however, include the description as specified with the
!description
directive. css
,extra_css
-
These options allow you to tell Text::FormBuilder to use different CSS styles for the built in template. A value given a
css
will replace the existing CSS, and a value given asextra_css
will be appended to the CSS. If both options are given, then the CSS that is used will becss
concatenated withextra_css
.If you want to use an external stylesheet, a quick way to get this is to set the
css
parameter to import your file:css => '@import(my_external_stylesheet.css);'
external_css
-
If you want to use multiple external stylesheets, or an external stylesheet in conjunction with the default styles, use the
external_css
option:# single external sheet external_css => 'my_styles.css' # mutliple sheets external_css => [ 'my_style_A.css', 'my_style_B.css', ]
messages
-
This works the same way as the
messages
parameter toCGI::FormBuilder->new
; you can provide either a hashref of messages or a filename.The default messages used by Text::FormBuilder are:
text_author Created by %s text_madewith Made with %s version %s text_required (Required fields are marked in <strong>bold</strong>.) text_invalid Missing or invalid value.
Any messages you set here get passed on to CGI::FormBuilder, which means that you should be able to put all of your customization messages in one big file.
charset
-
Sets the character encoding for the generated page. The default is ISO-8859-1.
All other options given to build
are passed on verbatim to the CGI::FormBuilder constructor. Any options given here override the defaults that this module uses.
The form
, write
, and write_module
methods will all call build
with no options for you if you do not do so explicitly. This allows you to say things like this:
my $form = Text::FormBuilder->new->parse('formspec.txt')->form;
However, if you need to specify options to build
, you must call it explictly after parse
.
form
my $form = $parser->form;
Returns the CGI::FormBuilder object. Remember that you can modify this object directly, in order to (for example) dynamically populate dropdown lists or change input types at runtime.
write
$parser->write($out_file);
# or just print to STDOUT
$parser->write;
Calls render
on the FormBuilder form, and either writes the resulting HTML to a file, or to STDOUT if no filename is given.
as_module
my $module_code = $parser->as_module($package, $use_tidy);
write_module
Note: The code output from the write_*
methods may be in flux for the next few versions, as I coordinate with the FormBuilder project.
$parser->write_module($package, $use_tidy);
Takes a package name, and writes out a new module that can be used by your CGI script to render the form. This way, you only need CGI::FormBuilder on your server, and you don't have to parse the form spec each time you want to display your form. The generated module is a subclass of CGI::FormBuilder, that will pass along any constructor arguments to FormBuilder, and set up the fields for you.
First, you parse the formspec and write the module, which you can do as a one-liner:
$ perl -MText::FormBuilder -e"Text::FormBuilder->parse('formspec.txt')->write_module('My::Form')"
And then, in your CGI script, use the new module:
#!/usr/bin/perl -w
use strict;
use CGI;
use My::Form;
my $q = CGI->new;
my $form = My::Form->new;
# do the standard CGI::FormBuilder stuff
if ($form->submitted && $form->validate) {
# process results
} else {
print $q->header;
print $form->render;
}
If you pass a true value as the second argument to write_module
, the parser will run Perl::Tidy on the generated code before writing the module file.
# write tidier code
$parser->write_module('My::Form', 1);
If you set $use_tidy
to a string beginning with `-' write_module
will interpret $use_tidy
as the formatting option switches to pass to Perl::Tidy.
as_script
my $script_code = $parser->as_script($use_tidy);
write_script
$parser->write_script($filename, $use_tidy);
If you don't need the reuseability of a separate module, you can have Text::FormBuilder write the form object to a script for you, along with the simplest framework for using it, to which you can add your actual form processing code.
The generated script looks like this:
#!/usr/bin/perl
use strict;
use warnings;
use CGI::FormBuilder;
my $form = CGI::FormBuilder->new(
# lots of stuff here...
);
# ...and your field setup subs are here
$form->field(name => '...');
unless ($form->submitted && $form->validate) {
print $form->render;
} else {
# do something with the entered data
}
Like write_module
, you can optionally pass a true value as the second argument to have Perl::Tidy make the generated code look nicer.
dump
Uses YAML to print out a human-readable representation of the parsed form spec.
EXPORTS
There is one exported function, create_form
, that is intended to ``do the right thing'' in simple cases.
create_form
# get a CGI::FormBuilder object
my $form = create_form($source, $options, $destination);
# or just write the form immediately
create_form($source, $options, $destination);
$source
accepts any of the types of arguments that parse
does. $options
is a hashref of options that should be passed to build
. Finally, $destination
is a simple scalar that determines where and what type of output create_form
should generate.
/\.pm$/ ->write_module($destination)
/\.(cgi|pl)$/ ->write_script($destination)
everything else ->write($destination)
For anything more than simple, one-off cases, you are usually better off using the object-oriented interface, since that gives you more control over things.
DEFAULTS
These are the default settings that are passed to CGI::FormBuilder->new
:
method => 'GET'
keepextras => 1
Any of these can be overriden by the build
method:
# use POST instead
$parser->build(method => 'POST')->write;
LANGUAGE
# name field_size growable label hint type other default option_list validate
field_name[size]|descriptive label[hint]:type=default{option1[display string],...}//validate
!title ...
!author ...
!description {
...
}
!pattern NAME /regular expression/
!list NAME {
option1[display string],
option2[display string],
...
}
!group NAME {
field1
field2
...
}
!section id heading
!head ...
!note {
...
}
!submit label, label 2, ...
!reset label
Directives
All directives start with a !
followed by a keyword. There are two types of directives:
- Line directives
-
Line directives occur all on one line, and require no special punctuation. Examples of line directives are
!title
and!section
. - Block directives
-
Block directives consist of a directive keyword followed by a curly-brace delimited block. Examples of these are
!group
and!description
. Some of these directives have their own internal structure; see the list of directives below for an explanation.
And here is the complete list of directives
!pattern
-
Defines a validation pattern.
!list
-
Defines a list for use in a
radio
,checkbox
, orselect
field. !group
-
Define a named group of fields that are displayed all on one line. Use with the
!field
directive. !field
-
DEPRECATED Include a named instance of a group defined with
!group
. See Field Groups for an explanation of the new way to include groups. !title
-
Line directive containing the title of the form.
-
Line directive naming the author of the form.
!description
-
A block directive containing a brief description of the form. Appears at the top of the form. Suitable for special instructions on how to fill out the form. All of the text within the block is folded into a single paragraph. If you add a second !description, it will override the first.
!section id Your section text goes here
-
A line directive that starts a new section. Each section has its own heading and id, which by default are rendered into separate tables.
!head
-
A line directive that inserts a heading between two fields. There can only be one heading between any two fields; the parser will warn you if you try to put two headings right next to each other.
!note
-
A block directive containing a text note that can be inserted as a row in the form. This is useful for special instructions at specific points in a long form. Like
!description
, the text content is folded into a single paragraph. !submit
-
A line directive with one or more submit button labels in a comma-separated list. Each label is a string. Multiple instances of this directive may be used; later lists are simply appended to the earlier lists. All the submit buttons are rendered together at the bottom of the form. See CGI::FormBuilder for an explanation of how the multiple submit buttons work together in a form.
To disable the display of any submit button, use
!submit 0
!reset
-
Line directive giving a label for the a reset button at the end of the form. No reset button will be rendered unless you use this directive.
!fb
-
The
!fb
block directive allows you to include any parameters you want passed directly to the CGI::FormBuilder constructor. The block should be a hashref of parameters serialized as YAML. Be sure to place the closing of the block on its own line, flush to the left edge, and to watch your indentation. Multiple!fb
blocks are concatenated, and the result is interpeted as one big chunk of YAML code.!fb{ method: POST action: '/custom/action' javascript: 0 }
Strings
Anywhere that it says that you may use a multiword string, this means you can do one of two things. For strings that consist solely of alphanumeric characters (i.e. \w+
) and spaces, the string will be recognized as is:
field_1|A longer label
If you want to include non-alphanumerics (e.g. punctuation), you must single-quote the string:
field_2|'Dept./Org.'
To include a literal single-quote in a single-quoted string, escape it with a backslash:
field_3|'\'Official\' title'
Quoted strings are also how you can set the label for a field to be blank:
unlabeled_field|''
Fields
Form fields are each described on a single line. The simplest field is just a name (which cannot contain any whitespace):
color
This yields a form with one text input field of the default size named `color'. The generated label for this field would be ``Color''. To add a longer or more descriptive label, use:
color|Favorite color
The descriptive label can be a multiword string, as described above. So if you want punctuation in the label, you should single quote it:
color|'Fav. color'
To add a descriptive hint that should appear to the right of the form field, put the hint in square brackets after the label, but before the field type:
# hint for a field without a label
color[select from a list]
# hint together with a label
color|Favorite color[select from this list]
To use a different input type:
color|Favorite color:select{red,blue,green}
Recognized input types are the same as those used by CGI::FormBuilder:
text # the default
textarea
password
file
checkbox
radio
select
hidden
static
For multi-select fields, append a *
to the field type:
colors:select*
To change the size of the input field, add a bracketed subscript after the field name (but before the descriptive label):
# for a single line field, sets size="40"
title[40]:text
# for a multiline field, sets rows="4" and cols="30"
description[4,30]:textarea
To also set the maxlength
attribute for text fields, add a !
after the size:
# ensure that all titles entered are 40 characters or less
title[40!]:text
This currently only works for single line text fields.
To create a growable field, add a *
after the name (and size, if given). Growable fields have a button that allows the user to add a copy of the field input. Currently, this only works for text
and file
fields, and you must have CGI::FormBuilder 3.02 or higher. Growable fields also require JavaScript to function correctly.
# you can have as many people as you like
person*:text
To set a limit to the maximum number of inputs a field can grow to, add a number after the *
:
# allow up to 5 musicians
musician*5:text
To create a radio
or select
field that includes an "other" option, append the string +other
to the field type:
position:select+other
Or, to let FormBuilder decide whether to use radio buttons or a dropdown:
position+other
Like growable fields, 'other' fields require FormBuilder 3.02 or higher.
For the input types that can have options (select
, radio
, and checkbox
), here's how you do it:
color|Favorite color:select{red,blue,green}
Values are in a comma-separated list of single words or multiword strings inside curly braces. Whitespace between values is irrelevant.
To add more descriptive display text to a value in a list, add a square-bracketed ``subscript,'' as in:
...:select{red[Scarlet],blue[Azure],green[Olive Drab]}
If you have a list of options that is too long to fit comfortably on one line, you should use the !list
directive:
!list MONTHS {
1[January],
2[February],
3[March],
# and so on...
}
month:select@MONTHS
If you want to have a single checkbox (e.g. for a field that says ``I want to recieve more information''), you can just specify the type as checkbox without supplying any options:
moreinfo|I want to recieve more information:checkbox
In this case, the label ``I want to recieve more information'' will be printed to the right of the checkbox.
You can also supply a default value to the field. To get a default value of green
for the color field:
color|Favorite color:select=green{red,blue,green}
Default values can also be either single words or multiword strings.
To validate a field, include a validation type at the end of the field line:
email|Email address//EMAIL
Valid validation types include any of the builtin defaults from CGI::FormBuilder, or the name of a pattern that you define with the !pattern
directive elsewhere in your form spec:
!pattern DAY /^([1-3][0-9])|[1-9]$/
last_day//DAY
If you just want a required value, use the builtin validation type VALUE
:
title//VALUE
By default, adding a validation type to a field makes that field required. To change this, add a ?
to the end of the validation type:
contact//EMAIL?
In this case, you would get a contact
field that was optional, but if it were filled in, would have to validate as an EMAIL
.
Field Groups
You can define groups of fields using the !group
directive:
!group DATE {
month:select@MONTHS//INT
day[2]//INT
year[4]//INT
}
You can also use groups in normal field lines:
birthday|Your birthday:DATE
This will create a line in the form labeled ``Your birthday'' which contains a month dropdown, and day and year text entry fields. The actual input field names are formed by concatenating the !field
name (e.g. birthday
) with the name of the subfield defined in the group (e.g. month
, day
, year
). Thus in this example, you would end up with the form fields birthday_month
, birthday_day
, and birthday_year
.
The only (currently) supported pieces of a fieldspec that may be used with a group in this notation are name, label, and hint.
The old method of using field groups was with the !field
directive:
!field %DATE birthday
This format is now deprecated, and although it still works, the parser will warn you if you use it.
Comments
# comment ...
Any line beginning with a #
is considered a comment. Comments can also appear after any field line. They cannot appear between items in a !list
, or on the same line as any of the directives.
TODO
Documentation/Tests
Document use of the parser as a standalone module
Make sure that the docs match the generated code.
Better tests!
Language/Parser
Pieces that wouldn't make sense in a group field: size, row/col, options, validate. These should cause build
to emit a warning before ignoring them.
!include
directive to include external formspec files
Better recovery from parse errors
Code generation/Templates
Revise the generated form constructing code to use the fieldopts
option to FB->new
; will require FB 3.02 to run.
Better integration with CGI::FormBuilder's templating system; rely on the FB messages instead of trying to make our own.
Allow for custom wrappers around the form_template
Maybe use HTML::Template instead of Text::Template for the built in template (since CGI::FormBuilder users may be more likely to already have HTML::Template)
BUGS
Placing fields
in a !fb
directive does not behave as expected (i.e. they don't show up). This is not a big issue, since you should be defining your fields in the body of the formspec file, but for completeness' sake I would like to get this figured out.
I'm sure there are more in there, I just haven't tripped over any new ones lately. :-)
Suggestions on how to improve the (currently tiny) test suite would be appreciated.
SEE ALSO
CGI::FormBuilder, http://formbuilder.org
THANKS
Thanks to eszpee for pointing out some bugs in the default value parsing, as well as some suggestions for i18n/l10n and splitting up long forms into sections.
To Ron Pero for a documentation patch, and for letting me know that software I wrote several years ago is still of use to people.
And of course, to Nathan Wiger, for giving us CGI::FormBuilder in the first place. Thanks Nate!
AUTHOR
Peter Eichman <peichman@cpan.org>
COPYRIGHT AND LICENSE
Copyright ©2004-2005 by Peter Eichman.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.