1
# -*- test-case-name: twisted.test.test_pb -*-
2
# Copyright (c) 2001-2008 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)