[Grok-dev] the beginnings of megrok.feeds

Brandon Craig Rhodes brandon at rhodesmill.org
Sat Jul 19 17:36:24 EDT 2008


I have started writing a "megrok.feeds" package that will let you easily
turn container-like objects into Atom and RSS feeds, and objects in your
containers into items inside of those feeds.  (Should I call this
"megrok.syndication" instead?  It takes longer to type.)  You can look
inside the Zope repository to see what I have done so far.

Some of you might recall that I have differences with Derek Richardson,
the author of "vice.outbound", about how his work should be extended to
create "megrok.feeds".  I had initally imagined that, for each Grok
container which could support a feed, the Grok programmer would write
something like:

    class FolderFeed(megrok.feeds.AtomFeed):
        grok.name('atom')
        grok.context('Folder')
        def update(self):
            self.title = Folder %r Feed' % self.name
            self.description = 'Recent changes to folder %r' % self.name
            ...

And I would write a grokker that did roughly what a grok.View grokker
does, except that there would be no choice of template (an "atom.pt"
from "megrok.feeds" would be used automatically), and there might be a
bit more logic under the covers to gather any extra information that the
feed needed.

 <SIDE QUESTION>
 Why does the "ViewGrokker" in Grok's "meta.py" call "provideAdapter",
 which I think is a global registration command, when registering a
 View?  Shouldn't it instead register the view as a local adapter inside
 of its particular Grok site?  Imagine that I have a Python module that
 provides "Foo" objects, and I instantiate two different Grok apps in my
 database that both use "Foo" objects; to be concrete, imagine that
 "app1" defines a "/one" view on a "Foo" object and that "app2" defines
 a "/two" view.  Since the grokker defines each view globally, then it
 seems that not only would these two desired URLs work:

    http://localhost:8080/app1/foo/one
    http://localhost:8080/app2/foo/two

 but also the two unwanted URLs:

    http://localhost:8080/app1/foo/two
    http://localhost:8080/app2/foo/one

 because the global registrations would make each application's views
 magically appear over in the other application?
 </SIDE QUESTION>

Derek instead advocated a different approach.  First, the application
designer would define, say, "/atom" as the way to view any feed-able
container as an Atom feed.  Second, they would then write an adapter for
each kind of container that they wanted turned into a feed.  The result
in Grok code might look like:

    class Atom(megrok.feeds.AtomFeed):
        grok.name('atom')

    class FolderFeed(megrok.feeds.Feed):
        grok.context('Folder')
        def update(self):
            self.title = Folder %r Feed' % self.name
            self.description = 'Recent changes to folder %r' % self.name
            ...

After thinking about Derek's scheme for two or three weeks, I realized
that it did have at least one nice feature: it forced programmers to use
the same name for feeds across their whole site.  With my example scheme
shown up above, if a programmer was not careful then they could wind up
with all sorts of names for a given kind of feed on a given site
('/atom', '/Atom', '/feed', etcetera) - and sites whose feed names are
inconsistent are really annoying.  Whereas with Derek's scheme, they
just say once "Atom feeds are located at /atom for all my containers",
and don't have to repeat the name over and over again for each container
that they describe how to feed-ify.

So because I like the idea of consistency, I tried doing things Derek's
way; you can take a look at the Grokkers here:

http://svn.zope.org/megrok.feeds/trunk/src/megrok/feeds/meta.py?rev=88618&view=auto

As you can see, per Derek's design, there are three registrations that
have to get done before an Atom feed can work; for example, for the
classes defined above, this would look like:

 - The feed view itself has to be registered as a multi-adapter:
   (factory=Atom, adapts=(IFeedable, IBrowserRequest), name='atom')

 - The adapter that turns the container into the feed is a single-adapter:
   (factory=FolderFeed, adapts=(Feed,), provides=IFeed)

 - The container type itself has to be marked as feedable:
   classImplements(Folder, IFeedable)

That way, when you say 'myfolder/atom', the "Atom" view will get
activated because 'myfolder' will be marked as "IFeedable", then it will
try to adapt its context to IFeed, and will succeed because the adapter
to IFeed exists.

In my recent exchange with Derek, I objected to this scheme because it
seemed odd to have to have so many interfaces just to get a single view
working.  Again, I'm rethinking that, because it would be so useful to
corral Grok developers into naming only once what they will call each
kind of feed on their site.

My big issue at the moment, though, which I guess is reflected in the
big "SIDE QUESTION" up above, is that I have no idea how to make any of
this local.  If the developer runs two Grok apps inside of a single Zope
instance, and one app made the decision that "/atom" is how you ask for
an Atom feed but the other app named its syndication links "/feed"
instead, how do I keep the results of these decisions "local" to each
app?  If "/atom" is registered sitewide through a "provideAdapter()"
call, then I don't see at all how it can affect only the current Grok
app and not others inside of the same instance.

I know it's odd to hear me ask about running multiple Grok apps in the
same instance, because I've complained before that it makes little sense
and that the extra "/app" part of every Grok URL is just an annoyance.
But if we're going to support them, then I need to understand how we
keep them separate from each other's active views; and, at the moment, I
don't see how this is done.

-- 
Brandon Craig Rhodes   brandon at rhodesmill.org   http://rhodesmill.org/brandon


More information about the Grok-dev mailing list