=head1 NAME

HTML::FormHandler::Manual::Intro - basic usage of FormHandler

=head1 SUMMARY

HTML::FormHandler is a form handling class primarily useful for getting HMTL form
data into the database. It provides attributes on fields that can be used
for creating a set of widgets and highly automatic templates, but does
not actually create the HTML code themselves, although there is an example of a
rendering role.

The DBIC & CDBI  models will save form fields automatically to the database, will
retrieve selection lists from the database (with type => 'Select' and a 
fieldname containing a single relationship, or type => 'Multiple' and a
many_to_many relationship), and will save the selected values (one value for 
'Select', multiple values in a mapping table for a 'Multiple' field). 

The 'form' is a Perl subclass of L<HTML::FormHandler> for non-database forms,
or a subclass of a model class for database forms, and in it you define
your fields (with many possible attributes), and initialization
and validation routines. Because it's a Perl class, you have a lot of 
flexibility.

You can use transformations. Moose type constraints, and coercions, listed 
in the field's 'apply' attribute, to validate or inflate the fields 
(see L<HTML::FormHandler::Field/apply>).  You can define your own 
L<HTML::FormHandler::Field> classes to create your own field types, and 
perform specialized validation. And you can subclass the methods in 
L<HTML::FormHandler::Model::DBIC> and L<HTML::FormHandler>.

The L<HTML::FormHandler> package includes a working 
example using a SQLite database and a number of forms in the test directory.
You can execute the sample from a downloaded distribution package with:

   perl -Ilib t/script/bookdb_server.pl

=head1 Basics

To use HTML::FormHandler, you need to create a form class, call the
form class from a controller, and choose a method of displaying
the form in an HTML page. 

Create a Form, subclassed from HTML::FormHandler::Model::DBIC

    package MyApp:Form::User;
    use HTML::FormHandler::Moose;
    extends 'HTML::FormHandler::Model::DBIC';

    # Associate this form with a DBIx::Class result class
    # Or 'item_class' can be passed in on 'new', or you
    # you can always pass in a row object
    has '+item_class' => ( default => 'User' );

    # Define the fields that this form will operate on
    # Field names are usually column, accessor, or relationship names in your
    # DBIx::Class result class. You can also have fields that don't exist
    # in your result class.

    has_field 'name'    => ( type => 'Text', label => 'Username, required => 1,
       required_message => 'You must enter a username', unique => 1,
       unique_message => 'That username is already taken' );
    # the css_class, title, and widget attributes are for use in templates
    has_field 'age'     => ( type => 'PosInteger', required => 1, css_class => 'box',
       title => 'User age in years', widget => 'age_text', range_start => 18 ); 
    has_field 'sex'     => ( type => 'Select', label => 'Gender', required => 1 );
    # a customized field class
    has_field 'birthdate => ( type => '+MyApp::Field::Date' );
    has_field 'hobbies' => ( type => 'Multiple', size => 5 );
    has_field 'address' => ( type => 'Text' );
    has_field 'city'    => ( type => 'Text' );
    has_field 'state'   => ( type => 'Select' );

    has '+dependency' => ( default => sub {
            [
                ['address', 'city', 'state'],
            ],
        }
    );

In a template, for an input field:

   <p>
   [% f = form.field('address') %]
   <label class="label" for="[% f.name %]">[% f.label %]:</label>
   <input type="text" name="[% f.name %]" id="[% f.name %]" value="[% f.fif %]">
   </p>

The value can come from the hash returned by $form->fif, from the 'fif'
attribute of the field, or can be supplied by FillInForm.
Plain HTML works fine for a simple input field if you use FillInForm to
supply the value.

For a select list, provide a relationship name as the field name, or provide
an options_<field_name> subroutine in the form. FillInForm alone is not
enough for select fields, since you need to access the field 'options'.
(field attributes: sort_order, label_column, active_column). TT example:

   <p>
   [% f = form.field('sex') %]
   <label class="label" for="[% f.name %]">[% f.label %]</label>
   <select name="[% f.name %]">
     [% FOR option IN f.options %]
       <option value="[% option.value %]" 
       [% IF option.value == f.fif %]selected="selected"[% END %]>
       [% option.label | html %]</option>
     [% END %] 
   </select>
   </p>

A multiple select list where 'hobbies' is a 'many_to_many' pseudo-relationship. 
(field attributes: sort_column, label_column, active_column).

   <p>
   [% f = form.field('hobbies') %]
   <label class="label" for="[% f.name %]">[% f.label || f.name %]</label>
   <select name="[% f.name %]" multiple="multiple" size="[% f.size %]">
     [% FOR option IN f.options %]
       <option value="[% option.value %]" [% FOREACH selval IN f.fif %][% IF selval == option.value %]selected="selected"[% END %][% END %]>[% option.label | html %]</option>
     [% END %] 
   </select>
   </p>

 
In a Catalyst controller:

    package MyApp::Controller::User;
    BEGIN {
       use Moose;
       extends 'Catalyst::Controller';
    }
    has 'form' => ( isa => 'MyApp::Form::User', is => 'rw',
        lazy => 1, default => sub { MyApp::Form::User->new } );

    # Create or edit
    sub edit : Local {
        my ( $self, $c, $user_id ) = @_;

        $c->stash( template => 'user/edit.tt' ); 
        return unless $self->form->process( item_id => $user_id,
           schema => $c->model('DB')->schema );

        # Form validated.
        $c->stash( user => $form->item );
        $c->res->redirect($c->uri_for('profile'));
    }
    ...

With the DBIC model the schema is set from the 'item' (row object)
passed in, or from the primary key ('item_id') and schema. 

The example above uses persistent forms in a Moose attribute. The
'process' method will clear out non-persistent form values and
update the information from the database row (if given). If you
modify form attributes that are not automatically cleared, you must
set those attributes on every request or clear them yourself.

You can also create a new form on each request with new:

   my $form = BookDB::Form::Book->new( item => $book );
   return unless $form->update( params => $c->req->parameters );

Here you use 'update', because 'process' is a convenience function
that calls 'clear' and 'update', and you don't want to clear. There is 
often no need to check the 'validated' flag, since that is the 
return value from the 'process', 'update', and 'validate' methods, 
although it can be useful if you are doing anything between the 
update/process/validate call and displaying the form, such as setting
the fillinform stash key.

Form processing is a two-pass operation. The first time through
the parameters will be an empty hashref, since the form has not been
submitted yet. FormHandler will load values from the database object
(item_id/schema or item) or from an 'init_object', and return false
because the form has not validated yet. At this point the 'return'
(in Catalyst) will cause the renderview processing to take place and 
the form will be displayed with initialized values (from a template 
using the 'fif' values or from L<HTML::FillInForm>) to allow user input.

When the form is submitted, the action in the HTML form's 'action'
value will be called (the same one that just displayed the form
usually), and the second pass of calling the FormHandler 
update/process/validate method will occur.

This time there WILL be values in the parameters, and FormHandler
will call the validation routines. If the validation succeeds, FormHandler
will return a 'true' value, and execution will fall through to after
the "return unless ...." line. At this point you will either redirect
to some other page, or in some cases redisplay the form with a message
that saving succeeded. If the validation fails, the 'return' will cause
the form to be displayed again.

The values to be used to fill in your form are automatically created
by FormHandler, and are available in the field's 'fif' attribute:

   $field->fif

or in the form's fif hash, which will contain the fill-in-form values
for all the form's fields:

   $form->fif

If you want to use L<HTML::FillInForm> to fill in values instead of the 
doing it in directly in a template using either the field or the form 'fif'
methods, you will need to set that up yourself in an 'end' routine
or a finalize method. One option would be to set the 'fif' hash in a
stash variable:

    $self->form->process( ... );
    $c->stash( fillinform => $self->form->fif );
    return unless $form->validated;
 
and then check for the stash variable in your end routine and call
FillInForm:

   sub end : Private
   {
      my ( $self, $c ) = @_;
      $c->forward('render') unless $c->res->output;
      if ($c->stash->{fillinform})
      {
         $c->response->body(
            HTML::FillInForm->new->fill(
               scalarref => \$c->response->{body},
               fdat      => $c->stash->{fillinform},
            )
         );
      }
   }
   sub render : ActionClass('RenderView') { }

=head1 Non-database forms

The base class for a  non-database form is HTML::FormHandler instead
of a model class.
You do not initialize a non-database form with an item or item_id,
although you can use an init_object for the initial values. You 
use the 'validate' method instead of 'update' or 'process'.  

After validation, you can get a hashref of values back from 
the 'values' method. 

   my $form = MyApp::Form::Book->new( init_object => { ... } );
   $form->validate( $params );
   return unless $form->validated;
   my $result = $form->values;


=head1 Form Models 

For a database form, use a model base class that interfaces with the 
database, such as L<HTML::FormHandler::Model::DBIC> or 
L<HTML::FormHandler::Model::CDBI>.

When using a database model, form field values for the row are retrieved from
the database using the field 'accessor' attributes (defaults to fieldname) 
as database class accessors. 
FormHandler will use relationships to populate single and multiple 
selection lists, and validate input. It doesn't yet do anything with other
relationships (though there are plans), but it will return a hash of the 
relationship created with HashRefInflator.

You can pass in either the primary key and or a row object to the form. If a 
primary key (item_id) is passed in, you must also provide the schema. 
The model will use the item_class (DBIC source name) to fetch the row from the 
database. If you pass in a row object (item), the schema, source_class, and 
item_id will be set from the row. 

The C<< $form->process >> or C<< $form->update >> methods will validate 
the parameters and then update or create the database row object.  

=head1 Field names

The standard way to use FormHandler is with field names that match your
database accessors. If you want to prepend the HTML field names with a
name plus dot, you can set the form 'name' and use the 'html_prefix'
flag. "$name." will be stripped from the beginning of the HTML fields
in 'munge_params' before the parameters are set, and will be added in
'fif'. The field's 'html_name' convenience attribute will return this
name for use in templates.

If you want the FormHandler field name to be different than the
database accessor, set 'accessor' on your fields. (It defaults to the field
name). You could then use any name that you want for your field. 

There are a number of name related field attributes. The 'name' is
the name used to identify this particular field in this fields array.
The 'full_name' is includes the names of all parents of this field,
like 'address.street.streetname'. The 'html_name' is the same as the
'full_name' unless you have set the 'html_prefix' flag, in which case
it includes the form name: 'myform.address.street.streetname'.

To retrieve a field by name, you can use either the full_name or a
chain: C<< $form->field('address')->field('street')->field('streetname') >>
or: C<< $form->field('address.street.streetname') >>.

You can process multiple FormHandler forms at the same time (using
the same HTML form) with multiple form objects and multiple process/update
calls. You would have to ensure that there are no duplicate field names by
using one of the methods mentioned above.

=head1 has_field

This is not actually a Moose attribute. It is sugar to allow the
declarative specification of fields. It will not create accessors for the
fields. The 'type' is not a Moose type, but an L<HTML::FormHandler::Field>
class name. To use this sugar, you must do 

   use HTML::FormHandler::Moose;

instead of C< use Moose; >. (Moose best practice advises putting 
C< no HTML::FormHandler::Moose; > or C<< use namespace:clean -except => 'meta' >> at
the end of the package to keep the namespace clean of imported methods.) 

To declare fields use the syntax:

   has_field 'title' => ( type => 'Text', required => 1 );
   has_field 'authors' => ( type => 'Select' );

instead of:

   sub field_list {
      return {
         fields => {
            title => {
               type => 'Text',
               required => 1,
            },
            authors => 'Select',
         }
      }            
   }
         
Fields specified in a field_list will overwrite fields specified with 'has_field'.
After processing, fields live in the 'fields' array, and can be accessed with the
field method: C<< $form->field('title') >>. 

Forms with 'has_field' field declarations may be subclassed. Or use 
L<HTML::FormHandler::Moose::Role> to create roles with the 'has_field' syntax:

   package Form::Role::Address;

   use HTML::FormHandler::Moose::Role; 

   has_field 'street' => ( type => 'Text', size => '50' );
   has_field 'city' => ( type = 'Text', size => 24 );
   has_field 'state' => ( type => 'Select );
   has_field 'zip' => ( type => '+Zip', required => 1 );

   no HTML::FormHandler::Moose::Role;
   1;

You can use roles to define fields and validations and include them in form
classes using 'with':

   package Form::Member;
   use HTML::FormHandler::Moose;
   with ''Form::Role::Person';
   with 'Form::Role::Address';
   extends 'HTML::FormHandler::Model::DBIC';

   has_field 'user_name' => ( type => 'Text', required => 1 );

   no HTML::FormHandler::Moose;
   1;   
   
If you prefix the field name with a '+' the attributes in this definition
will modify existing attributes or be added to an existing field definition: 

    has_field 'user' => ( type => 'Text', ...., required => 1 );
    ....
    has_field '+user' => ( required => 0 );


=head1 The form field_list

Returns a hashref of field definitions.

The possible keys in the field_list hashref are:

   required
   optional
   fields
   auto_required
   auto_optional

The field_list is one way to define the fields in your form (though
you can also add fields individually). 

You can categorize your fields as required and optional with two separate 
hashrefs:

    my $field_list => {
        required => {
            field_one => 'Text', 
        },
        optional => {
            field_two => 'Text', 
        },
    };

Or you can use one hashref and indicate 'required' as yet another field
attribute:

    my $field_list => {
        fields => [
            field_one => {
               type => 'Text',
               required => 1
            },
            field_two => 'Text,
         ],
     }; 

(Making the value of the "fields" key an array allows FormHandler to
create the "order" of the fields in the order in which you define them.)
The only required key is "type", which determines the field class.  
All other keys are attributes of L<HTML::FormHandler::Field> or its subclasses.

An example of a select field:

    my $field_list = {
        fields => {
            favorite_color => {
                type            => 'Select',
                label_column    => 'color_name',
                active_column   => 'is_active',
            },
        },
    };

The definition above is the equivalent of the following code:

    my $field = HTML::FormHandler::Field::Select->new(
       name => 'favorite_color', 
       required => 1,
       label_column => 'color_name',
       active_column => 'is_active' );
    $form->add_field( $field );


For the "auto" field_list keys, provide a list of field names.  
The field types will be determined by calling 'guess_field_type' 
in the model.  

    auto_required => ['name', 'age', 'sex', 'birthdate'],
    auto_optional => ['hobbies', 'address', 'city', 'state'],

The 'guess_field_type' method could be customized to provide more
sophisticated determination of types. For the DBIC model, the schema
class must be available when the auto fields are constructed.

Note that field_lists do not have the same flexibility in subclassing
that 'has_field' fields do.

=head1 Fields

A form's fields are created from the 'has_field' and 'field_list'
definitions.
FormHandler processes the field lists and creates an array of 
L<HTML::FormHandler::Field> objects. The "type" of a field 
determines which field class to use. The field class determines which
attributes are valid for a particular field. A number of field classes are 
provided by FormHandler. You can customize the validation in your form on a 
per field basis, but validation that will be used for more than one field 
might be more easily handled in a custom field class. 

Fields can also be added dynamically with the 'add_field' method.

In the template the fields are accessed with C< form.field('name') >.
Field errors are in C<< $field->errors >>. 

The fields are assumed to be in the HTML::FormHandler::Field name
space.  If you want to explicitly list the field's package, prefix it
with a plus sign. The field name space for "+" prefixed fields can
be set with the form's "field_name_space" attribute:
 
    has '+field_name_space' => ( default => 'MyApp::Form::Field' );

    has_field 'name' => ( type => 'Text' ); # HTML::FormHandler::Field::Text
    has_field 'foo'  => ( type => +Foo' );  # MyApp::Form::Field::Foo

The most basic type is "Text", which takes a single scalar value. (If the
type of a field is not specified, it will be set to 'Text'.)  A "Select"
class is similar, but its value must be a valid choice from a list of options.
A "Multiple" type is like "Select" but it allows selecting more than one value
at a time.

Each field has a "value" method, which is the field's internal value.  This is
the value your database object would have (e.g. scalar, boolean 0 or 1,
DateTime object).  

When data is passed in to validate the form, it is trimmed of leading and trailing
whitespace by the base field class and placed in the field's "input" attribute.  
Each field has a validate method that validates the input data and then moves 
it to the internal representation in the "value" attribute.  Depending on the model, 
it's this internal value that is stored or used by your application.

By default, the validation is simply to copy the data from the "input" to the "value"
field attribute, but you might have a field that must be converted from a text
representation to an object (e.g. month, day, year to DateTime). These sorts of
conversions can also be done by Moose type constraints or apply transformations.

=head2 Compound fields

A compound field is a field that has sub-fields. Compound fields can be
created in two ways: 1) using a field class, 2) by declaration.

To create a compound field class, you must extend 
L<HTML::FormHandler::Field::Compound> and use L<HTML::FormHandler::Moose> to
allow declaring fields:

  package MyApp::Field::Duration;

  use HTML::FormHandler::Moose;
  extends 'HTML::FormHandler::Field::Compound';

  has_field 'month' => (type => 'Integer');
  has_field 'day' => ( type => 'Integer' );
  has_field 'minutes' => ( type => 'Integer' );

Then in the form:

  has_field 'my_duration' => ( type => '+Duration' );

To create a compound field by declaration, declare the containing
compound field and subfields, prefixing the subfield names
with the name of the containing compound field plus a dot:

   package MyApp::Form;

   use HTML::FormHandler::Moose;
   extends 'HTML::FormHandler';

   has_field 'duration' => ( type => 'Compound' );
   has_field 'duration.month' => ( type => 'Integer' );
   has_field 'duration.day' => ( type => 'Integer' );
   has_field 'duration.year' => ( type => 'Integer' );

In an HTML form the name of the field must be the complete name
with dots. The 'html_name' field attribute can be used to get
this name:

   [% field.html_name %]

=head2 Filters, transformations, and constraints

L<HTML::FormHandler> has a flexible system of of filters and constraints. You can
use Moose types to constrain the allowable values in a field and use coercions to
inflate the HTML field input, such as for a DateTime. You can also create
non-Moose transformations and constraints. See the 'apply' attribute
in L<HTML::FormHandler::Field>.

   has_field 'some_field' => ( apply => [ 'MooseType', 
       { transform => sub {...}, message => 'xxxx' },
       { check => sub { ... }, message => 'xxxx' } ] );

The actions in the 'apply' array will be performed in the order they are
specified, allowing fine-grained control over inflation and validation.

You can also create a simple subroutine in your form class to perform validation.
The default name of this subroutine is 'validate_<fieldname>', but the name can
also be set in the field with the 'set_validate' attribute. 

If you need to access form attributes such as the schema, the 'set_validate'
subroutine may be preferable, but most validations can be performed using
either method.

=head2 Creating custom fields

Subclass a custom field from L<HTML::FormHandler::Field>, or one of the
existing subclasses. Almost everything that is done in a custom field
class can also be done declaratively. The advantage of a field class
is that it can simplify declaration of often-repeated sets of attributes. 

The simplest subclasses contain only a 'validate' routine or an 'apply' attribute,
which is called by the base Field class from 'process'. Look at 
L<HTML::FormHandler::Field::Email>, for example.

If the field's value will be an object instead of a simple scalar, such
as a DateTime, then you will also need a transformation method to reformat
the object into a form suitable for an HTML form field.

Some custom field might only require setting certain attributes to
defaults, such as the L<HTML::FormHandler::Field::Hour> field, which
set 'range_start' to 0 and 'range_end' to 23. A 'select' field might
override the 'build_options' builder for the 'options' array, like
L<HTML::FormHandler::Field::IntRange>. A field may add additional 
attributes, such as 'label_format' in L<HTML::FormHandler::Field::IntRange>,
or set the 'required_message'.

An alternative to new field classes for many field validations might
be roles with collections of validations.

=head1 Common form attributes

The 'dependency' field_list key is an array of arrays of field names.  
During validation, if any field in a given group 
contains the pattern /\S/ (non-blank), the 'required' flag
is set for all of the fields in the group.

   has '+dependency' => ( default => sub {
            [
               ['address', 'city', 'state', 'zip'],
               ['cc_no', 'cc_expires'],
            ],
        },
    );

The 'item_class':

   has '+item_class' => ( default => 'Book' );

The form name:

   has '+name' => ( default => 'book_form' );

The field name space for use with '+' prefixed fields:

   has '+field_name_space' => ( default => 'MyApp::Form::Field' );
   ...
   has_field 'subname' => ( type => '+SubName' );
   
An 'init_object' for filling in the form with default values instead of
the database object. (To set individual field values use "init_value_$fieldname".)

   has '+init_object' => ( default => sub { 
         {  name => 'Choose name',
            project => 'Standard'
         }
      }
   );

=head1 Other methods for your form

=over 4

=item options_$fieldname 

If you have a 'Select' or 'Multiple' field, there are three ways to provide the 
'options', or the list of values and labels for the select list. 1) Get them 
automatically from a database table (from the relationship that is the field 
name/accessor), 2) provide them in the field's 'options' attribute, or 2) 
provide them from an options_$fieldname method in the form.

An 'options_$fieldname' method should return a list of ordered key (option 
value) and value (label to be displayed in the select list) pairs.

   sub options_fruit {
       return (
           1   => 'apples',
           2   => 'oranges',
           3   => 'kiwi',
       );
   }

You can also write custom methods to retrieve the option info from the database:

   sub options_country
   {
      my $self = shift; 
      return unless $self->schema;
      my @rows =
         $self->schema->resultset( 'Country' )->
            search( {}, { order_by => ['rank', 'country_name'] } )->all;
      return [ map { $_->digraph, $_->country_name } @rows ];
   }


=item init_value_$fieldname

Allows you to provide (in the form) a different initial value for a particular 
field than that in the database.

   sub init_value_license {
      my ( $self, $field, $item ) = @_;
      return 0 unless $item && $item->license_id; 
      return $item->license_id;
   }


=item validate_$fieldname

Instead of using field constraints, you can choose to
do per-field validation customization in a form method:

    sub validate_age {
        my ( $self, $field ) = @_;
        $field->add_error('Sorry, you must be 18')
            if $field->value < 18;
    }

A different form method name for this can be specified with the field's 
'set_validate' attribute:

    has_field 'age' => ( type => 'Text', set_validate => 'check_age' );

    sub check_age {
       ...
    } 

=item cross_validate

Handle cross-field validation, or any validation that needs to be done after
the entire form is validated.  This method is executed whether or not the form 
has validated so far.

   sub cross_validate {
      my $self = shift;
      if ( $self->field('count')->value && $self->field('duration')->value )
      {
          $self->field('duration')->add_error( 
                    'Do not enter both a count and a duration' );
      }
   }

=item update_model

Override the model's 'update_model' method to do additional updates.

   sub update_model {
      my $self = shift;
      $self->SUPER::update_model;
      my $event = $self->item;
      $event->update( ... );
   }

=back

=head1 Filling the HTML form with values

There are three ways to get the database or parameter values into the actual
HTML form. 

You can use the field method 'fif' (where "f" is "form.field('book')" ):
 
   [% f.fif %]

You can use the hash returned by the form method "fif":

   [% form.fif.book %]

Or you can use L<HTML::FillInForm> and the C<< $form->fif hash >>. 

If you are already using FormHandler field attributes in your form elements,
then using the field 'fif' method is probably easiest. If you are not using
FormHandler field attributes, then your choice is between using form.fif and
FillInForm.  

If you are not using FormHandler select lists and you use FillInForm, then 
it is possible to have FormHandler process HTML forms that have no template
references to the form object at all, as long as the field names are correct.
If you think that FillInForm is evil, then you could manage with only
using FormHandler to fill in the form.

=head1 Testing

It's much easier to write unit tests for FormHandler forms than for
Catalyst controllers. The 't' directory of the downloaded distribution 
has lots of examples. Here is an example of a test script for a DBIC form:


   use Test::More tests => 14;
   use lib 't/lib';

   use_ok( 'BookDB::Form::Book');
   use_ok( 'BookDB::Schema::DB');

   my $schema = BookDB::Schema::DB->connect('dbi:SQLite:t/db/book.db');
   ok($schema, 'get db schema');

   my $form = BookDB::Form::Book->new(schema => $schema);

   # This is munging up the equivalent of param data from a form
   my $good = {
       'title' => 'How to Test Perl Form Processors',
       'author' => 'I.M. Author',
       'genres' => [2, 4],
       'format'       => 2,
       'isbn'   => '123-02345-0502-2' ,
       'publisher' => 'EreWhon Publishing',
   };
   ok( $form->update( params => $good ), 'Good data' );

   my $book = $form->item;
   END { $book->delete };
   ok ($book, 'get book object from form');
   my $num_genres = $book->genres->count;
   is( $num_genres, 2, 'multiple select list updated ok');
   is( $form->value('format'), 2, 'get value for format' );

   my $bad_1 = {
       notitle => 'not req',
       silly_field   => 4,
   };
   ok( !$form->validate( $bad_1 ), 'bad 1' );

   my $bad_2 = {
       'title' => "Another Silly Test Book",
       'author' => "C. Foolish",
       'year' => '1590',
       'pages' => 'too few',
       'format' => '22',
   };
   ok( !$form->validate( $bad_2 ), 'bad 2');
   ok( $form->field('year')->has_errors, 'year has error' );
   ok( $form->field('pages')->has_errors, 'pages has error' );
   ok( !$form->field('author')->has_errors, 'author has no error' );
   ok( $form->field('format')->has_errors, 'format has error' );

   $form->set_param( year => 1999 );
   $form->set_param( pages => 101 );
   $form->set_param( format => 2 );
   ok( $form->validate, 'now form validates' );
   


=head1 AUTHORS

Gerda Shank, gshank@cpan.org

=head1 COPYRIGHT

This library is free software, you can redistribute it and/or modify it under
the same terms as Perl itself.

=cut
