[Grok-dev] Re: Atom and RSS feeds in Grok

Derek Richardson derek.richardson at gatech.edu
Mon Jun 23 18:32:50 EDT 2008


Brandon Craig Rhodes wrote:
> "Derek Richardson" <derek.richardson at gatech.edu> writes:
>
>> Currently, in Vice, there is one view that never changes ... This view
>> adapts (IFeedable, IBrowserRequest).  To make an object able to be
>> fed, you do two things:
>>
>> 1. Write an adapter from the object's class to IFeed
>> 2. Provide IFeedable on the object
>>
>> This way, the view contains what is the usually the *same* for all
>> requests for the feed, regardless of context, and the adapter
>> encapsulates everything that *varies* by the context.
>
> This approach sounds completely unacceptable.  The goal here is not for
> there to be a single view, like:
>
>  (IFeedable, IBrowserRequest), "feed"
>
> that makes all syndicatable objects on a Grok site grow a "/feed" view
> which renders them as some single kinds of feed (Atom or RSS, depending
> on the developer's settings?).  Rather, the goal is for a Grok developer
> to be able to declare several feeds on a single object, so that "/rss"
> on his favorite container could return one kind of feed, "/atom" could
> return another, and maybe even "/atom_recursive" return a third which
> differs not only in content but in format.  Your suggestion does not
> sound capable of supporting this basic use case.

How absurd a rebuttal! *Of course* this use case is supported. Just
because you do not have to rewrite the view class *every* time you
want to syndicate a different type of container does not mean you
cannot register the unitary view class multiple times under the same
name, with different functionality available at different url
extensions.

> Even worse, your scheme doubles the number of interfaces in play, merely
> in order to provide information which is redundant.  To review: you
> suggest splitting container syndication into two separate adaptation
> steps:
>
>  container --(custom adapter)--> IFeed object --(universal view)--> HTML
>
> This split means that you are unable to drive the rendering process by
> the fact that the developer has written the correct adapter that gets
> his container and provides IFeed information (like its feed name, feed
> title, and so forth).  Instead, the developer has to take a second step
> of attaching the IFeedable marker to his container before your view will
> pick it up.

To be charitable, I will interpret 'number of interfaces in play' as
meaning 'number of times the total number of interfaces is
implemented.' Because it is patently untrue that the number of
interfaces doubles, as you seem to imply with your vague wording. So,
yes, each container must implement IFeedable to be known to be
adaptable to IFeed, but this merely means registering the container as
implementing IFeedable, not writing a new interface for every type of
container to be fed.

I don't know why you are so bothered by the total number of times that
the total number of interfaces is implemented. Perhaps you would care
to share why this matters in the least?

Besides, this is an established zope 3 pattern. As an example, see
zope.annotations.IAnnotatable. Philipp has this to say about
IAnnotatable in the second edition of his book:

"""
IAnnotatable
Zope needs to determine if an object has an IAnnotations adapter very
frequently, which means it needs to be done very quickly. Rather than
trying to adapt each object to IAnnotations and reacting to success or
failure, Zope uses an optimization. It requests that objects "promise"
to be adaptable to IAnnotations.
They do this by declaring to be annotatable, by providing IAnnotatable
from the zope.annotation package. This interface does not promise any
additional functionality expressed in methods or attributes. It is an
implied contract, a marker interface. The implied contract is that it
is possible to get an annotations adapter for any annotatable object.
"""

> And not does using the IFeedable marker require a second step, but,
> strictly speaking, it's a fraudulent adapter registration!  Let's look
> at it again:
>
>  (IFeedable, IBrowserRequest), "feed"
>
> As you can see, this view makes the promise that "if you provide an
> object that satisfies IFeedable, then I can answer a browser request by
> rendering it."  But this is false!  It's not the presence of the marker
> interface that makes your view able to process an object; in fact, if
> you run your view on an object that's merely marked IFeedable, then the
> view will fail and say something like "Cannot adapt your container to
> the IFeed interface."  It's really the ability to *be adapted* to IFeed
> that makes the object renderable, and you place the burden on the
> developer to consistently make double-registrations for every container
> that he wants to syndicate, and to make them all correctly.

See above. Your overblown vitriol should be directed at, not only
Vice, but the Zope 3 codebase itself. Certainly the authors of Zope 3
did not find their usage of exactly this pattern in zope.annotation to
be "fraudulent," as you so graphically tar my adherence to this
standard pattern.

> We can now proceed to yet another issue:
>
> Because this is Grok-land, I can actually hide this ugliness and
> complexity, by writing the grokker for "grok.Feed" such it actually does
> the double-registration for the user - maybe it will register itself as
> the container-to-IFeed adapter while also remembering to place IFeedable
> on the container itself.  Then the burden placed upon developers goes
> away.

So, we see it - there is no substance to the above objections, since
they will almost never impinge upon the end-programmer experience!

> Until, that is, they encounter a problem and fire up a debugger to
> examine some mistake they made in their adapter.  If they have practice
> debugging Grok applications, they will know that all other Grok views -
> grok.View, grok.XMLRPC, and grok.REST, for example - have one of their
> own content objects as their "context".  This is, so far as I know, an
> iron rule in Grok: in any view, "self.context" will always be the
> user-defined object that lives at the URL that one would get by chopping
> off the view's name.
>
> But this will not be the case with your "two-storey" views!  The URL
> will simply say something like:
>
>    http://.../app/container/feed
>
> Which makes it look like the "context" of the view "feed" should be the
> object named by "app/container".  But the developer, in his debugger,
> will discover that the "/feed" feed view is actually an object he's
> never seen before that lives in "vice.outbound.core"; that its "context"
> is an instance of his "grok.Feed" adapter, which, it turns out, doesn't
> even have a URL of its own (!); and that only by going inside of its
> "context" can he then find his container.  This seems to create an
> important, and unnecessary, asymmetry with the other kinds of Grok
> views, and, even worse, does it "magically" without the user being able
> to guess from the appearance of his "grok.Feed" declaration that what's
> being created is any different than any other sort of Grok view.

Ha! Your approach falls before the same objection. To repeat a section
of my original response that you conveniently redacted:

"""
This approach that you are taking with feed items is exactly what I am
contending should be done with the feed itself. To carry your feed
logic above to its logical conclusion, you would not have FeedItem
adapters either, but simply have a view that knows how to go from
objects to the information required to render an object as an item in
a feed. My position is consistent: adapters adapt objects of varying
interfaces to a single interface so that they can be used by a single
view.
"""

I contend that to be consistently indirect in the same way is better
to be inconsistent in the use of indirection. Plus, I think this is at
worst a minor drawback in the face of my overwhelmingly better
approach. So, let's talk about that next...

>> Your approach would require adding all the methods that currently live
>> on the adapter to the view, so that the view can render an object of a
>> class and so that it can be selectively overridden, to minimize
>> boilerplate. ... perhaps the view boilerplate lives on one superclass
>> and is simply inherited repeatedly, so you don't actually rewrite it.
>> But I object to it being there at all.  I contend that your route
>> results in one class that is less cohesive and more tightly coupled
>> than my two class approach.  It is better to separate the constant and
>> varying than to tramp the constant through every variation.
>
> You make a big deal about this, but really the only "constant" element
> in your dinky little feed View that I can see is that it takes Python
> "datetimes" provided by the user in their IFeed adapter, and turns them
> into date strings of the format required by the RSS and Atom feed
> specifications.  Big deal!  You prefer the expense, messiness, and
> complexity of your "two-storey" scheme, just so the developer doesn't
> have to inherit, from the Feed superclass, a tiny bit of logic that
> reformats dates?
>
> That doesn't make sense.

Even if this were true, it would not be a rebuttal of my initial
claim, just a quantification of the abuse you commit - you are still
abusive with respect to good design, just not as violently as you
might potentially be. That's hardly a justification.

And, furthermore, it's not true. You also might want to change the
mime type, the encoding, the cache headers, etc. And you might want to
add things not currently there, such as I have done for a private site
that tgz bundles the feed document with all the enclosures, so that
repeated queries do not need to be made. You can't rule out in advance
the utility of this, except by being dogmatic.

Plus, you are simply arguing the empirical effects I suggested as
examples of how the bad design plays out, not answering the core claim
that you are engaged in poor design.

>> The clincher would be if someone wanted to change the view and use the
>> same IFeed-adapted objects.  In your scenario, this would result in a
>> combinatorial explosion of views.  In my scenario, it results in one
>> more view and zero more adapters.
>
> No, it wound not have that result, because any sensible developer, on
> realizing that, say, five of his content types are similar enough that
> the same adapter could syndicate all of them, will simply tag them all
> with an interface that (gasp!) rather than being a marker is an actual,
> honest assertion that they share some of the same attributes and
> methods, and then write something like:
>
>     class MammalFeed(grok.Feed):
>         grok.context(IMammal)
>         grok.formats(rss='rss')
>         def update(self):
>             sci_name = '<i>%s %s</i>' % (self.genus, self.species)
>             self.title = "The Mammal %s" % sci_name
>             self.description = ("Exciting recent events in the hunting"
>                                 " and cooking of %s" % sci_name)

Ah, if my use were but illegitimate. I suggest you take up the issue
with Philipp and the other authors of Zope 3, rather than maligning me
for using the technologies in the way they are clearly intended. This
suggested amendment on your part is merely a stopgap measure for
achieving a partial capture of what I achieve at the beginning, and
with more work, besides!

Derek


More information about the Grok-dev mailing list