[Zope-dev] How is 'retrieveItem intended to work with TTW Specialists?

Phillip J. Eby pje@telecommunity.com
Fri, 29 Sep 2000 18:57:11 -0500


At 03:14 PM 9/29/00 -0500, Steve Spicklemire wrote:
>
>Thanks so much for the response... 
>
>It turns out, I tried the Python Method and it had the 
>same basic problem as the DTML Method... however I've now
>moved on, using deletageRetrieve to a new thorny issue. ;-)
>
>When I get an object from of another specialist it appears
>that it doies not inherit from my specialist, but from 
>*its* specialist. (e.g., my framework specialist has methods
>that I want the dataskins to acquire). I've currently got it
>wrapped like so:
>
>    def retrieveItem(self, key):
>        """ get an item...."""
>        return self.delegateRetrieve(self, None, key=key).__of__(self)
>
>I've tried several different combinations of __of__ etc, but
>none of them seems to allow the dataskin to acquire, for example,
>standard_html_header from my specialist. Is there any way to 
>enforce acquisition at this level?

You can use aq_base before the __of__, but I wouldn't recommend it, because
you'll be forcibly ripping off security-related data.  Probably aq_explicit
would work better, since it would prevent default acquisition but leave
security acquisitions relatively intact.

I still suspect, though, that your design is not optimal.  If you actually
need a "bridge" specialist that lets you change the behaviors of the thing
you're accessing, then you should really just build a bridge specialist and
not simply do directly delegated retrieval.  What you want is to set up a
rack in the specialist that creates a virtual object which gets all its
attributes (the ones you care about, anyway) from an object retrieved from
the other specialist.  SkinScript example:

WITH otherSpecialist.getItem(self.id)
COMPUTE theRealThing = (RESULT is None) and NOT_FOUND or RESULT

WITH self.theRealThing COMPUTE
myAttr1 = RESULT.theirAttr1, 
myAttr2 = theirAttr2, # equivalent to myAttr2 = RESULT.theirAttr2
SomeAttr,             # equivalent to SomeAttr = RESULT.SomeAttr
myAttr5 = theirAttr1 * theirAttr6

# Shorthand way of copying attributes with the same names
WITH self.theRealThing COMPUTE foo,bar,baz

You would put this in a script inside a rack in your "bridge" specialist.
You would set up the rack to be "non-persistent" and use "theRealThing" as
the existence attribute.  Now, when you access the rack, it will create a
dummy object and try to look at its "theRealThing" attribute.  This will
cause the SkinScript to call the other Specialist and attempt retrieval.
If it succeeds, theRealThing will equal the object and the Rack will
consider the object to exist in the bridge specialist.  If it fails,
theRealThing will be a non-existent attribute and the Rack's getItem will
return None.

Let's say it has succeeded.  You now have an object with no attributes
loaded other than 'id' and 'theRealThing'.  You attempt to access attribute
SomeAttr.  The second SkinScript declaration fires, and computes the values
of myAttr1, myAttr2, SomeAttr, and myAttr5, caching them in the DataSkin.
Voila.  You now have a completely transformed object, in the context of
*your* specialist, with *your* attribute names.  It has no leftover
acquisition context, but of course you had to have permission to access all
those attributes on the object and to the specialist you retrieved it from.
 But here's the real kicker...  You determine in *your* rack the precise
ZClass it will be implemented as.  In effect, you have rewrapped an
object's data into another class.

Okay, so that works for reads.  What about writes?  That's a little more
complex, as you'll need to write something like:

STORE foo,bar
USING self.theRealThing.manage_changeProperties(foo=self.foo,bar=self.bar)

For whatever combinations of properties are applicable.  If the properties
are on a sheet, the USING expression gets more complicated.  Note, too,
that we could have done transformations on the 'id' to look something up,
and that we could also have multiple racks, each doing transformations from
different Specialists.

Now, you may be asking yourself, "This all looks incredibly flexible, but
is it efficient?"  Hell no, of course not.  You are much better off, if you
have the option, of specifying to your framework's user the requirements
you have for objects in that part of your system.  Then, when they are
designing their system, they can get the names and features right, and you
call *their* specialist for what you need.  If they didn't do it the easy
way, then it's again *their* responsibility to re-cast your whitebox as a
bridge.

(Note, by the way, that if the end-user's raw data is coming from something
like an SQL database in the first place, then all they have to do is map
from SkinScript to SQL, and this is *much* more efficient than mapping
Specialist->Rack->Specialist->Rack->SQL, which involves many more layers of
object creation, method calls, and security checks.)

To put it another way: design your whitebox specialist how you want it.
Make it complete, but of course some parts will have to be changed if
someone wants to use other than your default implementation.  If they want
its data to come from somewhere else, they can plug in the SkinScript or do
whatever else it takes.

So, to sum up...  Stop worrying about delegated retrieval!  :)  You're
stepping into app integrator territory here.  ZPatterns was designed to
make it easy to make reusable frameworks without hardly trying.  And it was
*especially* designed for retrofitting object frameworks over legacy
applications and databases.