| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
| 2 # See LICENSE for details. | |
| 3 | |
| 4 | |
| 5 """ | |
| 6 Persistently cached objects for PB. | |
| 7 | |
| 8 Maintainer: U{Glyph Lefkowitz<mailto:glyph@twistedmatrix.com>} | |
| 9 | |
| 10 Future Plans: None known. | |
| 11 """ | |
| 12 | |
| 13 # Twisted imports | |
| 14 from twisted.internet import defer | |
| 15 | |
| 16 # sibling imports | |
| 17 import jelly | |
| 18 import banana | |
| 19 import flavors | |
| 20 | |
| 21 # System Imports | |
| 22 import time | |
| 23 | |
| 24 class Publishable(flavors.Cacheable): | |
| 25 """An object whose cached state persists across sessions. | |
| 26 """ | |
| 27 def __init__(self, publishedID): | |
| 28 self.republish() | |
| 29 self.publishedID = publishedID | |
| 30 | |
| 31 def republish(self): | |
| 32 """Set the timestamp to current and (TODO) update all observers. | |
| 33 """ | |
| 34 self.timestamp = time.time() | |
| 35 | |
| 36 def view_getStateToPublish(self, perspective): | |
| 37 '(internal)' | |
| 38 return self.getStateToPublishFor(perspective) | |
| 39 | |
| 40 def getStateToPublishFor(self, perspective): | |
| 41 """Implement me to special-case your state for a perspective. | |
| 42 """ | |
| 43 return self.getStateToPublish() | |
| 44 | |
| 45 def getStateToPublish(self): | |
| 46 """Implement me to return state to copy as part of the publish phase. | |
| 47 """ | |
| 48 raise NotImplementedError("%s.getStateToPublishFor" % self.__class__) | |
| 49 | |
| 50 def getStateToCacheAndObserveFor(self, perspective, observer): | |
| 51 """Get all necessary metadata to keep a clientside cache. | |
| 52 """ | |
| 53 if perspective: | |
| 54 pname = perspective.perspectiveName | |
| 55 sname = perspective.getService().serviceName | |
| 56 else: | |
| 57 pname = "None" | |
| 58 sname = "None" | |
| 59 | |
| 60 return {"remote": flavors.ViewPoint(perspective, self), | |
| 61 "publishedID": self.publishedID, | |
| 62 "perspective": pname, | |
| 63 "service": sname, | |
| 64 "timestamp": self.timestamp} | |
| 65 | |
| 66 class RemotePublished(flavors.RemoteCache): | |
| 67 """The local representation of remote Publishable object. | |
| 68 """ | |
| 69 isActivated = 0 | |
| 70 _wasCleanWhenLoaded = 0 | |
| 71 def getFileName(self, ext='pub'): | |
| 72 return ("%s-%s-%s.%s" % | |
| 73 (self.service, self.perspective, str(self.publishedID), ext)) | |
| 74 | |
| 75 def setCopyableState(self, state): | |
| 76 self.__dict__.update(state) | |
| 77 self._activationListeners = [] | |
| 78 try: | |
| 79 data = open(self.getFileName()).read() | |
| 80 except IOError: | |
| 81 recent = 0 | |
| 82 else: | |
| 83 newself = jelly.unjelly(banana.decode(data)) | |
| 84 recent = (newself.timestamp == self.timestamp) | |
| 85 if recent: | |
| 86 self._cbGotUpdate(newself.__dict__) | |
| 87 self._wasCleanWhenLoaded = 1 | |
| 88 else: | |
| 89 self.remote.callRemote('getStateToPublish').addCallbacks(self._cbGot
Update) | |
| 90 | |
| 91 def __getstate__(self): | |
| 92 other = self.__dict__.copy() | |
| 93 # Remove PB-specific attributes | |
| 94 del other['broker'] | |
| 95 del other['remote'] | |
| 96 del other['luid'] | |
| 97 # remove my own runtime-tracking stuff | |
| 98 del other['_activationListeners'] | |
| 99 del other['isActivated'] | |
| 100 return other | |
| 101 | |
| 102 def _cbGotUpdate(self, newState): | |
| 103 self.__dict__.update(newState) | |
| 104 self.isActivated = 1 | |
| 105 # send out notifications | |
| 106 for listener in self._activationListeners: | |
| 107 listener(self) | |
| 108 self._activationListeners = [] | |
| 109 self.activated() | |
| 110 open(self.getFileName(), "wb").write(banana.encode(jelly.jelly(self))) | |
| 111 | |
| 112 def activated(self): | |
| 113 """Implement this method if you want to be notified when your | |
| 114 publishable subclass is activated. | |
| 115 """ | |
| 116 | |
| 117 def callWhenActivated(self, callback): | |
| 118 """Externally register for notification when this publishable has receiv
ed all relevant data. | |
| 119 """ | |
| 120 if self.isActivated: | |
| 121 callback(self) | |
| 122 else: | |
| 123 self._activationListeners.append(callback) | |
| 124 | |
| 125 def whenReady(d): | |
| 126 """ | |
| 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. | |
| 130 | |
| 131 Idiomatic usage would look like:: | |
| 132 | |
| 133 publish.whenReady(serverObject.getMeAPublishable()).addCallback(lookAtTh
ePublishable) | |
| 134 """ | |
| 135 d2 = defer.Deferred() | |
| 136 d.addCallbacks(_pubReady, d2.errback, | |
| 137 callbackArgs=(d2,)) | |
| 138 return d2 | |
| 139 | |
| 140 def _pubReady(result, d2): | |
| 141 '(internal)' | |
| 142 result.callWhenActivated(d2.callback) | |
| OLD | NEW |