[Grok-dev] first thoughts on "regebro-guido-templates"
Darryl Cousins
darryl at darrylcousins.net.nz
Sun Oct 28 18:10:50 EDT 2007
Hi,
On Sun, 2007-10-28 at 01:22 -0400, Brandon Craig Rhodes wrote:
> I have many things to say about the "regebro-guido-templates" branch,
> but since it's now after midnight, this email will only include two
> thoughts - which, if they get addressed in the branch, might start
> clearing up smaller issues that I won't mention for now.
>
> The two subjects are:
>
> 1) Why self.__grok_module__ keeps getting involved.
> 2) Putting template logic together into one class.
>
> My ideas:
>
> 1) The question was raised: Why does the new template code require
> template plugin classes to do this in their __init__() functions:
>
> self.__grok_module__ = martian.util.caller_module()
>
> This should go away. What is it trying to do?
>
> I have looked around, and noticed that the old-fashioned Grok
> template logic does exactly the same thing. And the reason why
> hinges on an interesting asymmetry: a Grok programmer creates
> Models and Views through subclassing, but he creates Templates
> through instantiation!
>
> Since martian is built around the idea of grokking classes, it
> only pays attention to objects whose __module__ is the same as
> that of the module it's searching. This works wonderfully for
> classes: it makes martian ignore classes you've brought into your
> module through "import", since their __module__ names the one they
> were imported from, not the one martian's currently scanning. But
> it has the side-effect of making martian completely ignore class
> instances defined in another module from the class itself, because
> Python instances don't have their own __module__ attribute - they
> inherit the value from their class instead. (For details, grep
> through martian for the "locally_defined" function.)
>
> And, again, all of this causes problems only because of the
> asymmetry between how you create, say, a view:
>
> class MyView(grok.View):
> ...
>
> and how you create a template:
>
> myTemplate = grok.PageTemplate("...")
>
> which produces a mere instance. This all brings us back to that
> ugly:
>
> self.__grok_module__ = martian.util.caller_module()
>
> call which, we now see, is an attempt to fool martain into looking
> at instances by stating which module they "belong to".
>
> I will suggest four alternative approaches, any of which I think
> would make things far simpler, and it would be really nice if the
> "regebro-guido-templates" branch was converted to one of them
> before being merged. I'm willing to write code. :-)
>
> Approach A: Make templates classes.
>
> This would abandon the asymmetry above, and require users to
> create a class for each inline template they wanted to create.
> Instead of having class-instance pairs like:
>
> class AView(grok.View):
> ...
> aTemplate = grok.PageTemplate("...")
>
> they would have pairs of classes, something like:
>
> class AView(grok.View):
> ...
> class ATemplate(grok.PageTemplate):
> content = '...'
>
> This might have other advantages involving the ability to mark
> up templates with the same sorts of directives we use for
> other things in Grok. For example, a Template could accept a
> grok.context(...) (or grok.view(...)?) statement explicitly
> stating the View it should work with, just like Views can
> state the objects that they adapt. Or maybe that would never
> be useful? :-)
This approach is pretty much what I use for mars.template. Please see:
http://svn.zope.org/Sandbox/darrylcousins/mars.template/src/mars/template/README.txt?view=markup
The ftests in the package use the old grok method of doctests but should
provide additional documentation on how it all works.
Disclaimer: I haven't been back to the mars packages for 3 months as
I've been busy with other projects. Obviously therefore they will be
broken against recent grok but the code may be worth a look still.
Best regards,
Darryl
>
> Approach B: Teach martian about instances explicitly.
>
> This would maintain the asymmetry between View classes and
> inline Template instances, but rather than requiring the
> implementor to practice witchcraft, would introduce a new
> martian directive "instances()" that tells martian to pay
> attention to instances of a class, not just the class itself.
> So template classes would look like:
>
> class GenshiTemplate(...):
> grok.instances()
> ....
>
> instead of having to set __grok_module__ on every single
> dratted instance that it wants martian to pay attention to.
>
> This would involve the difficult decision as to whether
> grok.instances() should be inherited by subclasses of a class,
> or whether each of them should have to repeat the directive in
> order to make its own instances discoverable. The latter
> seems the safer approach but I'm too tired at this point to
> ponder all the issues.
>
> Approach C: make Views link to their inline templates explicitly
>
> Magically associating the View my.Foo with the contents of the
> file "my_templates/foo.pt" is great. But how important is it,
> really, to magically associate a variable named "foo" with the
> View?
>
> class Foo(grok.View):
> ...
> foo = grok.PageTemplate(...)
>
> Couldn't we instead have the View state that it wanted to use
> an inline template like this?
>
> foo = grok.PageTemplate(...)
> class Foo(grok.View):
> grok.template(foo)
> ...
>
> Then we would not need magic to grok instances at all.
>
> Approach D: search for templates without grokking them
>
> Why grok instances of templates at all? Template files like
> "foo.pt", after all, aren't grokked! They're discovered by a
> little routine that scans the _templates directory and looks
> for appropriate files. Instead of grokking template
> instances, why not just explicitly scan each module for
> template instances in a search that exactly parallels the
> search for template files? The scans could be run and compare
> their findings for conflicts first ("template 'foo' is defined
> both in 'foo.pt' and in-line!"), then offer the resulting
> collection of templates to the association routine. This
> could, I think, be done straightforwardly by adding a
> findModule() routine to templatereg.py that parallels the
> structure of findFilesystem().
>
> Those wiser, more experienced, and more awake ought to weigh in on
> these approaches. I think I will have to read through them again
> tomorrow before even starting to decide which one I myself like
> best. :-)
>
> 2) I think the reason that "regebro-guido-templates" offers a clumsy
> and verbose mechanism for plugging in new templates is that they
> copied the two-class way that Grok already does it, which is
> clumsy and verbose because Grok itself inherited it from
> zope.pagetemplate. :-)
>
> By "two-class", I mean that you have to create one class for
> inline templates, and another class for templates that are stored
> in files. I think these two situations should be collapsed into
> one class, so that the person plugging in a new template language
> just writes one class like:
>
> class MyPageTemplateLanguage(grok.PageTemplateLanguage):
> grok.name('mtl')
>
> def setup(self, filename, content):
> self._template = genshi.Template(content)
>
> def render(self, view):
> return self._template.render(**self.namespace(view))
>
> Okay, I'm too tired to keep typing now. I'll outline more about a
> single-class approach tomorrow, maybe. And then I can get started on
> how the namespace() situation could perhaps be simplified and
> streamlined a bit. But, tomorrow.
>
More information about the Grok-dev
mailing list