alternate front-ends to the Apache and CGI interfaces, or subclassing chunks
of the front-end modules to alter Maypole's behaviour in particular ways.
+=head3 Separate model class modules
+
+You want to put all the C<BeerDB::Beer> routines in a separate module,
+so you say:
+
+ package BeerDB::Beer;
+ BeerDB::Beer->has_a(brewery => "BeerDB::Brewery");
+ sub foo :Exported {}
+
+And in F<BeerDB.pm>, you put:
+
+ use BeerDB::Beer;
+
+It doesn't work.
+
+B<Solution>: It doesn't work because of the timing of the module
+loading. C<use Beer::Beer> will try to set up the C<has_a> relationships
+at compile time, when the database tables haven't even been set up,
+since they're set up by
+
+ BeerDB->setup("...")
+
+which does its stuff at runtime. There are two ways around this; you can
+either move the C<setup> call to compile time, like so:
+
+ BEGIN { BeerDB->setup("...") }
+
+or move the module loading to run-time (my preferred solution):
+
+ BeerDB->setup("...");
+ BeerDB::Beer->require;
+
=head3 Debugging with the command line
You're seeing bizarre problems with Maypole output, and you want to test it in
The request will carry on as though it were a normal C<do_edit> POST, but
with the additional fields we have provided.
+=head3 Catching errors in a form
+
+A user has submitted erroneous input to an edit/create form. You want to
+send him back to the form with errors displayed against the erroneous
+fields, but have the other fields maintain the values that the user
+submitted.
+
+B<Solution>: This is basically what the default C<edit> template and
+C<do_edit> method conspire to do, but it's worth highlighting again how
+they work.
+
+If there are any errors, these are placed in a hash, with each error
+keyed to the erroneous field. The hash is put into the template as
+C<errors>, and we process the same F<edit> template again:
+
+ $r->{template_args}{errors} = \%errors;
+ $r->{template} = "edit";
+
+This throws us back to the form, and so the form's template should take
+note of the errors, like so:
+
+ FOR col = classmetadata.columns;
+ NEXT IF col == "id";
+ "<P>";
+ "<B>"; classmetadata.colnames.$col; "</B>";
+ ": ";
+ item.to_field(col).as_HTML;
+ "</P>";
+ IF errors.$col;
+ "<FONT COLOR=\"#ff0000\">"; errors.$col; "</FONT>";
+ END;
+ END;
+
+If we're designing our own templates, instead of using generic ones, we
+can make this process a lot simpler. For instance:
+
+ <TR><TD>
+ First name: <INPUT TYPE="text" NAME="forename">
+ </TD>
+ <TD>
+ Last name: <INPUT TYPE="text" NAME="surname">
+ </TD></TR>
+
+ [% IF errors.forename OR errors.surname %]
+ <TR>
+ <TD><SPAN class="error">[% errors.forename %]</SPAN> </TD>
+ <TD><SPAN class="error">[% errors.surname %]</SPAN> </TD>
+ </TR>
+ [% END %]
+
+The next thing we want to do is to put the originally-submitted values
+back into the form. We can do this relatively easily because Maypole
+passes the Maypole request object to the form, and the POST parameters
+are going to be stored in a hash as C<request.params>. Hence:
+
+ <TR><TD>
+ First name: <INPUT TYPE="text" NAME="forename"
+ VALUE="[%request.params.forename%]">
+ </TD>
+ <TD>
+ Last name: <INPUT TYPE="text" NAME="surname"
+ VALUE="[%request.params.surname%]">
+ </TD></TR>
+
+Finally, we might want to only re-fill a field if it is not erroneous, so
+that we don't get the same bad input resubmitted. This is easy enough:
+
+ <TR><TD>
+ First name: <INPUT TYPE="text" NAME="forename"
+ VALUE="[%request.params.forename UNLESS errors.forename%]">
+ </TD>
+ <TD>
+ Last name: <INPUT TYPE="text" NAME="surname"
+ VALUE="[%request.params.surname UNLESS errors.surname%]">
+ </TD></TR>
+
=head3 Uploading files and other data
You want the user to be able to upload files to store in the database.