123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 |
- # -*- test-case-name: twisted.spread.test.test_pb -*-
- # Copyright (c) Twisted Matrix Laboratories.
- # See LICENSE for details.
- """
- Persistently cached objects for PB.
- Maintainer: Glyph Lefkowitz
- Future Plans: None known.
- """
- from __future__ import absolute_import, division
- import time
- from twisted.internet import defer
- from twisted.spread import banana, jelly, flavors
- class Publishable(flavors.Cacheable):
- """An object whose cached state persists across sessions.
- """
- def __init__(self, publishedID):
- self.republish()
- self.publishedID = publishedID
- def republish(self):
- """Set the timestamp to current and (TODO) update all observers.
- """
- self.timestamp = time.time()
- def view_getStateToPublish(self, perspective):
- '(internal)'
- return self.getStateToPublishFor(perspective)
-
- def getStateToPublishFor(self, perspective):
- """Implement me to special-case your state for a perspective.
- """
- return self.getStateToPublish()
- def getStateToPublish(self):
- """Implement me to return state to copy as part of the publish phase.
- """
- raise NotImplementedError("%s.getStateToPublishFor" % self.__class__)
- def getStateToCacheAndObserveFor(self, perspective, observer):
- """Get all necessary metadata to keep a clientside cache.
- """
- if perspective:
- pname = perspective.perspectiveName
- sname = perspective.getService().serviceName
- else:
- pname = "None"
- sname = "None"
- return {"remote": flavors.ViewPoint(perspective, self),
- "publishedID": self.publishedID,
- "perspective": pname,
- "service": sname,
- "timestamp": self.timestamp}
- class RemotePublished(flavors.RemoteCache):
- """The local representation of remote Publishable object.
- """
- isActivated = 0
- _wasCleanWhenLoaded = 0
- def getFileName(self, ext='pub'):
- return ("%s-%s-%s.%s" %
- (self.service, self.perspective, str(self.publishedID), ext))
-
- def setCopyableState(self, state):
- self.__dict__.update(state)
- self._activationListeners = []
- try:
- with open(self.getFileName(), "rb") as dataFile:
- data = dataFile.read()
- except IOError:
- recent = 0
- else:
- newself = jelly.unjelly(banana.decode(data))
- recent = (newself.timestamp == self.timestamp)
- if recent:
- self._cbGotUpdate(newself.__dict__)
- self._wasCleanWhenLoaded = 1
- else:
- self.remote.callRemote('getStateToPublish').addCallbacks(self._cbGotUpdate)
- def __getstate__(self):
- other = self.__dict__.copy()
- # Remove PB-specific attributes
- del other['broker']
- del other['remote']
- del other['luid']
- # remove my own runtime-tracking stuff
- del other['_activationListeners']
- del other['isActivated']
- return other
- def _cbGotUpdate(self, newState):
- self.__dict__.update(newState)
- self.isActivated = 1
- # send out notifications
- for listener in self._activationListeners:
- listener(self)
- self._activationListeners = []
- self.activated()
- with open(self.getFileName(), "wb") as dataFile:
- dataFile.write(banana.encode(jelly.jelly(self)))
- def activated(self):
- """Implement this method if you want to be notified when your
- publishable subclass is activated.
- """
-
- def callWhenActivated(self, callback):
- """Externally register for notification when this publishable has received all relevant data.
- """
- if self.isActivated:
- callback(self)
- else:
- self._activationListeners.append(callback)
- def whenReady(d):
- """
- Wrap a deferred returned from a pb method in another deferred that
- expects a RemotePublished as a result. This will allow you to wait until
- the result is really available.
- Idiomatic usage would look like::
- publish.whenReady(serverObject.getMeAPublishable()).addCallback(lookAtThePublishable)
- """
- d2 = defer.Deferred()
- d.addCallbacks(_pubReady, d2.errback,
- callbackArgs=(d2,))
- return d2
- def _pubReady(result, d2):
- '(internal)'
- result.callWhenActivated(d2.callback)
|