Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Refactor new/update/process and clear for persistent forms

  • Loading branch information...
commit 48fcf283ad0a2013698c544eae09cc08cc55cb0b 1 parent 381ef05
gerda.shank@gmail.com authored
View
186 lib/HTML/FormHandler.pm
@@ -34,31 +34,35 @@ One of its goals is to keep the controller interface as simple as possible,
and to minimize the duplication of code.
An example of a Catalyst controller that uses an HTML::FormHandler form
-to update or create a 'Book' record (this example does not use
-L<Catalyst::Controller::Role::HTML::FormHandler>):
+to update a 'Book' record:
- package MyApp::Controller::Book;
+ package MyApp::Controller::Book;
+ use Moose;
+ use base 'Catalyst::Controller';
+ use MyApp::Form::Book;
+ has 'edit_form' => ( isa => 'MyApp::Form::Book', is => 'rw',
+ default => sub { MyApp::Form::Book->new } );
- use Moose;
- use MyApp::Form::Book;
-
- sub edit : Local {
- my ( $self, $c, $id ) = @_;
-
- # Create the form object
- my $form = MyApplication::Form::Book->new( $id );
- $c->stash->{form} = $form;
+ sub book_base : Chained PathPart('book') CaptureArgs(0)
+ {
+ my ( $self, $c ) = @_;
+ # setup
+ }
+ sub item : Chained('book_base') PathPart('') CaptureArgs(1)
+ {
+ my ( $self, $c, $book_id ) = @_;
+ $c->stash( book => $c->model('DB::Book')->find($book_id) );
+ }
+ sub edit : Chained('item') PathPart('edit') Args(0)
+ {
+ my ( $self, $c ) = @_;
- # Update or create the record if form posted
- $form->update_from_form( $c->req->parameters )
- if $c->req->method eq 'POST';
- # the return will cause the "end" action to use the default
- # view to display the form
- return unless $c->req->method eq 'POST' && $form->validated;
+ $c->stash( form => $self->edit_form, template => 'book/form.tt' );
+ return unless $self->edit_form->process( item => $c->stash->{book},
+ params => $c->req->parameters );
+ $c->res->redirect( $c->uri_for('list') );
+ }
- # form validated, so continue on to some other action
- $c->res->redirect( $c->uri_for('view', $form->item->id) );
- }
An example of a form class:
@@ -67,7 +71,7 @@ An example of a form class:
use Moose;
extends 'HTML::FormHandler::Model::DBIC';
- has '+item_class' => ( default => 'Book' );
+ has '+item_class' => ( default => 'User' );
sub profile {
return {
@@ -297,6 +301,14 @@ Place to store user data
has 'user_data' => ( isa => 'HashRef', is => 'rw' );
+=head2 ctx
+
+Place to store application context
+
+=cut
+
+has 'ctx' => ( is => 'rw' );
+
=head2 language_handle, build_language_handle
Holds a Local::Maketext language handle
@@ -368,7 +380,7 @@ has 'submit' => ( is => 'rw' );
=head2 params
Stores HTTP parameters.
-Also: set_param, get_param, reset_params, delete_param, from
+Also: set_param, get_param, _params, delete_param, from
Moose 'Collection::Hash' metaclass.
=cut
@@ -383,8 +395,9 @@ has 'params' => (
provides => {
set => 'set_param',
get => 'get_param',
- clear => 'reset_params',
+ clear => 'clear_params',
delete => 'delete_param',
+ empty => 'has_params',
},
);
@@ -461,11 +474,10 @@ Or a single item (model row object) or item_id (row primary key)
may be supplied:
MyForm->new( $id );
-
-or
-
MyForm->new( $item );
+If you will be processing a persistent form with 'process', no arguments
+are necessary.
The common attributes to be passed in to the constructor are:
item_id
@@ -494,6 +506,10 @@ The 'item', 'item_id', and 'item_class' attributes are defined
in L<HTML::FormHandler::Model>, and 'schema' is defined in
L<HTML::FormHandler::Model::DBIC>.
+FormHandler forms are handled in two steps: 1) create with 'new',
+2) handle with 'process' or 'update'. FormHandler doesn't
+care whether most parameters are set on new or process or update,
+but a 'profile' argument should be passed in on 'new'.
=head2 BUILD, BUILDARGS
@@ -535,23 +551,112 @@ sub BUILD
return;
}
+=head2 process
+
+For persistent FormHandler instances, processes a form
+
+ my $validated = $form->process( item => $book,
+ params => $c->req->parameters );
+
+or:
+
+ my $validated = $form->process( item_id => $item_id,
+ schema => $schema, params => $c->req->parameters );
+
+Calls 'clear_all' to clear previous values, calls 'update' to process the form.
+If you set attributes that are not cleared and you have a persistent form,
+you must either set that attribute on each request or clear it.
+
+=cut
+
+sub process
+{
+ my ( $self, @args ) = @_;
+ $self->clear;
+ return $self->update(@args);
+}
+
=head2 clear
-Clears out state information on the form. Normally only used in
-tests.
+Calls clear_state, clear_model (in the model), and clears 'ctx'
=cut
sub clear
+{
+ my $self = shift;
+ $self->clear_state;
+ $self->clear_model;
+ $self->ctx(undef) if $self->ctx;
+}
+
+=head2 update
+
+Pass in item or item_id/schema and parameters.
+
+ my $form = MyApp::Form::Book->new;
+ <later>
+ $form->clear;
+ $form->update( item => $item );
+
+=cut
+
+sub update
+{
+ my ( $self, @args ) = @_;
+ my $hashref = {@args};
+ while ( my ($key, $value) = each %{$hashref} )
+ {
+ $self->$key($value);
+ }
+ $self->init_from_object;
+ $self->load_options;
+ my $validated = $self->validate if $self->has_params;
+ $self->update_model if $validated;
+ return $validated;
+}
+
+=head2 clear_state
+
+Clears out state information in the form.
+
+ validated
+ ran_validation
+ num_errors
+ updated_or_created
+ fields: value, input, fif, errors
+ params
+
+=cut
+
+sub clear_state
{
my $self = shift;
$self->validated(0);
$self->ran_validation(0);
$self->num_errors(0);
- $self->clear_values;
+ $self->clear_values;
+ $self->clear_params;
$self->updated_or_created(undef);
}
+=head2 clear_values
+
+Clears field value, input, errors. Form params.
+
+=cut
+
+sub clear_values
+{
+ my $self = shift;
+ for ( $self->fields )
+ {
+ $_->value(undef);
+ $_->input(undef);
+ $_->clear_errors;
+ }
+}
+
=head2 build_form
This parses the form profile and creates the individual
@@ -781,23 +886,6 @@ sub init_from_object
}
}
-=head2 clear_values
-
-Clears the internal and external values of the form
-
-=cut
-
-sub clear_values
-{
- my $self = shift;
-
- for ( $self->fields )
- {
- $_->value(undef);
- $_->input(undef);
- }
- $self->reset_params;
-}
=head2 fif -- "fill in form"
@@ -902,7 +990,7 @@ object stays in memory between requests), returns the cached validated result.
is true. To force a re-validation call $form->clear.
Params may be passed in to validate, or else may be set earlier
-by passing into 'update_from_form' in the model, or using the params setter.
+on new, or by using the params setter.
The method does the following:
@@ -955,7 +1043,7 @@ sub validate
$self->cross_validate($params);
# model specific validation
- $self->model_validate;
+ $self->validate_model;
$self->clear_dependency;
# count errors
View
85 lib/HTML/FormHandler/Manual/Intro.pod
@@ -27,7 +27,7 @@ create your own field types, and perform specialized validation. And you
can subclass the methods in HTML::FormHandler::Model::DBIC and
HTML::FormHandler.
-The L<Catalyst::Controller::HTML::FormHandler> package includes a working
+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:
@@ -42,7 +42,6 @@ the form in an HTML page.
Create a Form, subclassed from HTML::FormHandler::Model::DBIC
package MyApp:Form::User;
-
use Moose;
extends 'HTML::FormHandler::Model::DBIC';
@@ -148,38 +147,69 @@ space, you can specify the config values in your application module:
__PACKAGE__->config( { 'Controller::HTML::FormHandler' =>
{ model_name => 'DB', form_name_space => 'MyApp::Form' }} );
-In a Catalyst controller (with Catalyst::Controller::HTML::FormHandler):
+In a Catalyst controller:
package MyApp::Controller::User;
- use strict;
- use warnings;
- use base 'Catalyst::Controller::HTML::FormHandler';
-
- # If you didn't specify the model_name already
- __PACKAGE__->config( model_name => 'DB');
+ use Moose;
+ use base 'Catalyst::Controller';
+ has 'form' => ( isa => 'MyApp::Form::User', is => 'rw',
+ default => sub { MyApp::Form::User->new } );
# Create or edit
sub edit : Local {
my ( $self, $c, $user_id ) = @_;
- $c->stash->{template} = 'user/edit.tt';
- # Validate and insert/update database. Args = pk, form name
- return unless $self->update_from_form( $user_id, 'User' );
+
+ $c->stash( template => 'user/edit.tt' );
+ return unless $self->form->process( item_id => $user_id,
+ schema => $c->model('DB::User')->schema );
+
# Form validated.
- $c->stash->{user} = $c->stash->{form}->item;
+ $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:
-With the Catalyst controller the schema is set from the model_name config
-options, ($c->model($model_name)...), but it can also be set by passing
-in the schema on "new", or setting with $form->schema($schema), or from
-passing in a row object.
+ my $form = BookDB::Form::Book->new( item => $book );
+ return unless $form->process( params => $c->req->parameters );
If you want to use FillInForm to fill in values instead of the field's
-fif attribute, use can L<Catalyst::Controller::HTML::FormHandler>. If you
-set C<< __PACKAGE__->config( fif => 1 ) >> the C<< $form->fif >> hash
-will be set in the C<< $c->stash->{fillinform} >>> stash key. The base
-controller 'end' routine will call FillInForm for you, or you can use
-the stash key to handle FillInForm yourself.
+fif attribute, 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:
+
+ my $validated = $self->form->process( ... );
+ $c->stash( fillinform => $self->form->fif );
+ return unless $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 The Form
@@ -192,7 +222,7 @@ FormHandler behavior can be changed in a particular form.
=head1 Form Models
If you are not using a database interface, the base class for your forms is
-HTML::FormHandler. For use with a database, you need to use a form model class--
+HTML::FormHandler. For use with a database, use a model base class--
a class that knows how to work with your data objects, such as
L<HTML::FormHandler::Model::DBIC> or L<HTML::FormHandler::Model::CDBI>.
@@ -207,8 +237,9 @@ primary key is passed in, the model will use the item_class (DBIC source
name) to fetch the row from the database. The database row is stored in the
form's "item" attribute.
-The $form->update_from_form method will validate the parameters and then
-update or create the database row object.
+The C<< $form->process >> or C<< $form->update >> methods will validate
+the parameters and then update or create the database row object.
+
=head1 The form profile
@@ -469,9 +500,7 @@ 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 >>. For Catalyst,
-an example "end" routine to call FillInForm is provided in
-L<Catalyst::Controller::HTML::FormHandler>
+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
View
29 lib/HTML/FormHandler/Manual/Tutorial.pod
@@ -49,7 +49,7 @@ create the file Book.pm.
This is your Form class. The form initializes the 'item_class' to the
source name of your DBIx::Class result class. The form's fields are defined
-in the 'profile' attribute. The names of the fields must match an
+in the 'profile' attribute. The names of the fields should match an
accessor in your DBIx::Class result source: a column, relationship, or
other accessor.
@@ -69,19 +69,16 @@ this simple form the default types are adequate.
=head2 Connect HTML::FormHandler to your controller
-Use L<Catalyst::Controller::Role::HTML::FormHandler>
-to connect your controllers to your form class.
+Your controller must use Moose:
-First, change your C<lib/MyApp/Controller/Books.pm> to use the
-the FormHandler role by adding:
+ use Moose;
+ use base 'Catalyst::Controller';
- use Moose ('with'); # if you're not already using Moose
- with 'Catalyst::Controller::Role::HTML::FormHandler';
+Create an attribute to hold your form:
-Add the database model name to the controller config (you could also specify
-it in the update_from_form call):
+ has 'form' => ( isa => 'MyApp::Form::Book', is => 'rw',
+ default => sub { MyApp::Form::Book->new } );
- __PACKAGE__->config( model_name => 'DB' );
=head2 Add Action to Display and Save the Form
@@ -90,10 +87,11 @@ In C<lib/MyApp/Controller/Books.pm> add the following method:
sub edit : Local {
my ( $self, $c, $book_id ) = @_;
- $c->stash->{template} = 'books/edit.tt2';
+ $c->stash( template => 'books/edit.tt2' );
- # Validate and insert/update database. Args = pk, form name
- return unless $self->update_from_form( $book_id, 'Book' );
+ # Validate and insert/update database
+ return unless $self->form->process( item_id => $book_id,
+ schema => $c->model('DB::Books')->schema );
# Form validated, return to the books list
$c->flash->{status_msg} = 'Book saved';
@@ -102,8 +100,9 @@ In C<lib/MyApp/Controller/Books.pm> add the following method:
This will handle both creating new books, and updating old books.
If $book_id is undefined, then HTML::FormHandler will create
-a new book from your form. You can also pass in a DBIx::Class row
-object instead of a primary key.
+a new book from your form. If you pass in a DBIx::Class row
+object instead of a primary key, you don't need to specify the
+schema.
=head2 Create a Template for the form
View
34 lib/HTML/FormHandler/Model.pm
@@ -149,29 +149,7 @@ sub init_value
}
-=head2 update_from_form
-
-Update or create the object.
-
-This needs to be overridden in the model subclass or in your
-form subclass. It should update $form->item, if set, otherwise
-create a new item.
-
-Any field names that are related to the class by "has_many" and
-have a mapping table should also be updated.
-
-Validation must also be run unless validation has already been run.
-($form->clear might need to be called if the $form object stays in memory
-between requests.)
-
-=cut
-
-sub update_from_form
-{
- die "must define 'update_from_form' in HTML::FormHandler::Model subclass";
-}
-
-=head2 model_validate
+=head2 validate_model
Validates profile items that are dependent on the model.
This is called via the validation process and the model class
@@ -187,7 +165,15 @@ The default method does nothing.
=cut
-sub model_validate { }
+sub validate_model { }
+
+=head2 clear_model
+
+Clear out any dynamic data for persistent object
+
+=cut
+
+sub clear_model { }
=head1 AUTHOR
View
28 lib/HTML/FormHandler/Model/CDBI.pm
@@ -39,6 +39,9 @@ HTML::FormHandler::Model::CDBI - Class::DBI model class for HTML::FormHandler
A Class::DBI database model for HTML::FormHandler
+I don't use CDBI, so this module is not well tested. Patches
+and tests gratefully accepted.
+
=head1 METHODS
@@ -242,31 +245,6 @@ sub init_value {
return @values;
}
-=head2 update_from_form
-
- my $ok = $form->update_from_form( $parameter_hash );
-
-Update or create the object from values in the form.
-
-The actual update is done in the C<update_model> method.
-Pass in hash reference of parameters.
-Returns false if form does not validate.
-
-=cut
-
-sub update_from_form {
- my ( $self, $params ) = @_;
-
- return unless $self->validate($params);
- if ( $self->item_class->can('do_transaction') ) {
- $self->item_class->do_transaction( sub { $self->update_model } );
- }
- else {
- $self->update_model;
- }
- return 1;
-}
-
=head2 model_validate
Validates profile items that are dependent on the model.
View
71 lib/HTML/FormHandler/Model/DBIC.pm
@@ -81,10 +81,7 @@ Catalyst context and the item_class in the plugin.
=cut
has 'schema' => (
- isa => 'DBIx::Class::Schema',
is => 'rw',
- lazy => 1,
- builder => 'build_schema'
);
has 'source_name' => (
isa => 'Str',
@@ -102,51 +99,34 @@ sub BUILDARGS
return {@args};
}
-=head2 update_from_form
-
- my $validated = $form->update_from_form( item => $row );
- or
- my $validated = $form->update_form_form( item_id => undef, schema => $schema );
-This is not the same as the subroutine called with $c->update_from_form--the
-Catalyst controller/role routine that calls this one. This method updates or
-creates the object from values in the form.
+=head2 validate_model
-All fields that refer to columns and have been changed will be updated. Field names
-that are a single relationship will be updated. Any field names that are related
-to the class by "has_many" and have the 'multiple' flag set are assumed to have a
-mapping table and will be updated. Validation is run unless validation has already
-been run. ($form->clear might need to be called if the $form object stays in memory
-between requests.)
-
-The actual update is done in the C<update_model> method. Your form class can
-override that method if you wish to do additional
-database inserts or updates. This is useful when a single form updates
-multiple tables, or there are secondary tables to update.
+The place to put validation that requires database-specific lookups.
+Subclass this method in your form. Validation of unique fields is
+called from this method.
=cut
-sub update_from_form
+sub validate_model
{
- my ( $self, $params ) = @_;
- return unless $self->validate($params);
- $self->update_model;
+ my ($self) = @_;
+ return unless $self->validate_unique;
return 1;
}
-=head2 model_validate
+=head2 clear_model
-The place to put validation that requires database-specific lookups.
-Subclass this method in your form. Validation of unique fields is
-called from this method.
+Clear state for persistent forms
=cut
-sub model_validate
+sub clear_model
{
- my ($self) = @_;
- return unless $self->validate_unique;
- return 1;
+ my $self = shift;
+ $self->item(undef);
+ $self->item_id(undef);
+ $self->schema(undef);
}
=head2 update_model
@@ -289,7 +269,7 @@ sub update_model
# Save item in form object
$self->item($item);
- $self->reset_params; # force reload of parameters from values
+ $self->clear_params; # force reload of parameters from values
return $item;
}
@@ -391,6 +371,7 @@ sub lookup_options
{
my ( $self, $field ) = @_;
+ return unless $self->schema;
my $field_name = $field->name;
my $prefix = $self->name_prefix;
$field_name =~ s/^$prefix\.//g if $prefix;
@@ -593,26 +574,6 @@ sub set_item
$self->schema( $item->result_source->schema );
}
-=head2 build_schema
-
-For initializing the DBIx::Class schema. User may override.
-Default is to die if a schema does not exist.
-
-Pass schema in on new:
-
- $my_form_class->new(item_id => $id, schema => $schema)
-
-Or pass in a row (result) object ('item'), and the schema will be set from
-the result object.
-
-=cut
-
-sub build_schema
-{
- my $self = shift;
- return if exists $self->{schema};
- die "Schema must be defined for HTML::FormHandler::Model::DBIC";
-}
sub build_source_name
{
View
8 t/book.t
@@ -24,7 +24,7 @@ my $form = BookDB::Form::Book->new(item_id => undef, schema => $schema);
ok( !$form->validate, 'Empty data' );
-$form->clear;
+$form->clear_state;
# This is munging up the equivalent of param data from a form
my $good = {
@@ -51,7 +51,7 @@ is( $num_genres, 2, 'multiple select list updated ok');
is( $form->value('format'), 2, 'get value for format' );
my $id = $book->id;
-$form->clear;
+$form->clear_state;
my $bad_1 = {
notitle => 'not req',
@@ -59,7 +59,7 @@ my $bad_1 = {
};
ok( !$form->validate( $bad_1 ), 'bad 1' );
-$form->clear;
+$form->clear_state;
my $bad_2 = {
'title' => "Another Silly Test Book",
@@ -79,7 +79,7 @@ ok( !$form->field('author')->has_errors, 'author has no error' );
ok( $form->field('format')->has_errors, 'format has error' );
-$form->clear;
+$form->clear_state;
$form = BookDB::Form::Book->new(item => $book, schema => $schema);
ok( $form, 'create form from db object');
View
8 t/book_auto.t
@@ -22,7 +22,7 @@ my $form = BookDB::Form::BookAuto->new(item_id => undef, schema => $schema);
ok( !$form->validate, 'Empty data' );
-$form->clear;
+$form->clear_state;
# This is munging up the equivalent of param data from a form
my $good = {
@@ -41,7 +41,7 @@ ok ($book, 'get book object from form');
# clean up book db & form
$book->delete;
-$form->clear;
+$form->clear_state;
my $bad_1 = {
notitle => 'not req',
@@ -49,7 +49,7 @@ my $bad_1 = {
};
ok( !$form->validate( $bad_1 ), 'bad 1' );
-$form->clear;
+$form->clear_state;
my $bad_2 = {
'book.title' => "Another Silly Test Book",
@@ -67,6 +67,6 @@ ok( !$form->field('pages')->has_errors, 'pages has no error' );
ok( !$form->field('author')->has_errors, 'author has no error' );
-$form->clear;
+$form->clear_state;
View
8 t/book_html.t
@@ -25,7 +25,7 @@ my $form = BookDB::Form::BookHTML->new(item_id => undef, schema => $schema);
ok( !$form->validate, 'Empty data' );
-$form->clear;
+$form->clear_state;
# This is munging up the equivalent of param data from a form
my $good = {
@@ -46,7 +46,7 @@ ok ( $book->title eq 'How to Test Perl Form Processors', 'get title');
# clean up book db & form
$book->delete;
-$form->clear;
+$form->clear_state;
my $bad_1 = {
'book.notitle' => 'not req',
@@ -54,7 +54,7 @@ my $bad_1 = {
};
ok( !$form->validate( $bad_1 ), 'bad 1' );
-$form->clear;
+$form->clear_state;
my $bad_2 = {
'book.title' => "Another Silly Test Book",
@@ -70,6 +70,6 @@ ok( $form->field('pages')->has_errors, 'pages has error' );
ok( !$form->field('author')->has_errors, 'author has no error' );
-$form->clear;
+$form->clear_state;
View
4 t/book_m2m.t
@@ -23,7 +23,7 @@ my $form = BookDB::Form::BookM2M->new(item_id => undef, schema => $schema);
ok( !$form->validate, 'Empty data' );
-$form->clear;
+$form->clear_state;
# This is munging up the equivalent of param data from a form
my $good = {
@@ -45,7 +45,7 @@ my $num_genres = $book->genres->count;
is( $num_genres, 3, 'multiple select list updated ok');
my $id = $form->item->id;
-$form->clear;
+$form->clear_state;
$form = BookDB::Form::BookM2M->new( item_id => $id, schema => $schema );
my $genres_field = $form->field('genres');
View
BIN  t/db/book.db
Binary file not shown
View
2  t/errors.t
@@ -76,6 +76,6 @@ is_deeply( \@field_names,
is( $form->field('fruit')->id, "testform_fruit", 'field has id' );
-$form->clear;
+$form->clear_state;
View
4 t/fif.t
@@ -46,7 +46,7 @@ is_deeply( $fif, {
pages => '702',
}, 'get form fif' );
-$form->clear;
+$form->clear_state;
$fif->{pages} = '501';
$form = BookDB::Form::Book->new(item => $book, schema => $schema, params => $fif);
@@ -62,7 +62,7 @@ my $validated = $form->validate;
ok( $validated, 'validated without params' );
-$form->clear;
+$form->clear_state;
my $params = {
title => 'Testing form',
isbn => '02340234',
View
6 t/form_handler.t
@@ -38,7 +38,7 @@ my $form = My::Form->new;
ok( !$form->validate, 'Empty data' );
-$form->clear;
+$form->clear_state;
my $good = {
reqname => 'hello',
@@ -53,7 +53,7 @@ my $bad_1 = {
fruit => 4,
};
-$form->clear;
+$form->clear_state;
ok( !$form->validate( $bad_1 ), 'bad 1' );
ok( $form->field('fruit')->has_errors, 'fruit has error' );
@@ -66,6 +66,6 @@ is( $form->field('fruit')->id, "testform_fruit", 'field has id' );
is( $form->field('fruit')->label, 'Fruit', 'field label');
-$form->clear;
+$form->clear_state;
View
2  t/lib/BookDB.pm
@@ -12,8 +12,6 @@ BookDB->config( name => 'BookDB' );
BookDB->setup;
-
-
=head1 NAME
BookDB - Catalyst based application
View
111 t/lib/BookDB/Controller/Book.pm
@@ -2,11 +2,13 @@ package BookDB::Controller::Book;
use Moose;
use base 'Catalyst::Controller';
-with 'Catalyst::Controller::Role::HTML::FormHandler';
-use DateTime;
use BookDB::Form::Book;
+use BookDB::Form::BookView;
-__PACKAGE__->config( form_name_space => 'BookDB::Form' );
+has 'edit_form' => ( isa => 'BookDB::Form::Book', is => 'rw',
+ default => sub { BookDB::Form::Book->new } );
+has 'view_form' => ( isa => 'BookDB::Form::BookView', is => 'rw',
+ default => sub { BookDB::Form::BookView->new } );
=head1 NAME
@@ -20,14 +22,6 @@ See L<BookDB>
Book Controller
-=head1 METHODS
-
-=over 4
-
-=item add
-
-Sets a template.
-
=cut
@@ -51,14 +45,11 @@ sub list : Chained('book_base') PathPart('list') Args(0)
sub do_list
{
my ( $self, $c ) = @_;
- # get an array of row object
+
my $books = [ $c->model('DB::Book')->all ];
- # set columns in order wanted for list
my @columns = ( 'title', 'author', 'publisher', 'year' );
-
- $c->stash->{books} = $books;
- $c->stash->{columns} = \@columns;
- $c->stash->{template} = 'book/list.tt';
+ $c->stash( books => $books, columns => \@columns,
+ template => 'book/list.tt' );
}
sub create : Chained('book_base') PathPart('create') Args(0)
@@ -72,111 +63,44 @@ sub create : Chained('book_base') PathPart('create') Args(0)
sub item : Chained('book_base') PathPart('') CaptureArgs(1)
{
my ( $self, $c, $book_id ) = @_;
- $c->stash->{book} = $c->model('DB::Book')->find($book_id);
+ $c->stash( book => $c->model('DB::Book')->find($book_id) );
}
-=item edit
-
-Handles displaying and validating the form
-Will save to the database on validation
-
-=cut
-
sub edit : Chained('item') PathPart('edit') Args(0)
{
my ( $self, $c ) = @_;
return $self->form($c);
}
-
sub form
{
my ( $self, $c ) = @_;
- # Name template, otherwise it will use book/add.tt or book/edit.tt
- $self->ctx->stash->{template} = 'book/form.tt';
-
- # Name form, otherwise it will expect 'Book::Edit'
- my $book = $c->stash->{book};
- my $validated = $self->update_from_form( $book, 'Book' );
- $c->stash->{form}->action( $c->chained_uri_for->as_string );
- return if !$validated; # This (re)displays the form, because it's the
- # 'end' of the method, and the 'default end' action
- # takes over, which is to render the view
-
- # get the new book that was just created by the form
- my $new_book = $c->stash->{form}->item;
-
- # redirect to list. 'show' also a possibility...
+ $c->stash( form => $self->edit_form, template => 'book/form.tt',
+ action => $c->chained_uri_for->as_string );
+ return unless $self->edit_form->process( item => $c->stash->{book},
+ params => $c->req->parameters );
$c->res->redirect( $c->uri_for('list') );
}
-=item form (without Catalyst plugin)
-
-Handles displaying and validating the form without the controller/role
-methods. Will save to the database on validation.
-
-=cut
-
-sub edit_alt : Chained('item') PathPart('edit_alt') CaptureArgs(0)
-{
- my ( $self, $c ) = @_;
-
- my $book = $c->stash->{book};
- my $form = BookDB::Form::Book->new($book);
- # put form and template in stash
- $c->stash->{form} = $form;
- $c->stash->{template} = 'book/edit_alt.tt';
-
- # update form
- $form->update_from_form( $c->req->parameters ) if $c->req->method eq 'POST';
- $c->stash->{form}->action( $c->chained_uri_for->as_string );
- return unless $c->req->method eq 'POST' && $form->validated;
-
- # get the new book that was just created by the form
- my $new_book = $form->item;
- # redirect to list.
- $c->res->redirect( $c->uri_for('list') );
-}
-
-=item delete
-
-Destroys a row and forwards to list.
-
-=cut
-
sub delete : Chained('item') PathPart('delete') Args(0)
{
my ( $self, $c ) = @_;
- # delete row in database
$c->stash->{book}->delete;
- # redirect to list page
$c->res->redirect( $c->uri_for('list') );
}
-=item view
-
-Fetches a row and sets a template.
-
-=cut
-
sub view : Chained('item') PathPart('') Args(0)
{
my ( $self, $c, $id ) = @_;
- $c->stash->{template} = 'book/view.tt';
- my $validated = $self->update_from_form( $c->stash->{book}, 'BookView' );
- return if !$validated;
-$DB::single=1;
- # form validated
+ $c->stash( form => $self->view_form, template => 'book/view.tt' );
+ return unless $self->view_form->process( item => $c->stash->{book},
+ params => $c->req->parameters );
$c->stash->{message} = 'Book checked out';
}
-=item do_return
-
-=cut
-
sub do_return : Chained('item') PathPart('return') Args(0)
{
my ( $self, $c ) = @_;
@@ -190,9 +114,6 @@ sub do_return : Chained('item') PathPart('return') Args(0)
$c->detach;
}
-=item
-=cut
-
=back
=head1 AUTHOR
View
147 t/lib/BookDB/Controller/Borrower.pm
@@ -2,154 +2,95 @@ package BookDB::Controller::Borrower;
use Moose;
use base 'Catalyst::Controller';
-with 'Catalyst::Controller::Role::HTML::FormHandler';
+use BookDB::Form::Borrower;
+has 'my_form' => ( isa => 'BookDB::Form::Borrower', is => 'rw',
+ default => sub { BookDB::Form::Borrower->new } );
=head1 NAME
BookDB::Controller::Borrower
-=head1 SYNOPSIS
-
-See L<BookDB>
-
=head1 DESCRIPTION
Controller for Borrower
-=head1 METHODS
-
-=over 4
-
-=item add
-
-Sets a template.
-
=cut
-__PACKAGE__->config( model_name => 'DB', form_name_space => 'BookDB::Form' );
-sub add : Local
+sub borrower_base : Chained PathPart('borrower') CaptureArgs(0)
{
my ( $self, $c ) = @_;
-
- $c->forward('do_form');
}
-=item form
-
-Handles displaying and validating the form
-Will save to the database on validation
-
-=cut
-
-sub do_form : Private
+sub default : Chained('borrower_base') PathPart('') Args
{
- my ( $self, $c, $id ) = @_;
-
- # Set template
- $c->stash->{template} = 'borrower/form.tt';
- # Fill form Al Gore
-$DB::single=1;
- my $validated = $self->update_from_form( $id, 'Borrower' );
-
- # this could also be
- # return unless $c->update_from_form( $id, 'Borrower');
- # but that makes it difficult to look at with the debugger.
- return if !$validated; # This (re)displays the form, because it's the
- # 'end' of the method, and the 'default end' action
- # takes over, which is to render the view
-
- # get the new borrower that was just created by the form
- my $new_borrower = $c->stash->{form}->item;
- $c->res->redirect( $c->uri_for('list') );
+ my ( $self, $c ) = @_;
+ return $self->do_list($c);
}
-=item default
-
-Forwards to list.
-
-=cut
-
-sub default : Private
+sub list : Chained('borrower_base') PathPart('list') Args(0)
{
my ( $self, $c ) = @_;
- $c->res->redirect( $c->uri_for('list') );
+ return $self->do_list($c);
}
-=item destroy
-
-Destroys a row and forwards to list.
+sub do_list
+{
+ my ( $self, $c ) = @_;
-=cut
+ my $borrowers = [ $c->model('DB::Borrower')->all ];
+ my @columns = ( 'name', 'email' );
+ $c->stash( borrowers => $borrowers, columns => \@columns,
+ template => 'borrower/list.tt' );
+}
-sub destroy : Local
+sub add : Chained('borrower_base') PathPart('add') Args(0)
{
- my ( $self, $c, $id ) = @_;
- $c->model('DB::Borrower')->find($id)->delete;
- $c->stash->{message} = 'Borrower deleted';
- $c->res->redirect( $c->uri_for('list') );
+ my ( $self, $c ) = @_;
+ # Create the empty borrower row for the form
+ $c->stash( borrower => $c->model('DB::Borrower')->new_result({}) );
+ return $self->form($c);
}
-=item do_add
-
-Adds a new row to the table and forwards to list.
-
-=cut
-
-=item edit
-
-Sets a template.
-
-=cut
-
-sub edit : Local
+sub item : Chained('borrower_base') PathPart('') CaptureArgs(1)
{
- my ( $self, $c, $id ) = @_;
-
- $c->forward('do_form');
+ my ( $self, $c, $borrower_id ) = @_;
+ $c->stash( borrower => $c->model('DB::Borrower')->find($borrower_id) );
}
-=item list
-
-Sets a template.
-
-=cut
-
-sub list : Local
+sub edit : Chained('item') PathPart('edit') Args(0)
{
my ( $self, $c ) = @_;
+ return $self->form($c);
+}
- # get an array of row objects
- my $borrowers = [ $c->model('DB::Borrower')->all ];
- my @columns = ( 'name', 'email' );
+sub form
+{
+ my ( $self, $c ) = @_;
- $c->stash->{borrowers} = $borrowers;
- $c->stash->{columns} = \@columns;
- $c->stash->{template} = 'borrower/list.tt';
+ $c->stash( form => $self->my_form, template => 'borrower/form.tt',
+ action => $c->chained_uri_for->as_string );
+ return unless $self->my_form->process( item => $c->stash->{borrower},
+ params => $c->req->parameters );
+ $c->res->redirect( $c->uri_for('list') );
}
-=item view
-
-Fetches a row and sets a template.
+sub delete : Chained('item') PathPart('delete') Args(0)
+{
+ my ( $self, $c ) = @_;
-=cut
+ $c->stash->{borrower}->delete;
+ $c->res->redirect( $c->uri_for('list') );
+}
-sub view : Local
+sub view : Chained('item') PathPart('') Args(0)
{
my ( $self, $c, $id ) = @_;
- # get row object for this borrower id
- my $borrower = $c->model('DB::Borrower')->find($id);
- # list of columns in order for form
my @columns = ( 'name', 'email', 'phone', 'url' );
-
- my $rel = $c->model('DB')->source('Borrower')->relationship_info('books');
- $c->stash->{columns} = \@columns;
- $c->stash->{borrower} = $borrower;
- $c->stash->{template} = 'borrower/view.tt';
+ $c->stash( columns => \@columns, template => 'borrower/view.tt' );
}
-=back
=head1 AUTHOR
View
8 t/lib/BookDB/Controller/Root.pm
@@ -10,6 +10,14 @@ use base 'Catalyst::Controller';
#
__PACKAGE__->config->{namespace} = '';
+sub index : Path Args
+{
+ my ( $self, $c ) = @_;
+ $c->res->redirect('/book');
+ $c->detach;
+
+}
+
=head1 NAME
MyApp::Controller::Root - Root Controller for MyApp
View
2  t/lib/BookDB/root/book/form.tt
@@ -3,7 +3,7 @@
[% FOR efield IN form.error_fields %]
<div class='error' id=error>[% efield.name _ ': ' _ efield.errors %] </div>
[% END %]
-<form action="[% form.action %]" method="post">
+<form action="[% action %]" method="post">
<p>
[% f = form.field('title') %]
<label class="label" for="[% f.name %]">[% f.label %]</label>
View
2  t/lib/BookDB/root/borrower/form.tt
@@ -7,7 +7,7 @@
[% END %]
[% END %]
-<form action="[% c.uri_for('add', form.item_id) %]" method="post">
+<form action="[% action %]" method="post">
[% FOREACH f IN form.sorted_fields %]
<p>
<label class="label" for="[% f.name %]">[% f.label || f.name %]</label>
View
8 t/lib/BookDB/root/borrower/list.tt
@@ -13,12 +13,12 @@
<td>[% borrower.$column %]</td>
[% END %]
<td>
- <a href="[% c.uri_for('view', borrower.id) %]">View</a>
- <a href="[% c.uri_for('edit', borrower.id) %]">Edit</a>
- <a href="[% c.uri_for('destroy', borrower.id) %]">Destroy</a>
+ <a href="[% '/borrower/' _ borrower.id %]">View</a>
+ <a href="[% '/borrower/' _ borrower.id _ '/edit' %]">Edit</a>
+ <a href="[% '/borrower/' _ borrower.id _ '/delete' %]">Destroy</a>
</td>[% counter = counter + 1 %]
</tr>[% END %]
</table>
-<p><a class="big" href="[% c.uri_for('add') %]">+ ADD</a></p>
+<p><a class="big" href="/borrower/add">+ ADD</a></p>
[% PROCESS scaffold/footer.tt %]
View
3  t/model_cdbi.t
@@ -0,0 +1,3 @@
+use Test::More tests => 1;
+
+use_ok( 'HTML::FormHandler::Model::CDBI' );
View
2  t/model_dbic.t
@@ -86,6 +86,6 @@ my $params = {
extra => 'extra_test'
};
-$form3->update_from_form( $params );
+$form3->update( params => $params );
is( $book3->author, 'S.Else', 'row object updated');
is( $form3->value('extra'), 'extra_test', 'value of non-db field');
View
84 t/process.t
@@ -0,0 +1,84 @@
+use strict;
+use warnings;
+use Test::More;
+use lib 't/lib';
+
+BEGIN {
+ eval "use DBIx::Class";
+ plan skip_all => 'DBIX::Class required' if $@;
+ plan tests => 18;
+}
+
+use_ok( 'HTML::FormHandler' );
+
+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;
+
+ok( $form, 'no param new' );
+
+ok( !$form->process( schema => $schema ), 'Empty data' );
+
+# 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->process( schema => $schema, 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 $id = $book->id;
+
+
+my $bad_1 = {
+ notitle => 'not req',
+ silly_field => 4,
+};
+
+ok( !$form->process( schema => $schema, params => $bad_1 ), 'bad 1' );
+
+
+my $bad_2 = {
+ 'title' => "Another Silly Test Book",
+ 'author' => "C. Foolish",
+ 'year' => '1590',
+ 'pages' => 'too few',
+ 'format' => '22',
+};
+
+ok( !$form->process( schema => $schema, params => $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->process(item => $book, schema => $schema);
+ok( $form, 'create form from db object');
+
+my $genres_field = $form->field('genres');
+is_deeply( sort $genres_field->value, [2, 4], 'value of multiple field is correct');
+
View
25 t/script/bookdb_server.pl
@@ -1,8 +1,8 @@
-#!/usr/local/bin/perl -w
+#!/usr/bin/perl -w
BEGIN {
$ENV{CATALYST_ENGINE} ||= 'HTTP';
- $ENV{CATALYST_SCRIPT_GEN} = 30;
+ $ENV{CATALYST_SCRIPT_GEN} = 31;
require Catalyst::Engine::HTTP;
}
@@ -21,8 +21,9 @@ BEGIN
my $keepalive = 0;
my $restart = $ENV{BOOKDB_RELOAD} || $ENV{CATALYST_RELOAD} || 0;
my $restart_delay = 1;
-my $restart_regex = '\.yml$|\.yaml$|\.pm$';
+my $restart_regex = '(?:/|^)(?!\.#).+(?:\.yml$|\.yaml$|\.conf|\.pm)$';
my $restart_directory = undef;
+my $follow_symlinks = 0;
my @argv = @ARGV;
@@ -36,7 +37,8 @@ BEGIN
'restart|r' => \$restart,
'restartdelay|rd=s' => \$restart_delay,
'restartregex|rr=s' => \$restart_regex,
- 'restartdirectory=s' => \$restart_directory,
+ 'restartdirectory=s@' => \$restart_directory,
+ 'followsymlinks' => \$follow_symlinks,
);
pod2usage(1) if $help;
@@ -60,6 +62,7 @@ BEGIN
restart_delay => $restart_delay,
restart_regex => qr/$restart_regex/,
restart_directory => $restart_directory,
+ follow_symlinks => $follow_symlinks,
} );
1;
@@ -85,11 +88,12 @@ =head1 SYNOPSIS
-rd -restartdelay delay between file checks
-rr -restartregex regex match files that trigger
a restart when modified
- (defaults to '\.yml$|\.yaml$|\.pm$')
+ (defaults to '\.yml$|\.yaml$|\.conf|\.pm$')
-restartdirectory the directory to search for
- modified files
- (defaults to '../')
-
+ modified files, can be set mulitple times
+ (defaults to '[SCRIPT_DIR]/..')
+ -follow_symlinks follow symlinks in search directories
+ (defaults to false. this is a no-op on Win32)
See also:
perldoc Catalyst::Manual
perldoc Catalyst::Manual::Intro
@@ -98,10 +102,9 @@ =head1 DESCRIPTION
Run a Catalyst Testserver for this application.
-=head1 AUTHOR
+=head1 AUTHORS
-Sebastian Riedel, C<sri@oook.de>
-Maintained by the Catalyst Core Team.
+Catalyst Contributors, see Catalyst.pm
=head1 COPYRIGHT
View
2  t/unique.t
@@ -20,7 +20,7 @@ my $form = BookDB::Form::Book->new(item_id => undef, schema => $schema);
ok( !$form->validate, 'Empty data' );
-$form->clear;
+$form->clear_state;
# This is munging up the equivalent of param data from a form
my $params = {
Please sign in to comment.
Something went wrong with that request. Please try again.