Jonathan,

I think that since Catalyst is so rapidly developing (and typically developing 
along the direction of the 'itch you need to scratch') some of these questions 
are a bit hard to answer.  Here's what I am doing:

1)  I'd like to see the search path for stuff evolve as well.  I know the M/V/C 
directories are just a starting point and that in the end maybe something else 
will work better for us.  I think having multiple roots for seaching for 
components arranged something like the way perl seperates CPAN libraries could 
help sort things out a lot.

/lib
    /vendor    #ready to go catalyst apps that a vendor provides for you
    /site        #Catalyst stuff that you install from cpan
    /local
        /Controller
       /Model
        /View

Personally I create a directory under /lib for my pluggable modules so I can 
keep the lib root clean.

I feel your pain about needing to split View up a bit.  Axkit and Coccoon have 
this really nice rendering pipeline that splits things up this way.  Of course 
those projects have it a bit easier since they are dedicated XML processing 
systems.

I think one of my biggest headaches is that there are a couple of ways stuff 
moves to a view from the controller.  Typically data is stored in the stash 
under a particular key (like the xlst view looks for data under stash->{xml}).  
However the Template toolkit just takes the whole stash.  So you end up with 
more hard coupling between the view and the controller.  I don't see much if 
any way around this.  The stash is really useful because it's an 'anything 
goes' place to drop stuff.  However that is a weakness too.  The same 
controller can't really pass information to either a template toolkit view or a 
xslt view since both those views expect something very different.  Also if you 
are too lazy and make the stash large that could case performance trouble.

For what it's worth my view directory looks like this.

/View
    /Serializers      #populates the response body somehow, typically from the 
stash or a passed object
    /Transformers  #performs some sort of transform on the response body
    /Filters                #perfoms filtering on the response body

But I think this could use a lot of work and thought.  Cocoon does it a bit 
differently, but again they are using SAX pipes.

2)  I'm typically using the stash as a sort of private namespace to keep myself 
from having to remember to forward parameters.  If you can get 
Controller::BindLex to work for you this makes it even easier;

my $rdf :Stashed = RDF->new(...);  #same as $c->stash->{rdf} =...

I use Catalyst actions for methods when I want catalyst to know about them and 
I use 'normal' methods
primarily to encapsulate behaviors like creating htmlwidgets or doing 
validation.  I tend to make a lot of
custom controllers with private methods.  Gives me an excuse to learn MOOSE :)

I'm using continuations when I'm on a controller that keeps returning to itself 
until a certain condition is met,
like a form has properly validated.


3)  I'd like some help understand return stuff as well.  It would be really 
cool if catalyst actions would
use a return system similar to the way mod_perl works so as to make it easy to 
set the http response

sub demo :Public
{
    # your stuff for the controller

    if($not_found)
    {
        return HTTP_NOT_FOUND
    }
}

Right now I find I have to set the response status and then in my custom end 
controller I have to look for it
and decide what to do.  However doing it this way would seriously break things 
so I guess it's a pipe dream
The new RenderView helps a bit but it doesn't really do what I need either 
since my view pipeline is a bit
more complicated.

Looking at the source code seems to suggest that the return value is not so 
important at this time.  But
maybe in the future if we all want to define it than it could change I suppose.

4.  This bothered me as well.  It seems ugly to me to say $self->foo($c) but I 
think there is a way around it if
you can understand this whole ACCEPT_CONTEXT things mentioned several places in 
the documentation.

I think the problem comes down to the fact that the context is constantly 
changing in response to the way
actions are acting, so if you have a method that is not a catalyst action and 
it needs the context you will
probably have to pass it.  I haven't found a good way around it either.  So I 
just accept it :)

5)  I dont' feel qualified to say anything about unicode other than what you 
already said.


----- Original Message ----
From: Jonathan Rockway <[EMAIL PROTECTED]>
To: [email protected]
Sent: Monday, July 31, 2006 8:58:12 AM
Subject: [Catalyst] Catalyst best practices?

I have a few questions about coding style and whatnot.  I've developed a
"Catalyst coding style", but since I don't work on other people's
applications, I don't know if anyone else does things like I do.  So I
ask, if you do -- why?  If you don't -- why?

1. Plugins

Most of my applications' internals are pluggable.  This leads to a
directory hierarchy like:

lib/MyApp/Controller
lib/MyApp/Model
lib/MyApp/View
lib/MyApp/Formatter
lib/MyApp/Filter
lib/MyApp/Signature.pm
lib/MyApp/User.pm

C<Formatter>s are text formatters that are pluggable with
L<Module::Pluggable> (so you could put the Formatter in your site_perl
directory or get one from CPAN, if desired).  Filters are where my local
TT filters live (since some things are hard to do in TT, but are not
appropriate tasks for the controller).

Then I have some modules that are local to my app and don't fit
anywhere.  User is a user (based on their PGP key), Signature is a
digital signature object.  They're specific to my application and
useless to the outside world, so they're in lib/MyApp. Seems logical to
me, but maybe lib/MyApp should *only* contain M/ V/ and C/ ?

2. Stash / call / forward

Inside controllers, there are a variety of ways to pass data and control
around.  You can put stuff in the stash and C<< $c->forward >> somewhere
and get your result back out of the stash.  You can forward with
arguments and get a return value back.  You can call the method (in
plain perl), providing C<$c>, and get results back from the stash.  You
can use closures, you can use references, etc., etc.  Lots of ways to do
things. :)  Which is best?  (stash, forward, retrieve from stash seems
like it's the "catalyst way", but who knows.  I've become fond of using
forward as a function call, but that seems non-optimal.)

BTW, shouldn't "forward" be called "call"?  When you "forward" e-mail
you don't get the message back.  When you "forward" a user to a
different webpage, they don't come back.  But when you "forward" control
in Catalyst, you get a result back :)

3. Return values from methods inside the controller.

I know about the conventions in C<auto>, but what about regular
methods?  Should they return a true value if nothing bad happens? 
Should they return false if something bad happens?  Should they throw
exceptions?  Should forwards be in eval {} blocks?  (I don't think
Catalyst propagates exceptions like this, but maybe it should?)

Does Catalyst care one way or the other?  Obviously C<die> has a bad
effect on the continued execution of your request, but what about
"return" or "return 0"?  I haven't noticed anything strange when
returning nothing (i.e. a plain "return"), which I usually do to avoid
returning something I didn't want to return.

(Side note:
Explicitly C<return>-ing prevents problems anyway:

     sub foo {
       my ($self, $c) = @_;
       $c->do_something
     }

Is do_something called in void, array, or scalar context?  You don't know!)

4. Internal methods:

If you have some sort of utility function (like C<_calculate_average> or
something, that you call on data in the course of your processing) in
your controller, should it be a method (instead of subroutine)?  Should
it be able to be forwarded to?  If it's not a class method, should it be
allowed to touch the catalyst context?  (so you have something like sub
_foo { my $c = shift; ... } _foo($c).  I do this, but it's pretty ugly. 
I don't think $c should be touched unless Catalyst calls the method for
you.)

5. Unicode

An earlier discussion today prompts this question: why do you have to
use C::P::Unicode and V::TT::ForceUTF8 to get "proper" Unicode support? 
In an ideal world, Unicode would be the default.  Hacks like
C::P::Unicode and ForceUTF8 just lead to problems, IMHO.  I think the
best thing would be for Catalyst to read the incoming headers, properly
convert everything to perl's internal unicode format (even if the input
is in some other locale).  The Controller would then use perl's
functions on the unicode data (which should "just work"), and the View
would do the same.  When it came time to send the response to the
client, an appropriate charset would be selected (probably UTF-8), and
the response would be re-coded from perl's internal format to what the
browser wants.  Appropriate headers would be generated, and the response
would be sent out.

(BTW, V::TT::ForceUTF8 [or rather, Template::Provider::Encoding] doesn't
work for me.  It changes TT's internal semantics, resulting in [% foo |
uri %] not being uri-encoded correctly, if foo is in utf8.   TT assumes
that utf8 characters are allowed in URIs if the encoding is utf8, but
the W3C apparently disagrees.  To work around this, I'm using regular
View::TT now, and that's working fine, as long as my in-source utf8
constants are encoded properly.  A lot of effort on my part, and it
feels to me like it's hanging on a thin thread that will break if my
incoming data was ja_JP.EUC instead of en_US.UTF-8.  Probably not (since
my incoming data comes from the filesystem, and perl does locale stuff
properly), but I'm a little uneasy.  And wow that's a lot of parentheses
))))) :)

Right now Unicode seems like an afterthought (try submitting
UTF8-encoded POD to search.cpan.org.  Doesn't work.)  I don't think we
can trust Joe Average Programmer to get it right.  I've spent a lot of
time and effort chasing down the "Wide character in print" warnings and
exercise adequate caution to prevent double-utf8 encoding (that's fun),
but I still have problems now and again.

Sorry about that -- sounds more like a rant than a question :) So the
question is -- how does your unicode support work?  And I<does> it work? :)

- - -

OK, this is all I can think of right now, but I'm sure I'm missing
something :)  Anyway, I'm very interested to hear what you all have to
say!  Thanks in advance for your commentary.

Regards,
Jonathan Rockway


_______________________________________________
List: [email protected]
Listinfo: http://lists.rawmode.org/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/[email protected]/
Dev site: http://dev.catalyst.perl.org/




_______________________________________________
List: [email protected]
Listinfo: http://lists.rawmode.org/mailman/listinfo/catalyst
Searchable archive: http://www.mail-archive.com/[email protected]/
Dev site: http://dev.catalyst.perl.org/

Reply via email to