Re: [Catalyst] Making Controllers thin and Models thick

2007-07-18 Thread Zbigniew Lukasiak

Hi there,

I am too working on a thick model component.  It's a ResultSet base
class with functions bearing (provisional?) names: build_create and
build_update.  They do params validation and update the database in a
transaction with data for both the individual record and related
stuff.

In short the CRUD controller actions using it can be as simple as:

sub update : Local {
   my ( $self, $c, $id ) = @_;
   if ( $c-request-method eq 'POST' ){
   my $update_result = $c-model( 'DB::Table' )-build_update(
$id, $c-request-params() );
   if ( $update_result-{status} eq 'OK' ){
   $c-res-redirect( $c-uri_for( table/view, $id) );
   }else{
   $c-stash( update_result = $update_result );
  }
   }else{
   $c-stash( item = $c-model( 'DB::Table' )-find( $id ) );
   }
}

Conforming to the rule about redirecting after a succesfull POST.

For validation it uses Data::FormValidator.

Some day I plan publish it to CPAN.  It's not too polished yet - but
if someone is interested I can put it on some public place (if my
employer agrees).

--
Zbyszek

On 7/17/07, Kee Hinckley [EMAIL PROTECTED] wrote:

[Was: Re: [Catalyst] Command-line utility using Controller - examples?]

On Jul 17, 2007, at 9:21 AM, Ronald J Kimball wrote:
 (The Controller in this case is probably not as thin as it should
 be. :)

The issue I had is that Catalyst makes it very easy to do the
database part of the Model, but it's not immediately obvious where to
put the rest of the business logic.  So it ends up going in the
Controller.

I finally resolved that by taking advantage of the ability to specify
the return class of the ResultSet.  That allows me to create a custom
class for each table (along with a common super-class for generic and
cross-table stuff).  The Controller has easy access to the ResultSet
class, and the ResultSet class has easy access to the database, so it
seemed like an ideal place to put the heavy logic.

The outline follows below.  I'd be interested in what people think of
it, since this is my first Catalyst app.

--
#
# Define the database portion of the Model
#
package Somewhere::Schema::MDB::Persona;
use base qw/Somewhere::Schema::Super::Class/;
__PACKAGE__-load_components(qw/ UTF8Columns Core /);
__PACKAGE__-table('persona', extra = { mysql_charset = 'utf8' });
__PACKAGE__-add_columns(
  ...
#
# Note that if you are defining the DB here, it's handy to have some
helper routines for common types like
# boolean and foreign-key
#
...
#
# Now the critical part.  Specify that the result set returned for
this table has the following class.
#
__PACKAGE__-resultset_class(__PACKAGE__ . '::ResultSet');


#
# Now define the business logic of the Model,
#
# This could be in a separate file, but it seemed to make sense to
keep it here, so I just keep right on going.
# That makes it real easy to update the biz-logic if the db changes.
#
package Somewhere::Schema::MDB::Persona::ResultSet;
use base 'Somewhere::Schema::Super::ResultSet';
use Error qw(:try);
use Error::Throws;
use Somewhere::Util;
use NEXT;
throws ::Error::InUse, -format = 'The [_1] [_2] is already in use.';



Now I can add helper methods to be called by the Controller.  And
since they are attached to the ResultSet, they have all the necessary
context to do their work.  It should never be necessary to pass $c to
them, since that would break their independence. In addition, I can
override update/insert to add validation.  Because really the
Controller should only be doing validation specific to the View--
things like checking to make sure that both of the password fields
match when the user enters their initial password.  Checking for
things like field length and correct types of data belong in the Model.

Another way to look at it is to consider any non-web utility programs
you write.  Presumably you can't trust them not to make mistakes
either--but they won't be calling the controller, so  In fact, I
define a separate validate function in the Model for each table.  The
Controller calls it before calling the actual create/update
operations.  By default it continues checking everything even if it
gets an error, and then it returns a list of errors.  Each error is
flagged with the field or fields it applies to.  So the Controller
can pass the error objects back to the View, which then uses the
field info to highlight the appropriate form fields.  If the
validation routine returns no errors, then the create/update
operation is called.  It calls the validate operation *again* (don't
rely on the caller doing the right thing!), but this time telling it
to throw an exception as soon as it sees any error.

Also on the trust side, it's the Controller's responsibility to
ensure that only the appropriate arguments are passed to the Model.
No passing every single variable given by the web browser to the
view.  Aside from security issues, this also provides an opportunity
to remap argument names if necessary (E.g. db changes, ui 

Re: [Catalyst] Making Controllers thin and Models thick

2007-07-18 Thread Bill Moseley
On Wed, Jul 18, 2007 at 09:39:25AM +, Zbigniew Lukasiak wrote:
 Hi there,
 
 I am too working on a thick model component.  It's a ResultSet base
 class with functions bearing (provisional?) names: build_create and
 build_update.  They do params validation and update the database in a
 transaction with data for both the individual record and related
 stuff.

Sounds like both you and Ken are pushing the validation into the
model -- or rather the ORM layer.  The model needs to do some
validation, of course, and I like to push that into the RDBMS where
possible.

I do wonder if that's not coupling the ORM and the validation too
closely.  Not that it happens very often, but if you decided to change
ORMs you would would still want the same validation of input data.

I tend to have a separate layer between the controller and the model
for validation.

 In short the CRUD controller actions using it can be as simple as:
 
 sub update : Local {
my ( $self, $c, $id ) = @_;
if ( $c-request-method eq 'POST' ){
my $update_result = $c-model( 'DB::Table' )-build_update(
 $id, $c-request-params() );
if ( $update_result-{status} eq 'OK' ){
$c-res-redirect( $c-uri_for( table/view, $id) );
}else{
$c-stash( update_result = $update_result );
   }
}else{
$c-stash( item = $c-model( 'DB::Table' )-find( $id ) );
}
 }


I suspect you write that enough times and you will notice the common
code.

My Create/Update controllers tend to look like this:

sub edit : Local {
my ( $self, $c, $id ) = @_;

$c-post_redirect( 'list', 'Foo was updated' )
if $c-update_from_from( $id );

}

Because update_from_form knows the action path and can therefore find
the associated form.  It can also determine what the concept of a
posted form is in a single place.  And the associated form knows what
model class to update or create a row in.  And the model class knows
how to validate ids.  And the form knows how to look at the model
class and determine lookup values for one-to-many and many-to-many
relationships.

I might validate $id a bit more to make sure the current user can
access it, but that can also happen in a chain or auto method.  Or
that might be considered part of the validation and the form module
can validate that the user can access $id.  But, I kind of consider
that more of a controller responsibility since an invalid $id in the
path for a given user is an invalid path (and thus maybe a 404).

-- 
Bill Moseley
[EMAIL PROTECTED]


___
List: Catalyst@lists.rawmode.org
Listinfo: http://lists.rawmode.org/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.rawmode.org/
Dev site: http://dev.catalyst.perl.org/


Re: [Catalyst] Making Controllers thin and Models thick

2007-07-18 Thread Pedro Melo

Hi,

On Jul 18, 2007, at 1:07 PM, Bill Moseley wrote:


On Wed, Jul 18, 2007 at 09:39:25AM +, Zbigniew Lukasiak wrote:

Hi there,

I am too working on a thick model component.  It's a ResultSet base
class with functions bearing (provisional?) names: build_create and
build_update.  They do params validation and update the database in a
transaction with data for both the individual record and related
stuff.


Sounds like both you and Ken are pushing the validation into the
model -- or rather the ORM layer.  The model needs to do some
validation, of course, and I like to push that into the RDBMS where
possible.


there is another view: keep everything in the model and treat your  
RDBMS as a simple data-store.


This is helpful in situations where your validations are complex or  
heavy, and its easier to scale the server farm that runs your models,  
than to scale the server farms that run your RDBMs.


Best regards,
--
Pedro Melo
Blog: http://www.simplicidade.org/notes/
XMPP ID: [EMAIL PROTECTED]
Use XMPP!



___
List: Catalyst@lists.rawmode.org
Listinfo: http://lists.rawmode.org/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.rawmode.org/
Dev site: http://dev.catalyst.perl.org/


Re: [Catalyst] Making Controllers thin and Models thick

2007-07-18 Thread John Napiorkowski

--- Bill Moseley [EMAIL PROTECTED] wrote:

 On Wed, Jul 18, 2007 at 09:39:25AM +, Zbigniew
 Lukasiak wrote:
  Hi there,
  
  I am too working on a thick model component.  It's
 a ResultSet base
  class with functions bearing (provisional?) names:
 build_create and
  build_update.  They do params validation and
 update the database in a
  transaction with data for both the individual
 record and related
  stuff.
 
 Sounds like both you and Ken are pushing the
 validation into the
 model -- or rather the ORM layer.  The model needs
 to do some
 validation, of course, and I like to push that into
 the RDBMS where
 possible.
 
 I do wonder if that's not coupling the ORM and the
 validation too
 closely.  Not that it happens very often, but if you
 decided to change
 ORMs you would would still want the same validation
 of input data.
 
 I tend to have a separate layer between the
 controller and the model
 for validation.
 
  In short the CRUD controller actions using it can
 be as simple as:
  
  sub update : Local {
 my ( $self, $c, $id ) = @_;
 if ( $c-request-method eq 'POST' ){
 my $update_result = $c-model( 'DB::Table'
 )-build_update(
  $id, $c-request-params() );
 if ( $update_result-{status} eq 'OK' ){
 $c-res-redirect( $c-uri_for(
 table/view, $id) );
 }else{
 $c-stash( update_result =
 $update_result );
}
 }else{
 $c-stash( item = $c-model( 'DB::Table'
 )-find( $id ) );
 }
  }
 
 
 I suspect you write that enough times and you will
 notice the common
 code.
 
 My Create/Update controllers tend to look like this:
 
 sub edit : Local {
 my ( $self, $c, $id ) = @_;
 
 $c-post_redirect( 'list', 'Foo was updated'
 )
 if $c-update_from_from( $id );
 
 }
 
 Because update_from_form knows the action path and
 can therefore find
 the associated form.  It can also determine what the
 concept of a
 posted form is in a single place.  And the
 associated form knows what
 model class to update or create a row in.  And the
 model class knows
 how to validate ids.  And the form knows how to look
 at the model
 class and determine lookup values for one-to-many
 and many-to-many
 relationships.
 
 I might validate $id a bit more to make sure the
 current user can
 access it, but that can also happen in a chain or
 auto method.  Or
 that might be considered part of the validation and
 the form module
 can validate that the user can access $id.  But, I
 kind of consider
 that more of a controller responsibility since an
 invalid $id in the
 path for a given user is an invalid path (and thus
 maybe a 404).
 
 -- 
 Bill Moseley
 [EMAIL PROTECTED]
 

I've come to the reluctant conclusion that for
anything other than trivial applications you will need
to validate in a couple of places.  Obviously the
database should be properly designed to enforce
integrity rules.  If you are using a database that
let's you create custom types, like Postgresql, you
can take it a bit further and actually create self
validating types for your columns.  I do this for very
common things like email addresses.  Postgresql is
nice for this since you can create custom types and
domains using Perl as your procedural language.

I end up mirroring a lot of this in DBIC using
DBIC::Validate since I'd rather catch syntactical
errors in my code instead of throwing a database
error.  For me that's the last line of defense.

Validation in the business logic is a different beast
since here you are enforcing not just types value but
actual business rules.  Although in theory you can do
all of this in the database or at the ORM level it's
usually best not to since your business logic is
actually trying to capture the full domain of activity
for your system, and this is hard to do with tables
and triggers.  Not impossible, just can be messy and
not too flexible.

I tend to think of this as 'spell checking versus
grammar checking'.  Your domain code is the grammar
for a particular business activity.  At least I think
of it that way.

At the User Interface level you also have checking
that has it's own needs.  For example you might have a
business process to create a new account for a user. 
At the UI level you ask for a new password and to
repeat the password in another field to verify that
you typed it correctly.  For me that is validation for
a user interface model and doesn't belong in the
domain logic, ORM or in the controller.  This is where
reading the code for the Reaction project has really
helped me to think about this.  

If this sounds like validation is scattered all over
your code, it doesn't have to be.  Because validation
tends to be a lot of configuration files stuff that
you can centralize if you want.  Data::Formvalidator
can cover a lot of this, since you can use it in DBIC
and has decent integration with Template Toolkit if
you are using that for your presentation layer.

--john



   

Re: [Catalyst] Making Controllers thin and Models thick

2007-07-18 Thread Bill Moseley
On Wed, Jul 18, 2007 at 07:49:50AM -0700, John Napiorkowski wrote:
 I've come to the reluctant conclusion that for
 anything other than trivial applications you will need
 to validate in a couple of places.  Obviously the
 database should be properly designed to enforce
 integrity rules.  If you are using a database that
 let's you create custom types, like Postgresql, you
 can take it a bit further and actually create self
 validating types for your columns.  I do this for very
 common things like email addresses.  Postgresql is
 nice for this since you can create custom types and
 domains using Perl as your procedural language.

Yes, validation is a bit generic of a term.

For me, the database should try and enforce a valid state of
the application.  An order row better reference a customer row.
An order status better reference a valid value in the status table or
have a valid check constraint.

Doesn't mean a bit of raw DBI can't hose the application, of course.

For application state changes I try and abstract that into a method in
the model.  I rarely have ORM specific code in the controller.

I tend to not validate things like email addresses or phone numbers at
the RDBMS level -- it's not critical to the state of the application
typically.  That's left to an I/O layer that is my user input
validation code.  That code can be used outside of Catalyst -- and
as I commented before, it's not tied to the model/ORM either.

Application state changes are typically caused by user input. And user
input is often multiple fields.  So, it makes sense to defined
forms for handling a set of fields all as one unit.  That validation
is also not easily defined in a static configuration file (or by
constraints on the database) so it also makes sense the forms are
bundled with code.

I doubt there's any best approach.  But, if the idea is thin
controllers then I like using one line of code. ;)

-- 
Bill Moseley
[EMAIL PROTECTED]


___
List: Catalyst@lists.rawmode.org
Listinfo: http://lists.rawmode.org/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.rawmode.org/
Dev site: http://dev.catalyst.perl.org/


Re: [Catalyst] Making Controllers thin and Models thick

2007-07-18 Thread Kee Hinckley

On Jul 18, 2007, at 10:49 AM, John Napiorkowski wrote:

validating types for your columns.  I do this for very
common things like email addresses.  Postgresql is
nice for this since you can create custom types and
domains using Perl as your procedural language.


pet-peeve
Please tell me you're either doing *real* email validation (e.g.  
Mail::Address), or something overly lax..  Aside from sites that  
won't let me enter valid email addresses (keep in mind that just  
about the only character not legal in an email address is NUL).  I've  
even had sites who updated their validation such that people who  
previously had accounts could no longer login.  (+ is the really  
common one to get rejected, but I had a friend who had his email  
rejected once because it had a - in the domain name!)


My rule of thumb on email validation is don't bother.  Or rather,  
if you really care, send mail to it and ask them to respond.  After  
all, just because it's valid doesn't mean it *works*.  I think a lot  
of sites do it just because.  Originally it was done because people  
kept making mistakes.  Of course the most of the mistakes were  
intentional attempts to avoid getting spammed (somewhere.com was  
*real* popular for fake addresses).


If you're just checking for typos /[EMAIL PROTECTED]/ is probably  
sufficient.  (Assuming of course that you don't want me to enter  
somewhere.com!nazgul as my email address, even though it might well  
work, and is certainly legal.)

/pet-peeve


I end up mirroring a lot of this in DBIC using
DBIC::Validate since I'd rather catch syntactical
errors in my code instead of throwing a database
error.  For me that's the last line of defense.


Are you referring to DBIx::Class::Validation, or is there another  
module.  I looked at that, but it didn't seem to make any use of the  
actual schema data.



I tend to think of this as 'spell checking versus
grammar checking'.  Your domain code is the grammar
for a particular business activity.  At least I think
of it that way.


That's a good metaphor.

___
List: Catalyst@lists.rawmode.org
Listinfo: http://lists.rawmode.org/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.rawmode.org/
Dev site: http://dev.catalyst.perl.org/


Re: [Catalyst] Making Controllers thin and Models thick

2007-07-18 Thread Dave Rolsky

On Wed, 18 Jul 2007, Kee Hinckley wrote:


2. Biz-logic
Things you've specified for you application.  E.g. We need an email address 
for all new accounts


3. DB
Constraints specific to the database and object model.  E.g. Field length, 
required fields.


The distinction between these two is pretty artificial. To give a simple 
example, think of most foreign key uses. If I have a table Post and a 
table User, and a foreign key such that Post must have a valid user_id 
from User, that's business logic. It says that posts must be associated 
with a valid user.


If SQL DBMS products didn't suck quite so much, I'd be able to say posts 
must be associated with user who is not marked as deleted.


Basically, your distinction for #3 seems to be stuff I can trivially 
define with the SQL implementation I happen to be using.


Having this sort of stuff in the DBMS is _in theory_ a good thing, as it 
actually ensures that the data you store is only valid data, according to 
the rules you've defined.


Unfortunately, in practice, doing this becomes very cumbersome. My take on 
this is that it's the lack of sophisticated declarative constraints that 
makes this so. Using triggers and stored procs is a bit gross, and I think 
many developers perceive it as too opaque.


Another problem is that translating the errors the DBMS gives you into 
something user-friendly can be a pain, and there's not a good standardized 
way of passing DBMS-side validation errors back to Perl code, so you get a 
string and have to parse it, which feels horribly fragile. Also, that the 
DBMS will generally blow up on the first constraint that's violated, 
rather than giving you a set of errors (first name is required, 
password must be 6 characters).


Personally, I'd much rather define my business logic using a rich 
declarative constraint language rather than in app code. As mentioned in 
another message, Pg does make this a little easier, since declaring custom 
types based on built-ins isn't really hard, so you can have a type like 
email_address, and say that it's a string which matches [EMAIL PROTECTED], 
which is a small step towards declarative business logic constraints.



-dave

/*===
VegGuide.Orgwww.BookIRead.com
Your guide to all that's veg.   My book blog
===*/

___
List: Catalyst@lists.rawmode.org
Listinfo: http://lists.rawmode.org/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.rawmode.org/
Dev site: http://dev.catalyst.perl.org/


Re: [Catalyst] Making Controllers thin and Models thick

2007-07-18 Thread John Napiorkowski

--- Kee Hinckley [EMAIL PROTECTED] wrote:

 On Jul 18, 2007, at 10:49 AM, John Napiorkowski
 wrote:
  validating types for your columns.  I do this for
 very
  common things like email addresses.  Postgresql is
  nice for this since you can create custom types
 and
  domains using Perl as your procedural language.
 
 pet-peeve
 Please tell me you're either doing *real* email
 validation (e.g.  
 Mail::Address), or something overly lax..  Aside
 from sites that  
 won't let me enter valid email addresses (keep in
 mind that just  
 about the only character not legal in an email
 address is NUL).  I've  
 even had sites who updated their validation such
 that people who  
 previously had accounts could no longer login.  (+
 is the really  
 common one to get rejected, but I had a friend who
 had his email  
 rejected once because it had a - in the domain
 name!)
 
 My rule of thumb on email validation is don't
 bother.  Or rather,  
 if you really care, send mail to it and ask them to
 respond.  After  
 all, just because it's valid doesn't mean it
 *works*.  I think a lot  
 of sites do it just because.  Originally it was
 done because people  
 kept making mistakes.  Of course the most of the
 mistakes were  
 intentional attempts to avoid getting spammed
 (somewhere.com was  
 *real* popular for fake addresses).
 
 If you're just checking for typos /[EMAIL PROTECTED]/ is
 probably  
 sufficient.  (Assuming of course that you don't want
 me to enter  
 somewhere.com!nazgul as my email address, even
 though it might well  
 work, and is certainly legal.)
 /pet-peeve

I use a standard perl module for this which is part of
a custom function and domain in Postgresql.  Since we
send the user an email with a validation token to
activate an account, doing strict email testing cuts
down on the number of bad emails that get bounced
back.

I also hate those forms that ask for an email for no
reason, other than they want you in their database.

 
  I end up mirroring a lot of this in DBIC using
  DBIC::Validate since I'd rather catch syntactical
  errors in my code instead of throwing a database
  error.  For me that's the last line of defense.
 
 Are you referring to DBIx::Class::Validation, or is
 there another  
 module.  I looked at that, but it didn't seem to
 make any use of the  
 actual schema data.

Doh, yeah, I always get the name wrong.  You'd think
I'd remember since I used it so much on my last
project.


 
  I tend to think of this as 'spell checking versus
  grammar checking'.  Your domain code is the
 grammar
  for a particular business activity.  At least I
 think
  of it that way.
 
 That's a good metaphor.
 
 ___
 List: Catalyst@lists.rawmode.org
 Listinfo:
 http://lists.rawmode.org/mailman/listinfo/catalyst
 Searchable archive:

http://www.mail-archive.com/catalyst@lists.rawmode.org/
 Dev site: http://dev.catalyst.perl.org/
 



  

Luggage? GPS? Comic books? 
Check out fitting gifts for grads at Yahoo! Search
http://search.yahoo.com/search?fr=oni_on_mailp=graduation+giftscs=bz

___
List: Catalyst@lists.rawmode.org
Listinfo: http://lists.rawmode.org/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/catalyst@lists.rawmode.org/
Dev site: http://dev.catalyst.perl.org/


[Catalyst] Making Controllers thin and Models thick

2007-07-17 Thread Kee Hinckley

[Was: Re: [Catalyst] Command-line utility using Controller - examples?]

On Jul 17, 2007, at 9:21 AM, Ronald J Kimball wrote:
(The Controller in this case is probably not as thin as it should  
be. :)


The issue I had is that Catalyst makes it very easy to do the  
database part of the Model, but it's not immediately obvious where to  
put the rest of the business logic.  So it ends up going in the  
Controller.


I finally resolved that by taking advantage of the ability to specify  
the return class of the ResultSet.  That allows me to create a custom  
class for each table (along with a common super-class for generic and  
cross-table stuff).  The Controller has easy access to the ResultSet  
class, and the ResultSet class has easy access to the database, so it  
seemed like an ideal place to put the heavy logic.


The outline follows below.  I'd be interested in what people think of  
it, since this is my first Catalyst app.


--
#
# Define the database portion of the Model
#
package Somewhere::Schema::MDB::Persona;
use base qw/Somewhere::Schema::Super::Class/;
__PACKAGE__-load_components(qw/ UTF8Columns Core /);
__PACKAGE__-table('persona', extra = { mysql_charset = 'utf8' });
__PACKAGE__-add_columns(
 ...
#
# Note that if you are defining the DB here, it's handy to have some  
helper routines for common types like

# boolean and foreign-key
#
...
#
# Now the critical part.  Specify that the result set returned for  
this table has the following class.

#
__PACKAGE__-resultset_class(__PACKAGE__ . '::ResultSet');


#
# Now define the business logic of the Model,
#
# This could be in a separate file, but it seemed to make sense to  
keep it here, so I just keep right on going.

# That makes it real easy to update the biz-logic if the db changes.
#
package Somewhere::Schema::MDB::Persona::ResultSet;
use base 'Somewhere::Schema::Super::ResultSet';
use Error qw(:try);
use Error::Throws;
use Somewhere::Util;
use NEXT;
throws ::Error::InUse, -format = 'The [_1] [_2] is already in use.';



Now I can add helper methods to be called by the Controller.  And  
since they are attached to the ResultSet, they have all the necessary  
context to do their work.  It should never be necessary to pass $c to  
them, since that would break their independence. In addition, I can  
override update/insert to add validation.  Because really the  
Controller should only be doing validation specific to the View-- 
things like checking to make sure that both of the password fields  
match when the user enters their initial password.  Checking for  
things like field length and correct types of data belong in the Model.


Another way to look at it is to consider any non-web utility programs  
you write.  Presumably you can't trust them not to make mistakes  
either--but they won't be calling the controller, so  In fact, I  
define a separate validate function in the Model for each table.  The  
Controller calls it before calling the actual create/update  
operations.  By default it continues checking everything even if it  
gets an error, and then it returns a list of errors.  Each error is  
flagged with the field or fields it applies to.  So the Controller  
can pass the error objects back to the View, which then uses the  
field info to highlight the appropriate form fields.  If the  
validation routine returns no errors, then the create/update  
operation is called.  It calls the validate operation *again* (don't  
rely on the caller doing the right thing!), but this time telling it  
to throw an exception as soon as it sees any error.


Also on the trust side, it's the Controller's responsibility to  
ensure that only the appropriate arguments are passed to the Model.   
No passing every single variable given by the web browser to the  
view.  Aside from security issues, this also provides an opportunity  
to remap argument names if necessary (E.g. db changes, ui hasn't).


So here's a piece of Controller code for doing signup for a service.

--
sub signup : Local {
my ($self, $c) = @_;
my (@errors);

# If the form hasn't been submitted yet, just display it
if (!$c-request-params-{'-signup'}) {
$c-template('to/signup.html');
return 1;
}

# Set some shortcut variables
my $params = $c-request-params();
my $acctparams = $params-{account};

# Check to see if the user typed things correctly
if ($acctparams-{email} ne $acctparams-{email2}) {
push(@errors, record Error::DoubleCheck(
-fields = [qw( account.email account.email2 )],
-text   = 'email addresses',
));
}
if ($acctparams-{password} ne $acctparams-{password2}) {
push(@errors, record Error::DoubleCheck(
-fields = [qw( account.password account.password2 )],
-text   = 'passwords',
));
}

#
# All the other error checking needs to be handled at the  
bizlogic level.