Ignore:
Timestamp:
Sep 21, 2006 4:03:43 AM (12 years ago)
Author:
hazmat
Message:

marker interface application, events, and cache invalidation

Location:
Products.ContentFlavors/trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • Products.ContentFlavors/trunk/configure.zcml

    r1673 r1674  
    2020    /> 
    2121 
     22 <subscriber 
     23    handler=".schema.handleFlavorsModified" 
     24    for=".interfaces.IFlavorsModifiedEvent"/> 
     25 
    2226</configure> 
    2327 
  • Products.ContentFlavors/trunk/interfaces.py

    r1673 r1674  
    2323$Id$ 
    2424""" 
    25 from zope.interface import Interface 
     25from zope.interface import Interface, Attribute, implements 
    2626from zope import schema 
     27 
     28from zope.app.event.interfaces import IObjectEvent 
     29from zope.app.event.objectevent import ObjectEvent 
    2730 
    2831class IFlavor( Interface ): 
     
    7073    #schema = schema.ObjectField( value_type= ) 
    7174 
     75 
    7276class IFlavorProvider( Interface ): 
    7377    """ 
     
    98102 
    99103 
    100 class IFlavorTool( Interface ): 
    101     """ tool for providing flavor views as cmf actions 
     104class IFlavorsModifiedEvent( IObjectEvent ): 
     105    """ Flavors Changed 
    102106    """ 
     107 
     108    provider = schema.Object( 
     109        description=u"IFlavorProvider for context", 
     110        schema=IFlavorProvider 
     111        ) 
     112     
     113    added_names = schema.List( 
     114        description=u"List of flavor names added to the content", 
     115        value_type=schema.ASCIILine() 
     116        ) 
     117 
     118    removed_names = schema.List( 
     119        description=u"List of flavor names removed from the content", 
     120        value_type=schema.ASCIILine() 
     121        ) 
     122     
     123class FlavorsModifiedEvent( ObjectEvent ): 
     124 
     125    implements( IFlavorsModifiedEvent ) 
     126     
     127    def __init__(self, object, provider, added_names, removed_names): 
     128        self.object = object 
     129        self.provider = provider 
     130        # might be useful.. might not.. 
     131        self.added_names = added_names 
     132        self.removed_names = removed_names 
     133         
     134 
  • Products.ContentFlavors/trunk/provider.py

    r1670 r1674  
    3131from zope.app.annotation.interfaces import IAnnotations 
    3232from zope import component 
    33 from zope.interface import implements 
    34 from interfaces import IFlavorProvider, IFlavor 
     33from zope.interface import implements, directlyProvides, directlyProvidedBy 
     34from zope.interface.interfaces import IInterface 
     35from zope.event import notify 
     36from interfaces import IFlavorProvider, IFlavor, FlavorsModifiedEvent 
    3537from persistent.list import PersistentList 
    3638 
     
    6769     
    6870    def setFlavorNames( self, flavors ): 
    69         flavor_set = list(  set( flavors ) ) 
    70         # verify each exists and is reachable from our location. 
    71         for name in flavor_set: 
    72             flavor = component.getAdapter( self.context, IFlavor, name=name ) 
     71        # could be decomposed via events.. avoids redundant adaptation 
     72        # to have it here, at cost of increased overriding complexity. 
     73        current = set( flavors ) 
     74 
     75        added_ifaces = set() 
     76        removed_ifaces = set() 
     77        remove = [] 
     78        provides = list(directlyProvidedBy( self.context )) 
     79 
     80        # discover flavor validity and new markers 
     81        for name in current: 
     82            # verify each exists and is reachable from our location. 
     83            flavor = component.queryAdapter( self.context, IFlavor, name=name ) 
     84            if not flavor: 
     85                remove.append( name ) 
     86                continue 
     87 
     88            # apply marker interface if present 
     89            if flavor.marker is not None:# and IInterface.providedBy( flavor.marker ): 
     90                if flavor.marker in provides: 
     91                    continue 
     92                added_ifaces.add( flavor.marker ) 
     93 
     94        # gather flavor deltas for notification 
     95        original = set( self._flavors ) - set( remove ) 
     96 
     97        added = current - original 
     98        removed = original - current 
     99 
     100        # gather old markers  
     101        for name in removed: 
     102            flavor = component.queryAdapter( self.context, IFlavor, name=name) 
     103            if flavor is None or flavor.marker is None: 
     104                continue 
     105                     
     106            if flavor.marker in provides: 
     107                removed_ifaces.add( flavor.marker ) 
     108 
     109        # set marker interfaces 
     110        for iface in removed_ifaces: 
     111            provides.remove(iface) 
     112 
     113        for iface in added_ifaces: 
     114            provides.append( iface ) 
     115             
     116        directlyProvides( self.context, *provides ) 
     117 
     118        # set flavors 
    73119        self._flavors[:] = [f for f in flavors if f in flavor_set] 
    74120 
     121        # send notifications  
     122        notify( FlavorModifiedEvent( object, self, added, removed ) ) 
     123         
    75124    flavor_names = property( getFlavorNames, setFlavorNames ) 
    76125 
  • Products.ContentFlavors/trunk/readme.txt

    r1673 r1674  
    7373 - flavors can give additional actions to content, based on registered 
    7474   browser menus definitions for flavor views. 
     75 
     76 - events are fired on invalidation 
     77 
     78 - we attempt an intelligent cache using class and provided by for 
     79   schema retrieval. 
     80 
    7581 
    7682ZCML 
     
    144150=========== 
    145151 
     152aware.py 
     153 
    146154  - schema delegator to adapter for FlavorSchemaProvider 
    147155 
     
    153161==================== 
    154162 
     163schema.py 
     164 
    155165  - provide archetypes schema composition and instance method generation. 
    156166 
    157 Flavor 
     167Flavor  
    158168====== 
    159   
     169 
     170flavor.py  
     171 
    160172  - archetypes schema 
    161173 
     
    167179======================== 
    168180 
     181provider.py 
     182 
    169183  - introspectable for flavors on a given context 
    170184 
     
    175189  - default implementation uses annotations 
    176190 
    177 FlavorTool 
    178 ========== 
    179  
    180   - action provider to pull z3 menu actions for content, based on cmfonfive.actionstool 
    181  
    182   - any ease of access apis nescessary.   
    183  
    184 FlavorUtility 
     191 
     192FlavorVocabulary 
    185193============= 
    186194 
    187   - vocabulary for flavors. 
     195  - vocabulary for flavors management ui 
     196 
     197Todo 
     198---- 
     199 
     200 - Composed Schema work, fields don't currently override properly, they are all 
     201   returned. 
     202 
     203 
    188204 
    189205 
  • Products.ContentFlavors/trunk/schema.py

    r1669 r1674  
    3232from Products.Archetypes.atapi import Schema, CompositeSchema 
    3333 
    34 class FlavorComposedSchema( CompositeSchema, Schema ): 
    35  
    36     def __init__(self): 
    37         self._schemas = [] 
     34from threading import local 
    3835 
    3936class FlavorSchemaComposer( object ): 
     
    4643    def getSchema( self ): 
    4744        if not getattr(self.context, "_v_schema", None): 
    48             try: 
    49                 self._composeFlavorSchema() 
    50             except: 
    51                 import pdb, traceback, sys 
    52                 traceback.print_exc() 
    53                 pdb.post_mortem( sys.exc_info()[-1] ) 
     45            self._composeFlavorSchema() 
    5446        return ImplicitAcquisitionWrapper( self.context._v_schema, self.context ) 
    5547     
     
    9183        # And cache schema in a volatile, composition is costly 
    9284        self.context._v_schema = schema         
     85 
     86 
     87################################# 
     88# Composed Schema 
     89 
     90class FlavorComposedSchema( CompositeSchema, Schema ): 
     91    def __init__(self): 
     92        self._schemas = [] 
     93 
     94################################# 
     95# Cache -- XXX not wired in.. uses instance caches and modification events 
     96#  
     97# we have possible per instance mutations we can cut this down to 
     98# instead per ( class, flavor set combinations ). bonus is we don't 
     99# need to deal with invalidations, since we treat schemas as 
     100# themselves as runtime immutable, else we need to watch schema 
     101# mutation, or do instance cache with invalidation. 
     102 
     103# not sure if this needs to be thread local.. but its safer 
     104     
     105class SchemaCache( object ): 
     106 
     107    def __init__(self): 
     108        self.storage = local() 
     109        self.storage.cache = {} 
     110         
     111    def get( self, object, flavor_names ): 
     112        key = self.getKey( object, flavor_names ) 
     113        return self.storage.cache.get( key ) 
     114 
     115    def set( self, object, schema, flavor_names ): 
     116        key = self.getKey( object, flavor_names ) 
     117        self.storage.cache[ key ] = schema 
     118 
     119    def getKey( self, object, flavor_names ): 
     120        klass = object.__class__ 
     121        return ( klass, tuple( names ) ) 
     122        #iset  = list( interface.providedBy( object ) ) 
     123        #iset.sort() 
     124         
     125GlobalSchemaCache = SchemaCache() 
     126 
     127################################# 
     128# Flavor Modification Event Handler 
     129 
     130def handleModifiedFlavors( event ): 
     131    # invalidate instance cache 
     132    if event.context._v_schema is not None: 
     133        del event.content._v_schema 
     134    #event.context._p_changed = 1 
     135 
     136 
     137################################# 
     138# Method Initialization 
    93139 
    94140def implementMethods(instance, field): 
Note: See TracChangeset for help on using the changeset viewer.