[Catalyst-commits] r9395 - in Catalyst-Controller-RHTMLO/trunk: .
lib lib/Catalyst lib/Catalyst/Controller
lib/Catalyst/Controller/RHTMLO
jgottshall at dev.catalyst.perl.org
jgottshall at dev.catalyst.perl.org
Tue Feb 24 19:46:16 GMT 2009
Author: jgottshall
Date: 2009-02-24 19:46:15 +0000 (Tue, 24 Feb 2009)
New Revision: 9395
Added:
Catalyst-Controller-RHTMLO/trunk/lib/
Catalyst-Controller-RHTMLO/trunk/lib/Catalyst/
Catalyst-Controller-RHTMLO/trunk/lib/Catalyst/Controller/
Catalyst-Controller-RHTMLO/trunk/lib/Catalyst/Controller/RHTMLO.pm
Catalyst-Controller-RHTMLO/trunk/lib/Catalyst/Controller/RHTMLO/
Catalyst-Controller-RHTMLO/trunk/lib/Catalyst/Controller/RHTMLO/Action.pm
Log:
Initial import of rough draft code
Added: Catalyst-Controller-RHTMLO/trunk/lib/Catalyst/Controller/RHTMLO/Action.pm
===================================================================
--- Catalyst-Controller-RHTMLO/trunk/lib/Catalyst/Controller/RHTMLO/Action.pm (rev 0)
+++ Catalyst-Controller-RHTMLO/trunk/lib/Catalyst/Controller/RHTMLO/Action.pm 2009-02-24 19:46:15 UTC (rev 9395)
@@ -0,0 +1,42 @@
+package Catalyst::Controller::RHTMLO::Action;
+
+use strict;
+use warnings;
+
+use base 'Catalyst::Action';
+use MRO::Compat;
+use Catalyst::Utils;
+
+__PACKAGE__->mk_accessors(qw/_config/);
+
+sub execute {
+ my $self = shift;
+ my ( $controller, $c, @args ) = @_;
+
+ $self->_setup_forms($c);
+
+ return $self->next::method(@_);
+}
+
+sub _setup_forms {
+ my ( $self, $c ) = @_;
+ my $cfg = $self->_config;
+
+ while( my ($name, $class) = each %{$cfg->{form_classes}} ) {
+ # setup form
+ $c->log->debug("Loading form '$class' as '$name'");
+ my $form = $class->new();
+ $form->params( $c->req->params );
+ $form->init_fields;
+
+ # put form in stash (first or only)
+ $c->stash->{ $cfg->{stash_name} } ||= $form;
+
+ # put form in stash under its name, in case of multiple forms
+ $c->stash->{ $cfg->{stash_hash_name} }->{$name} = $form;
+ }
+
+ return;
+}
+
+1;
Added: Catalyst-Controller-RHTMLO/trunk/lib/Catalyst/Controller/RHTMLO.pm
===================================================================
--- Catalyst-Controller-RHTMLO/trunk/lib/Catalyst/Controller/RHTMLO.pm (rev 0)
+++ Catalyst-Controller-RHTMLO/trunk/lib/Catalyst/Controller/RHTMLO.pm 2009-02-24 19:46:15 UTC (rev 9395)
@@ -0,0 +1,251 @@
+package Catalyst::Controller::RHTMLO;
+
+use strict;
+use warnings;
+
+use base 'Catalyst::Controller';
+use MRO::Compat; # to get $self->next::method() right
+use Catalyst::Utils;
+
+=head1 NAME
+
+Catalyst::Controller::RHTMLO - Catalyst Base Controller for Rose::HTML::Objects forms
+
+=head1 SYNOPSIS
+
+ package MyApp::Controller::Books;
+ use base 'Catalyst::Controller::RHTMLO';
+
+ # loads MyApp::Form::Book (which isa Rose::HTML::Form)
+ sub edit : Local Form('Book') {
+ my ( $self, $c ) = @_;
+
+ # form object is already init'ed with params and stashed
+ my $form = $c->stash->{form};
+
+ if ( $form->was_submitted ) {
+ if ( $form->validate ) {
+ # write to db or whatever
+ }
+ else {
+ # show errors or whatever
+ }
+ }
+ }
+
+ # display two search forms on same page
+ sub search : Local Form('ByAuthor,ByTitle') {
+ my ( $self, $c ) = @_;
+
+ if ( $c->stash->{forms}->{ByAuthor}->was_submitted ) {
+ # look up books by author
+ }
+ elsif { $c->stash->{forms}->{ByTitle}->was_submitted ) {
+ # look up books by title, duh
+ }
+ }
+
+=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>
+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:
+
+ sub edit : Local Form('Book') { }
+
+This will ensure that MyApp::Form::Book is loaded and initialized, basically
+equivalent to the following:
+
+ my $form = MyApp::Form::Book->new();
+ $form->params($c->req->params);
+ $form->init_fields;
+ $c->stash->{form} = $form;
+
+The namespace used to complete the form class name is
+L<configurable|/CONFIGURATION>, or you can specify a full package name by
+prepending a 'plus' sign:
+
+ 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:
+
+ sub search : Local Form('ByAuthor,ByTitle,BySubject') {
+ my ($self, $c) = @_;
+
+ $c->stash->{forms}{ByAuthor}->action($c->uri_for('/search/byauthor'));
+ $c->stash->{forms}{ByTitle}->method('GET');
+ $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.
+
+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',
+ }
+ );
+
+=over
+
+=item C<action_class> (default 'Catalyst::Controller::RHTMLO::Action')
+
+If you want to add more functionality to the automatic form loading and initialization, you can create your own action class:
+
+ package MyApp::Action::RoseForm;
+ use base 'Catalyst::Controller::RHTMLO::Action';
+
+ sub execute {
+ my $self = shift;
+ my ($controller, $c, @args) = @_;
+
+ # load forms via base class
+ $self->next::method(@_);
+
+ # do cool stuff
+ $c->stash->{form}->add_fields(
+ secure_token => {
+ type => 'hidden',
+ value => $c->some_cool_security_token
+ }
+ );
+ return;
+ }
+
+=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.:
+
+ 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.
+
+=item C<stash_hash_name>
+
+Default: 'forms'. Sets the stash key under which all forms for a given action
+will be stored by class name.
+
+=item C<stash_name>
+
+Default: 'form'. Sets the stash key under which the first form for a given
+action will be stored.
+
+=head1 PRIOR ART
+
+There are several other modules on CPAN that do similar things, many having
+inspired this module in various ways.
+
+=over
+
+=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.
+
+=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>.
+
+=item L<CatalystX::CRUD::Controller::RHTMLO>
+
+A component that enables use of 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<Catalyst::Controller>, L<Catalyst::Action>, L<Catalyst>
+
+=head1 AUTHOR
+
+Jason Gottshall <jgottshall att capwiz dott com>
+
+=head1 LICENSE
+
+This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->config(
+ 'Controller::RHTMLO' => {
+ form_attr => 'Form',
+ action_class => 'Catalyst::Controller::RHTMLO::Action',
+ stash_name => 'form',
+ stash_hash_name => 'forms',
+ }
+);
+
+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}} ) {
+ # ensure at least one form name is provided
+ @$formnames = grep { $_ } @$formnames;
+ unless (@$formnames) {
+ die "Attribute '$cfg->{form_attr}' for action '$args->{reverse}' missing form name";
+ }
+
+ # validate and load form classes
+ foreach my $name (@$formnames) {
+ my $class =
+ ( $name =~ s/^\+// )
+ ? join( '::', $cfg->{form_prefix}, $name )
+ : $name;
+ Catalyst::Utils::ensure_class_loaded($class);
+ $cfg->{form_classes}{$name} = $class;
+ }
+
+ # pass config to action
+ $args{_config} = $cfg;
+
+ # set action class
+ push @{ $args{attributes}{ActionClass} }, $cfg->{action_class};
+ }
+
+ return $self->next::method(%args);
+}
+
+1;
More information about the Catalyst-commits
mailing list