[Catalyst] Catalyst design philosophy

Michael Reece mreece at sacbee.com
Sat Apr 9 02:52:11 CEST 2005


i have some questions about general design philosophy with catalyst and
controllers.

i have spent the past week and a half porting my app from maypole to
catalyst 4.

to that end, i took some ideas from C::M::CDBI::CRUD, the Hops example, and
Maypole, and created my own M::CDBI::CRUD class that closely emulates
maypole's action dispatching.

other than login and logout, everything is dispatched through MyApp's
!default action.

  '!default' => sub {
      my ( $self, $c, $table) = @_;

      my $model = $self->model;

      # model->table_re returns qr/^(its|list|of|tables)$/
      if ($table =~ $model->table_re) {
          my $class = $model->loader->find_class($table);

          # eat table from args before forwarding
          shift @{ $c->req->args };
          $c->forward( $class );

          # check for table-specific template
          if (my $t = $c->stash->{template}) {
              if (-r $self->config->{root} . "/$table/$t") {
                  $c->stash->{template} = "$table/$t";
              }
          }
      }
  },


the model is grabbed from:

sub model {
    my ($self) = @_;
    return $self->comp( $self->config->{base_model} );
}


it works, but i'm not totally happy with it.

for one, the action is forwarded to the model class instead of a controller.
the model class's 'sub process' takes care of the rest of the dispacthing:

  sub process {
      my $self    = shift;
      my $c       = shift;
      my $method  = shift || 'list';
      $c->stash->{item}       = $self->retrieve( $_[0] ) if $_[0];
      $c->stash->{template}   = $method;
      $c->stash->{class}      = ref $self || $self;
      $self->$method( $c, @_ ) if $self->can($method);
  }

since all of the table classes get inherited from this model, it is
inherently dangerous since a user can hit /myapp/atable/delete/1
(Class::DBI's delete) rather than /myapp/atable/destroy/1 (my CRUD's
destroy), since it happily calls any available method.  i then override 'sub
MyApp::M::CDBI::ATable::list' etc to perform custom actions.

i suppose a single controller could handle the dispatching for the table
actions, limiting the 'if can($method)' problem, but it seems like i am
still missing out on the pluggability with controllers, especially seeing
the direction Cat5 is taking.  also, how would the controller know what
tables have been loaded, and thus which url patterns to handle, if it is
separated thusly from the model class (that is, without grabbing a reference
to MyApp from its constructor, and going back through
$self->{myapp}->model->loader, and hoping the models got loaded first)?

my question is, how are others doing this sort of thing?  should each table
have its own controller?  or at least those tables that override their
list/destroy/etc methods?

thinking to the future when/if i have to convert this to Cat5 .. it looks
like in Cat5 you can't forward to another controller's default action.  how
would a Cat5 controller know that it should handle /table/action/ for any
table without me creating redundant sub's for each table name or hard-coding
the list of tables into a :Regex action pattern?

all this confusion has me thinking i am approaching this all wrong from the
start, and i would like to get into the proper mindset before i get too much
further.

i welcome any comments, discussion, etc.

thanks.
michael.




More information about the Catalyst mailing list