[Catalyst-commits] r9396 - in Catalyst-Controller-RHTMLO/trunk: .
lib/Catalyst/Controller lib/Catalyst/Controller/RHTMLO t
t/lib t/lib/TestApp t/lib/TestApp/C t/lib/TestApp/Form
jgottshall at dev.catalyst.perl.org
jgottshall at dev.catalyst.perl.org
Tue Feb 24 22:11:53 GMT 2009
Author: jgottshall
Date: 2009-02-24 22:11:53 +0000 (Tue, 24 Feb 2009)
New Revision: 9396
Added:
Catalyst-Controller-RHTMLO/trunk/t/
Catalyst-Controller-RHTMLO/trunk/t/lib/
Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp.pm
Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp/
Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp/C/
Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp/C/Forms.pm
Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp/C/Root.pm
Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp/Form/
Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp/Form/One.pm
Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp/Form/Two.pm
Catalyst-Controller-RHTMLO/trunk/t/unit.t
Modified:
Catalyst-Controller-RHTMLO/trunk/lib/Catalyst/Controller/RHTMLO.pm
Catalyst-Controller-RHTMLO/trunk/lib/Catalyst/Controller/RHTMLO/Action.pm
Log:
First attempts at tests
Modified: Catalyst-Controller-RHTMLO/trunk/lib/Catalyst/Controller/RHTMLO/Action.pm
===================================================================
--- Catalyst-Controller-RHTMLO/trunk/lib/Catalyst/Controller/RHTMLO/Action.pm 2009-02-24 19:46:15 UTC (rev 9395)
+++ Catalyst-Controller-RHTMLO/trunk/lib/Catalyst/Controller/RHTMLO/Action.pm 2009-02-24 22:11:53 UTC (rev 9396)
@@ -7,7 +7,7 @@
use MRO::Compat;
use Catalyst::Utils;
-__PACKAGE__->mk_accessors(qw/_config/);
+__PACKAGE__->mk_accessors(qw/stash_name stash_hash form_classes/);
sub execute {
my $self = shift;
@@ -20,20 +20,19 @@
sub _setup_forms {
my ( $self, $c ) = @_;
- my $cfg = $self->_config;
- while( my ($name, $class) = each %{$cfg->{form_classes}} ) {
+ while ( my ( $name, $class ) = each %{ $self->form_classes } ) {
# setup form
$c->log->debug("Loading form '$class' as '$name'");
- my $form = $class->new();
+ my $form = $class->new( app => $c );
$form->params( $c->req->params );
$form->init_fields;
# put form in stash (first or only)
- $c->stash->{ $cfg->{stash_name} } ||= $form;
+ $c->stash->{ $self->stash_name } ||= $form;
# put form in stash under its name, in case of multiple forms
- $c->stash->{ $cfg->{stash_hash_name} }->{$name} = $form;
+ $c->stash->{ $self->stash_hash }->{$name} = $form;
}
return;
Modified: Catalyst-Controller-RHTMLO/trunk/lib/Catalyst/Controller/RHTMLO.pm
===================================================================
--- Catalyst-Controller-RHTMLO/trunk/lib/Catalyst/Controller/RHTMLO.pm 2009-02-24 19:46:15 UTC (rev 9395)
+++ Catalyst-Controller-RHTMLO/trunk/lib/Catalyst/Controller/RHTMLO.pm 2009-02-24 22:11:53 UTC (rev 9396)
@@ -9,14 +9,15 @@
=head1 NAME
-Catalyst::Controller::RHTMLO - Catalyst Base Controller for Rose::HTML::Objects forms
+Catalyst::Controller::RHTMLO - Catalyst base controller for integrating
+Rose::HTML::Form objects
=head1 SYNOPSIS
package MyApp::Controller::Books;
use base 'Catalyst::Controller::RHTMLO';
- # loads MyApp::Form::Book (which isa Rose::HTML::Form)
+ # loads MyApp::Form::Book, which should ->isa('Rose::HTML::Form')
sub edit : Local Form('Book') {
my ( $self, $c ) = @_;
@@ -33,8 +34,8 @@
}
}
- # display two search forms on same page
- sub search : Local Form('ByAuthor,ByTitle') {
+ # display several search forms on same page
+ sub search : Local Form('ByAuthor') Form('ByTitle') {
my ( $self, $c ) = @_;
if ( $c->stash->{forms}->{ByAuthor}->was_submitted ) {
@@ -47,18 +48,19 @@
=head1 DESCRIPTION
-This base controller glues Catalyst actions to form classes derived from
-L<Rose::HTML::Form>, a component of John Siracusa's excellent L<Rose::Object>
+This base controller glues L<Catalyst> actions to form classes derived from
+L<Rose::HTML::Form>, a component of John Siracusa's burgeoning L<Rose>
framework. Unlike some other form-loading modules (see L</"PRIOR ART">), this
one does not include any mechanism for defining form structures; it merely
loads, instantiates, and initializes pre-written form classes for use in your
controllers.
-In order to utilize a particular form in a particular Catalyst action, simply declare an attribute on the subroutine:
+In order to utilize a particular form in a particular Catalyst action, simply
+declare an attribute on the subroutine:
sub edit : Local Form('Book') { }
-This will ensure that MyApp::Form::Book is loaded and initialized, basically
+This will ensure that C<MyApp::Form::Book> is loaded and initialized, basically
equivalent to the following:
my $form = MyApp::Form::Book->new();
@@ -72,10 +74,10 @@
sub edit : Local Form('+My::FormClasses::Book') { }
-To display more than one distinct form on a page, just list them all in the
-attribute, delimited with commas or spaces:
+To utilize more than one distinct form class in the same action, simply declare
+additional attributes:
- sub search : Local Form('ByAuthor,ByTitle,BySubject') {
+ sub search : Local Form('ByAuthor') Form('ByTitle') Form('BySubject') {
my ($self, $c) = @_;
$c->stash->{forms}{ByAuthor}->action($c->uri_for('/search/byauthor'));
@@ -83,46 +85,39 @@
$c->stash->{forms}{BySubject}->name('bytopic');
}
-(Note that you must put all the form names inside one set of quotes; I<DO NOT>
-try to quote each individual form name. This is a limitation of perl5's
-subroutine attributes.) The first form listed will still be stored in the stash
-in the usual location; I<all> the forms (including the first) will be stored
-under a separate stash key, in a hash keyed to the name used to load them.
+The first form listed will be stored in the stash in the usual location; I<all>
+the forms (including the first) will be stored under a separate
+(L<configurable|/CONFIGURATION>) stash key, in a hash keyed to the name used to
+load them.
-If for some reason you need to render the same form more than once on a page,
-just list it again:
-
- sub search : Local Form('Search,Search') {
- my ($self, $c) = @_;
-
- $c->stash->{forms}{Search}[0]->name('search_0');
- $c->stash->{forms}{Search}[1]->name('search_1');
- }
-
-In this (weird but possible) case, the forms will be put into an arrayref in
-the expected location. I haven't actually attempted to use this technique in
-production, so I don't know what else you might have to do to make it work...
-
=head1 CONFIGURATION
You can override many defaults using Catalyst's configuration mechanism:
__PACKAGE__->config(
- 'Controller::RHTMLO' => {
- form_attr => 'HasForm',
- action_class => 'MyApp::Action::RoseForm',
- stash_name => 'formobj',
- stash_hash_name => 'allforms',
- form_prefix => 'MyApp::RoseForm',
- }
+ # settings for all controllers using this base
+ 'Catalyst::Controller::RHTMLO' => {
+ form_attr => 'HasForm',
+ form_action_class => 'MyApp::Action::RoseForm',
+ form_stash_name => 'formobj',
+ form_stash_hash => 'allforms',
+ form_prefix => 'MyApp::RoseForm',
+ },
+ # settings for specific controllers in MyApp
+ 'Controller::Foo' => { # or 'C::Foo' if MyApp is built that way
+ form_prefix => 'MyApp::FooForms',
+ },
);
=over
-=item C<action_class> (default 'Catalyst::Controller::RHTMLO::Action')
+=item C<form_action_class>
-If you want to add more functionality to the automatic form loading and initialization, you can create your own action class:
+Default: C<'Catalyst::Controller::RHTMLO::Action'>
+If you want to add more functionality to the automatic form loading and
+initialization, you can create your own custom action class:
+
package MyApp::Action::RoseForm;
use base 'Catalyst::Controller::RHTMLO::Action';
@@ -145,24 +140,31 @@
=item C<form_attr>
-Default: 'Form'. Set this to alter the subroutine attribute used to indicate
-one or more forms to be loaded by a given action, e.g.:
+Default: C<'Form'>
+
+Set this to alter the subroutine attribute used to indicate one or more forms
+to be loaded by a given action, e.g.:
sub edit : Local HasForm('Books') { }
=item C<form_prefix>
-Default: 'MyApp::Form' (using your app's actual name). Set this to the
-namespace where your Rose::HTML::Form subclasses live.
+Default: C<'MyApp::Form'> (using your app's actual name)
-=item C<stash_hash_name>
+Set this to the namespace where your Rose::HTML::Form subclasses live.
-Default: 'forms'. Sets the stash key under which all forms for a given action
-will be stored by class name.
+=item C<stash_hash>
+Default: C<'forms'>
+
+Sets the stash key under which all forms for a given action will be stored,
+keyed according to the name used to load them.
+
=item C<stash_name>
-Default: 'form'. Sets the stash key under which the first form for a given
+Default: C<'form'>
+
+Sets the stash key under which the first (and often I<only>) form for a given
action will be stored.
=head1 PRIOR ART
@@ -175,74 +177,104 @@
=item L<Catalyst::Controller::FormBuilder>
Provided a lot of insight into how to trigger the form loading process with a
-custom subroutine attribute. Based on L<CGI::FormBuilder> rather than
-Rose::HTML::Form.
+custom subroutine attribute and custom action class. Based on
+L<CGI::FormBuilder> (rather than L<Rose::HTML::Form>).
=item L<CatalystX::RoseIntegrator>
-Looks like it uses a CGI::FormBuilder-style config file to construct
-Rose::HTML::Form objects on the fly, rather than having static subclasses. Also
-seems to include direct model integration with L<Rose::DB::Object>.
+Looks like it uses a L<CGI::FormBuilder>-style config file to construct
+L<Rose::HTML::Form> objects on the fly, rather than having static subclasses.
+Also seems to include direct model integration with L<Rose::DB::Object>.
=item L<CatalystX::CRUD::Controller::RHTMLO>
-A component that enables use of Rose::HTML::Form objects with Peter Karman's
+A component that enables use of L<Rose::HTML::Form> objects with Peter Karman's
cool L<CatalystX::CRUD> API.
=back
=head1 SEE ALSO
-L<Rose::HTML::Form>, L<Rose::HTML::Objects>, L<Rose::Object>,
+L<Rose::HTML::Form>, L<Rose::HTML::Objects>, L<Rose>,
L<Catalyst::Controller>, L<Catalyst::Action>, L<Catalyst>
=head1 AUTHOR
Jason Gottshall <jgottshall att capwiz dott com>
+=head1 ACKNOWLEDGEMENTS
+
+mst: for patiently helping me make sense of Catalyst's configuration system
+phaylon: for pointing me to existing documentation on extending components
+
=head1 LICENSE
-This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+This program is free software; you can redistribute it and/or modify it under
+the same terms as Perl itself.
=cut
+__PACKAGE__->mk_accessors(
+ qw/
+ form_attr
+ form_action_class
+ form_stash_name
+ form_stash_hash
+ form_prefix
+ /
+);
+
__PACKAGE__->config(
- 'Controller::RHTMLO' => {
- form_attr => 'Form',
- action_class => 'Catalyst::Controller::RHTMLO::Action',
- stash_name => 'form',
- stash_hash_name => 'forms',
- }
+ form_attr => 'Form',
+ form_action_class => 'Catalyst::Controller::RHTMLO::Action',
+ form_stash_name => 'form',
+ form_stash_hash => 'forms',
);
+sub COMPONENT {
+ my ( $self, $c, $arguments ) = @_;
+
+ # merge global config with controller-specific config
+ #TODO refactor this into Moose role for Catalyst components in general
+ $arguments = $self->merge_config_hashes(
+ $c->config->{'Catalyst::Controller::RHTMLO'},
+ $arguments
+ ) if defined $c->config->{'Catalyst::Controller::RHTMLO'};
+
+ # set default form prefix according to appname
+ $self->config->{form_prefix} = $c->config->{name} . "::Form";
+
+ return $self->next::method( $c, $arguments );
+}
+
sub create_action {
my ( $self, %args ) = @_;
- my $cfg = $self->config->{'Controller::RHTMLO'};
- $cfg->{form_prefix} ||= $self->config->{'name'} . '::Form';
-
- if ( my $formnames = delete $args{attributes}{$cfg->{form_attr}} ) {
+ if ( my $formnames = delete $args{attributes}{ $self->form_attr } ) {
# ensure at least one form name is provided
- @$formnames = grep { $_ } @$formnames;
+ @$formnames = grep {$_} @$formnames;
unless (@$formnames) {
- die "Attribute '$cfg->{form_attr}' for action '$args->{reverse}' missing form name";
+ die sprintf
+ q{Attribute '%s()' for action '/%s' must specify an RHTMLO-based form class},
+ $self->form_attr, $args{reverse};
}
# validate and load form classes
foreach my $name (@$formnames) {
my $class =
- ( $name =~ s/^\+// )
- ? join( '::', $cfg->{form_prefix}, $name )
- : $name;
+ $name =~ s/^\+//
+ ? $name # leading plus indicates fully-qualified package name
+ : join( '::', $self->form_prefix, $name );
Catalyst::Utils::ensure_class_loaded($class);
- $cfg->{form_classes}{$name} = $class;
+ $args{form_classes}{$name} = $class;
}
# pass config to action
- $args{_config} = $cfg;
+ $args{stash_name} = $self->form_stash_name;
+ $args{stash_hash} = $self->form_stash_hash;
# set action class
- push @{ $args{attributes}{ActionClass} }, $cfg->{action_class};
+ push @{ $args{attributes}{ActionClass} }, $self->form_action_class;
}
return $self->next::method(%args);
Added: Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp/C/Forms.pm
===================================================================
--- Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp/C/Forms.pm (rev 0)
+++ Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp/C/Forms.pm 2009-02-24 22:11:53 UTC (rev 9396)
@@ -0,0 +1,16 @@
+package TestApp::C::Forms;
+
+use strict;
+use warnings;
+
+use base 'Catalyst::Controller::RHTMLO';
+
+sub one_form : Global Form('One') { }
+
+sub two_forms : Global Form('One') Form('Two') { }
+
+sub fully_qualified : Global Form('+TestApp::Other::Form') { }
+
+1;
+
+
Added: Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp/C/Root.pm
===================================================================
--- Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp/C/Root.pm (rev 0)
+++ Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp/C/Root.pm 2009-02-24 22:11:53 UTC (rev 9396)
@@ -0,0 +1,25 @@
+package TestApp::C::Root;
+
+use strict;
+use warnings;
+use base 'Catalyst::Controller';
+use Storable qw/freeze/;
+
+__PACKAGE__->config->{namespace} = '';
+
+sub end : Private {
+ my ( $self, $c ) = @_;
+
+ my $s = $c->stash;
+
+ my ($form, $forms) = $s->{expect} ? @{delete $s->{expect}} : qw/form forms/;
+ foreach my $f ( values %{$s->{$forms}} ) {
+ # app is $c, which doesn't freeze well
+ $f->app({});
+ }
+
+ $c->response->body( freeze( $s ) );
+}
+
+1;
+
Added: Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp/Form/One.pm
===================================================================
--- Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp/Form/One.pm (rev 0)
+++ Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp/Form/One.pm 2009-02-24 22:11:53 UTC (rev 9396)
@@ -0,0 +1,7 @@
+package TestApp::Form::One;
+
+use base 'Rose::HTML::Form';
+
+1;
+
+
Added: Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp/Form/Two.pm
===================================================================
--- Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp/Form/Two.pm (rev 0)
+++ Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp/Form/Two.pm 2009-02-24 22:11:53 UTC (rev 9396)
@@ -0,0 +1,7 @@
+package TestApp::Form::Two;
+
+use base 'Rose::HTML::Form';
+
+1;
+
+
Added: Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp.pm
===================================================================
--- Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp.pm (rev 0)
+++ Catalyst-Controller-RHTMLO/trunk/t/lib/TestApp.pm 2009-02-24 22:11:53 UTC (rev 9396)
@@ -0,0 +1,14 @@
+package TestApp;
+
+use strict;
+use warnings;
+
+use Catalyst::Runtime;
+use Catalyst;
+
+our $VERSION = '0.01';
+__PACKAGE__->config( name => 'TestApp' );
+__PACKAGE__->setup;
+
+1;
+
Added: Catalyst-Controller-RHTMLO/trunk/t/unit.t
===================================================================
--- Catalyst-Controller-RHTMLO/trunk/t/unit.t (rev 0)
+++ Catalyst-Controller-RHTMLO/trunk/t/unit.t 2009-02-24 22:11:53 UTC (rev 9396)
@@ -0,0 +1,20 @@
+use strict;
+use warnings;
+
+use Test::More tests => 3; # last test to print
+use Storable qw/thaw/;
+
+# setup library path
+use FindBin qw($Bin);
+use lib "$Bin/lib";
+use Catalyst::Test 'TestApp';
+
+# 1
+use_ok "Catalyst::Controller::RHTMLO";
+
+# 2
+ok( request('/one_form')->is_success, 'can request one form' );
+my $one_form = thaw( get( '/one_form' ) );
+# 3
+isa_ok( $one_form->{form}, 'Rose::HTML::Form', 'form' );
+
More information about the Catalyst-commits
mailing list