1 # -*- test-case-name: twisted.test.test_pb -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
6 Persistently cached objects for PB.
8 Maintainer: Glyph Lefkowitz
10 Future Plans: None known.
15 from twisted.internet import defer
16 from twisted.spread import banana, jelly, flavors
19 class Publishable(flavors.Cacheable):
20 """An object whose cached state persists across sessions.
22 def __init__(self, publishedID):
24 self.publishedID = publishedID
27 """Set the timestamp to current and (TODO) update all observers.
29 self.timestamp = time.time()
31 def view_getStateToPublish(self, perspective):
33 return self.getStateToPublishFor(perspective)
35 def getStateToPublishFor(self, perspective):
36 """Implement me to special-case your state for a perspective.
38 return self.getStateToPublish()
40 def getStateToPublish(self):
41 """Implement me to return state to copy as part of the publish phase.
43 raise NotImplementedError("%s.getStateToPublishFor" % self.__class__)
45 def getStateToCacheAndObserveFor(self, perspective, observer):
46 """Get all necessary metadata to keep a clientside cache.
49 pname = perspective.perspectiveName
50 sname = perspective.getService().serviceName
55 return {"remote": flavors.ViewPoint(perspective, self),
56 "publishedID": self.publishedID,
59 "timestamp": self.timestamp}
61 class RemotePublished(flavors.RemoteCache):
62 """The local representation of remote Publishable object.
65 _wasCleanWhenLoaded = 0
66 def getFileName(self, ext='pub'):
67 return ("%s-%s-%s.%s" %
68 (self.service, self.perspective, str(self.publishedID), ext))
70 def setCopyableState(self, state):
71 self.__dict__.update(state)
72 self._activationListeners = []
74 dataFile = file(self.getFileName(), "rb")
75 data = dataFile.read()
80 newself = jelly.unjelly(banana.decode(data))
81 recent = (newself.timestamp == self.timestamp)
83 self._cbGotUpdate(newself.__dict__)
84 self._wasCleanWhenLoaded = 1
86 self.remote.callRemote('getStateToPublish').addCallbacks(self._cbGotUpdate)
88 def __getstate__(self):
89 other = self.__dict__.copy()
90 # Remove PB-specific attributes
94 # remove my own runtime-tracking stuff
95 del other['_activationListeners']
96 del other['isActivated']
99 def _cbGotUpdate(self, newState):
100 self.__dict__.update(newState)
102 # send out notifications
103 for listener in self._activationListeners:
105 self._activationListeners = []
107 dataFile = file(self.getFileName(), "wb")
108 dataFile.write(banana.encode(jelly.jelly(self)))
113 """Implement this method if you want to be notified when your
114 publishable subclass is activated.
117 def callWhenActivated(self, callback):
118 """Externally register for notification when this publishable has received all relevant data.
123 self._activationListeners.append(callback)
127 Wrap a deferred returned from a pb method in another deferred that
128 expects a RemotePublished as a result. This will allow you to wait until
129 the result is really available.
131 Idiomatic usage would look like::
133 publish.whenReady(serverObject.getMeAPublishable()).addCallback(lookAtThePublishable)
135 d2 = defer.Deferred()
136 d.addCallbacks(_pubReady, d2.errback,
140 def _pubReady(result, d2):
142 result.callWhenActivated(d2.callback)