Re: Schema-by-interface options

Martin Aspeli <[email protected]> Sun, 24 Sep 2006 13:03:28 +0100
Newsgroups gmane.comp.web.zope.plone.archetypes.devel
Message-ID <[email protected]>
Hi Kapil,


> yes, flavors are on disk keyed by name.  created events would be 
> subscribed by a handler adapting the content to flavorprovider, and 
> setting applicable site wide flavors, although its a bit of science 
> fiction till it gets ported to zope 2.10 and has access to the new 
> persistent registry stuff.

It's not science fiction to me working on 3.0. The future's good. :)

>> I'm not sure it has to be done with monkey-patching as such. My 
>> solution basically looked like this:
>>
>>   BaseObject's Schema() does: schema = ISchemaComposer(self)()
>>
>>   the SchemaComposer's __call__ method would look up adapters to 
>> ISchemaProvider - which could come from local or global site managers
>>
>>   each ISchemaProvider would provide a part of a Schema object (still 
>> not attached to a class or instance)
>>
>>   the final schema would be cached (keyed on __class__ in the current 
>> implementation, though I realise that may be too restrictive) and then 
>> returned
>>
>>   subsequent lookups would use the cache until invalidated via an event
>>
>>   you need to run class-gen to build out methods (on the class, in my 
>> case) for AT to work - the schema composer took care of this.
>>
>> I wouldn't call this monkey patching. All it does is defer the 
>> decision of what Schema() should return until run-time (i.e. the first 
>> time it's called/after the cache is invalidated).
>>
>> However, I'm agreeing with you that the subscriber-based version will 
>> work with the kind of decoupling/indirection that ContentFlavors 
>> favours (sorry), and that the above solution makes per-instance much 
>> more difficult unnecessarily.
>>
> 
> the above solution is broken for the use cases you've presented, is what 
> i've been try relate. you want to use possibly local settings to modify 
> globals. ie. as soon as you have site specific adapters in place, let's 
> say two different schemas in place on sites with overlapping fields, 
> they both dump possibly incompatibly on the class, and you have broken 
> behavior. classgen on the class IS monkey patching a global variable. 

Yes true. The schema cache could be site-local rather than global in a 
module, but what ClassGen does scribbles all over the __dict__.

> the only reason it works in at, is because its by convention done only 
> once, but its not reliable when you have different schemas trying to do 
> it at runtime on the same class.

Agree.

>> Yes. My solution had an 'order' attribute that specifies an int, and 
>> it's sorted by this. klass.schema was 0, and other could use numbers 
>> as they wished. But I never liked this. Note that this is how menus 
>> work in Zope 3...
> 
> i think its just as much of an issue in zope3 as much as it is here, 
> needing awareness of configuration space ordering, esp. when ordering 
> has semantic importance to the runtime is really not fun. maybe its okay 
> for z3, where its meant to be an app server not a product, but requiring 
> plone end users/admins to deal with it is just a recipe for frustration.

True. But end users/admins will typically deal with (i.e. change) local 
things, in which case you can store them in an ordered list and provide 
a proper UI to re-order if necessary (which is what I presume you do).

>> The only negative I can think of here, really, is that if you 
>> uninstall the thing that provided the "global" (site-local) schema, 
>> you need to find all the content objects that have it attached. You 
>> can build some safety into that, of course - if the flavour can't be 
>> found, don't bomb. But it still seems a bit more tricky.
> 
> this safety net, is already done in content flavors during runtime 
> composition, with logging, and the content cleaned up when flavors are 
> edited.

Good. We probably want some kind of general utility function to perform 
clean-up as well, but that should be trivial.

> i was trying to demonstrate a new primitive to utilize z3 
> interfaces/schema specifications that delegated to local components for 
> a z3 solution that allowed for global application of local behavior 
> without per instance modifications and without stomping on a global 
> variable. still not sure if its feasible, but perhaps worth exploring.

Stephan Richter & co are doing stuff like that. GenericSetup is *kindof* 
like that. Check out http://svn.zope.org/z3c.baseregistry, which I think 
does something like what you're talking about (I may be wrong).

>> I trust you to get things like caches right. :)
>>
> 
> i added some caches stats in debug mode into the user interface for 
> verification ;-), the global cache only gets one miss per ordered flavor 
> set schema, the local cache with weakrefs, approaches 98% cache hit 
> success.

Like I said, trust you. ;)

> most of AT uses the field.getAccessor( instance ) semantic, which is 
> fine, but yes its anti-pattern for composition and mutable schemas to 
> spell accessor names directly on instances, since their all volatile 
> (_v_ prefixed), but code doing that would be broken anyways if a flavor 
> was removed. experience with ATSENG has shown its not an issue in 
> practice. ie. context.Schema['rabbit_soup']

Ben was saying we should have something like :

   def get(flavor, key)
   def set(flavor, key, value)

in BaseObject, where 'flavor' is a namespace and key was the key of a 
field *in that flavor* (so that you could have two flavors with the same 
field name).

getAccessor() could return any callable, meaning it didn't have to be 
something on the object. The first real problem I found was in security 
checks that would go look for the accessor on a class and check its 
security assertions. But again, fixing all this is a lot of AT surgery 
that may be quite risky. Since you never change things at runtime 
anyway, this should be safe.

But - what does ContentFlavors do when two flavors provide the same 
field id?

>> So, specifically, this is what I'd like to do:
>>
>>   1. Merge current trunk into 1.5 whitmo-schema-by-iface so that it's 
>> 1.5 compatible. This contains more fun with interfaces and so on that 
>> helps all of this.
>>
>>   2. This uses schema = ISchema(self). The adapter factory here is a 
>> class that does things that scares me - like 'self = 
>> ImplicitAquisitonWrapper(schema)' in __init__(). Yipes. :) We can do 
>> it better (imho) by using a function as a custom adapter factory - 
>> this is trivial.
> 
> could you give a link?

http://dev.plone.org/archetypes/browser/Archetypes/branches/whitmo-schema-by-iface/Schema/adapters.py

Instead, I'd suggest:

@zope.component.implementer(ISchema)
@zope.component.adapter(IBaseObject)
def schemaAdapterFactory(context):
     return context.schema

<adapter factory=".adapters.schemaAdapterFactory" />

Then, if ContentFlavors wanted to return something different, I'd do:

@zope.component.implementer(ISchema)
@zope.component.adapter(IFlavorAware)
def schemaAdapterFactory(context):
     return IFlavorSchemaProvider(self).schema

<five:implements
     class="Products.Archetypes.BaseObject.BaseObject"
     interface=".interfaces.IFlavorAware"
     />

<adapter factory=".aware.schemaAdapterFactory" />

>>   3. I also think the aq-wrapping in Schema() should stay in the 
>> Schema() method, so that it does:
>>
>>    def Schema(self):
>>        schema = ISchema(self)
>>        return ImplicitAcquisitonWrapper(schema)

The above examples assume this is the case, btw.

>> That way, at least we're not requiring all ISchema adapters to 
>> remember to aq-wrap (which may even lead to some funny semantics if 
>> the ISchema adapter is used elsewhere).
>>
> 
> sounds good.

Good.

>>   4. We slap a big, scary note on the ISchema interface that people 
>> need to worry about generating methods, and caches.
> 
> and rabbits with big pointy teeth ;-)

Were-Rabbit!

>> Oh - and can we eventually move this to AT svn?
> 
> yes.

Like... now? :)

Looking through the code now. I'd like to fix up whit's branch, and then 
do a branch of ContentFlavors to make use of it.

Martin

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV