On Nov 7, 2005, at 10:18 AM, Ian Bicking wrote:
> Well, Subway does use Cheetah, which isn't TALish, and has some
> features similar to Myghty. Actually, reading your post on Myghty,
> several of the features Myghty has that Cheetah doesn't don't look
> too hard to implement -- like implicit inheritance, %init, etc.
> Maybe even with just a Cheetah.Template.Template superclass.
Yea, that is true. However the features I mentioned on that post were
just the beginning, the component abilities go deep into the entire
system. Re-implementing it all is possible, and some features would
be easier than others, but that doesn't solve the problem of deep
Routes integration with the Controller.
> TG is using Paste Script in 0.9, though with a turbogears-admin
> wrapper.
Excellent, I'm hoping to see some tasks in Paste Script get easier as
more people use them and contribute patches.
> I actually like .expose; I think forcing internal naming
> conventions to express publicness is a little unclean, because it
> conflates "public to the rest of the program" and "public to the
> world". Though another alternative to .expose is a naming
> convention, like action_*, which means "public to the world".
True, though generally in the context of a Controller, public to the
rest of the program and the world are equivalent. I can't really
imagine any reason to stick functions that are Controller private
inside a Controller class unless the intention is also to have it
private to that class (ie, for use only with that Controller). If it
isn't Controller private, and is intended to be called from elsewhere
in the program it typically belongs in a separate library module, or
at least its own class.
Ruby dodges this problem to an extent by having two realms of
'private'. They have 'protected' and 'private', and stick the non-
exposed actions under 'protected', though 'protected' in Ruby is
equivilant to '_prefix' practically speaking.
> Another option is building "explicitness" into the routes
> definitions. I guess it could be similar to tainted strings. When
> a string is exactly matched by a route, the target is explicitly
> made public. When you get something that simply matches :action,
> then it's only implicitly public. Of course, the implicit :action
> matching could potentially be turned into a naming convention.
> E.g., a rule that bound a "method" variable would mean an explicit
> match, and a rule that bound an "action" variable would be
> interpreted by the resolver as meaning "action_*". That's probably
> what I'd be inclined to do.
Hmm, not sure what you mean about the matching. Perhaps an example?
> Personally I'd be interested in a very open sort of framework.
> I.e., one that keeps the parts separate, since I would like (for
> myself) to make an easy sort of transition from old code to new.
> So a route would identify a controller, which would in turn be a
> WSGI application (probably with adaptation of some sort). With a
> different level of abstractness, a controller produces variables;
> these variables can get rendered in a template, sent via JSON, etc.
> -- I think that's a clever part of TurboGears. But the controllers
> don't really have to care that much about what kind of template
> they are sending to. Dynamically scoped template providing might
> be interesting here.
Yea, that is pretty nice the way TG is bundled up. I've been
considering something like that for Pylons as well, and it wouldn't
be too difficult, just need to have Myghty dump the args it gets
instead of rendering them. Probably using the same .execute intercept
I plan on using when testing the Controller to ensure templates were
passed the right vars. It'd be pretty easy to hook-up a system
whereby people could make their own controller filter, like (I'm not
a JSON expert, so I doubt this is right):
m.connect('*url/jsonify', controller='filter', action='json')
class FilterController(BaseController):
def json(self, m, r, **params):
r.content_type = 'text/xml' # Or whatever is needed
args = m.subexec(url, capture_args=True)
m.out(jsonify(args)
There's no capture_args option right now, but I need to add a wrapper
for testing anyways and I could make it easy to trigger like that.
I'm not sure Routes would work right when dealing with dispatching to
entire WSGI apps instead of just a Controller, though I don't see why
a Filter middleware of some sort couldn't use Routes to do that. I
kind of lost you after that though when it comes to the WSGI app,
adaptation, etc.
I agree that a light coupling of the parts is ideal, though as I
mentioned before it becomes more useful to have the template and
controller somewhat aware of each other for certain functionality.
The only bounds Pylons so far has when it comes to tying the
Controller to the template is through the magic temp attribute of the
controller (though it could be used easily enough without a template).
I'm still not totally happy with this solution, but it does make it
easier than passing all the variables into the template directly. As
we're using a stateless, persistent controller things can't be
attached to the Controller instance. Which leaves 2 options to move
variables into the template,
1) Pass them explicitly (as TG does)
2) Have a special thread-safe variable that's cleared on every
request, and available in the template
We're using option #2 right now, though having a magic container as a
controller instance attribute thats available in the template might
cause confusion. James' tutorial (
http://pylons.groovie.org/project/
wiki/JamesNotes) does do an excellent job of clarifying its use.
While they could be passed everywhere, that becomes a hassle in some
cases. For example, if you had some auth code that needed to be run
before 30 actions, but not for 2 of the ones in a controller, in
Rails you put inside your class:
before_filter :authorize_user, :except => ['login', 'logout']
# Call authorize_user action, unless its the login or logout method
In Pylons, we're having a __before__ method that can be used to
implement similar functionality.
Now, this ties back to how variables are moved around, because we
need a way to get variables from __before__ into the actual action
method thats called. Having a self.temp that can be used is very
handy in this case, and others where you need to store variables for
later use without relying on trying to manage some dict yourself and
pass it all over the place. So yea... I'm somewhat torn, I know I
want to be able to whisk a magic container around without explicitly
passing it (thread-safe singleton or magic object attached to
controller instance), yet I'm not too keen on the current 'self.temp'
stuff I put in.
If there's some way to still do this, but in a more clear-cut way,
I'd love to hear some ideas. :)
The odd thing about holding the variables intended for the template,
is that I find myself typically setting up variables with the
specific intention of being used in a rendered template. The utility
of being able to return them in JSON is somewhat perplexing in much
of my code since I'm passing complex objects that wouldn't be useful
as JSON. I'd actually code completely separate functions if I needed
specific data back as JSON, just as I'd code special functions for
SOAP/XML-RPC services (though maybe those two are likely to have a
more similar dataset).
- Ben