When I first started using Perl Catalyst for web application development I felt appalled by how sloppy it seemed to pass important application data around in a secondary global namespace, the Catalyst Stash. I guess it's context global, but it stills seems messy to me in that it seems to poorly replicate features already built into Perl, and for which Perl can help you use correctly. I mean, a global is bad enough, but if you use strict and warnings you will at least get warned if you abuse a global, such as if you try to use it before it's initialized and all that. But the Stash is basically a Hash, and it's prone to typo issues, etc., basically all the evil stuff that I tried to banish from my Perl applications years ago.
It's one of those things that seems fundamentally busted to me, yet it works well enough for simple to medium cases. It just feels to me like it decouples control way too much, and in my experience the stash becomes a sort of no man's land.
I think it's one of the three things that bother me about Catalyst (with the ->forward/go/visit/detach versus method call ambiguity and the way the default TT view guesses a template based on the action name being the other two things that work acceptably enough but still manage to bother me quite a bit).
Thoughts? Am I being too controlling here? Should I just go with the flow or are there others out there that are also bothered by this?
It does bug me, but I'm not sure what the alternative is. You can't really use an object, because there's so many different things that can in the stash (anything that can be passed to a view plus anything you need to pass between chained methods in controllers).
Posted by: Dave Rolsky | 09/29/2009 at 10:31 AM
The stash with chaining I find particularly annoying, although I guess that is something fixable. Having to stash something and unstash it over and over as I descend chained methods just seems overly repetitive. I guess part of my problem is that the stash is like a catch all for so many different uses, and each use has pretty distinct needs. It leads to confusion. Like, with -forward/detach/etc., you can place args in the call, like -forward('Component', [@args]), OR you can stash stuff and then the forwarded to bit expects to find it in the stash, which seems really dangerous to me, in terms of how the stash can just have anything. You can really mix and match without any guidance as to what is the best thing to do.I guess for the view you might need something more loosely defined, and it definitely avoids the also evil ...-view('TT', [\@somelist, {qw/hash of a very long list of keys and values}, 'more', $stuff]); Although lately I lean toward something like this over the stash, since I also allow the View to access models directly (in a readonly manner) since some MVC paradigms allow for this.
Posted by: John Napiorkowski | 09/29/2009 at 11:54 AM
osfameron also touched on this recently in his blog:http://greenokapi.net/blog/2009/06/04/global-state-in-web-applications-catalysts-stash/
Using a Moose object for stash like this does sound enticing to me!
/I3az/
Posted by: draegtun | 09/29/2009 at 12:06 PM
I find the stash to be pretty tedious too, especially with complex chained actions.I was talking to a friend who works on rails, and the way he described rails is that any variable in scope of the action method is available in the template. I may be misrepresenting this some, but it seemed like a reasonable solution to me (kind of like the BindLex stuff that was popular a while back.) I don't see how that would really work for chained actions, though.
Posted by: Lee Aylward | 09/29/2009 at 12:52 PM
For passing values between actions in the same controller you can use controller attributes, after all controller is now a Moose object so you can just call $self-what_ever instead of $c-stash-{what_ever}.
Posted by: zby | 09/29/2009 at 02:56 PM
I'm just wondering if we are over thinking the whole deal. From what I can see, we use the stash for communicate between methods/actions (either through chains or -forward/etc or as $self-method...), which in my mind is really terrible. My direct experience with a large application backs this up. People haphazardly toss stuff into the stash and other stuff they pass as 'normal' arguments and the whole thing is a nightmare to maintain. The do this because they are not sure what to do, since the Catalyst docs don't really offer a best practice and the framework makes it too easy to discover the wrong way to do things (in my opinion.) For this use we really should be passing real args. That means we need something better than the stash for chaining, but solving that shouldn't be so hard.For passing stuff to the View, well, again I am of two minds here. Not all MVC paradigms require the View go through the Controller to get Model information. Many systems allow (and in some cases require) a View to listen in (read only) a Model, or some sort of UI Model adaptor for the complex cases. So really I'm thinking we could either load up info for the View in a true View class, using Moose style attributes, or as part of an initialization for a something like TT or Mason. That would lead us to having more than one View class in order for the View class to actually say what it needs. I know that's a big change from the way we handle this now, but maybe it's more managible in the end.
Posted by: John Napiorkowski | 09/29/2009 at 02:56 PM
zby: I'm not sure it's better to use Moose attributes in the controller in the way you suggest. The information we want to pass is part of the request context while the attributes are going to be associated with the application context... I think. I guess I'm not sure the best way to handle chaining, but honestly I'm not in love with chaining given the difficulties I've seen it present to new and mid level programmers. It's a dispatch paradigm that doesn't exist anywhere else in Perl, which I guess is part of the issue.
Posted by: John Napiorkowski | 09/29/2009 at 03:24 PM
As a followup to the chaining and related issue for passing params, I
might be more excited about some sort of context sensitive IOC
container for Catalyst, something that would create what I needed based
on whatever settings. So the way that could work is that if you had a
terminal chained action (the last action in a two method chain) which
wanted a parameter from the previous chain to perform some sort of
database lookup (one of the more common uses for chaining that I see)
you could simple declare that that resultset depended on the parameter
from the previous. and then instead of having to fetch out the
parameter, perform the lookup, you just use the IOC container and ask
if for what you need.
Posted by: John Napiorkowski | 09/29/2009 at 03:52 PM
I view it as standard OO programming - things that you need in most methods you put on the object as an attribute, other things you just pass as parameters. Among them you'll have things that are context related and others that are application related - but it is not different from normal OO programming where you can have attributes that are computed at the creation time and then are more or less constant and there are attributes that need to be refreshed from some other data from time to time.I am also not a big fan of chained and I think it should be replaced by something closer to the normal OO dispatch mechanism. The problem with chained and also it's power comes from joining two things: dispatching and parameters. I imagine there should be another way to mark the parts of the uri that are used for dispatching and use others as parameters.
Posted by: zby | 09/30/2009 at 02:39 AM
It's probably just the way I approach OO, but I typically don't use fields to communicate state between methods. I typically prefer to pass everything I need to a method, or use some other technique. I think I do this because I've had trouble when dealing with other people's sloppy code and not being certain of the state. But its not a hard and fast rule, just my way.My issue with chaining stems mostly from how much trouble I've seen it give junior and midlevel programmers. It's easy to mess up the chain, and typically not easy to figure out what you did wrong. Also, I find that building a chained action's url via $c-uri_for to be harder than it should. So lately I avoid it. But again, I know chaining is popular and the things I am complaining about are fixable with effort.
Posted by: John Napiorkowski | 09/30/2009 at 07:31 PM
[this is good] The theme is interesting, I will take part in discussion. Together we can come to a right answer. I am assured.
Posted by: Jasper Bernier | 05/10/2010 at 10:04 PM
[this is good] I am final, I am sorry, but it is necessary for me little bit more information.
Posted by: Silvester Lord | 06/14/2010 at 09:18 AM