[Catalyst] Catalyst best practices?

Jonathan Rockway jon at jrock.us
Mon Jul 31 02:58:12 CEST 2006


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

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 370 bytes
Desc: OpenPGP digital signature
Url : http://lists.rawmode.org/pipermail/catalyst/attachments/20060730/9786a049/attachment.pgp 


More information about the Catalyst mailing list