diff options
Diffstat (limited to 'www/dev_guide/cache.rst')
-rw-r--r-- | www/dev_guide/cache.rst | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/www/dev_guide/cache.rst b/www/dev_guide/cache.rst new file mode 100644 index 0000000..07775f2 --- /dev/null +++ b/www/dev_guide/cache.rst @@ -0,0 +1,404 @@ +Caching placeholders and #cache +=============================== + +(cache) + +Dynamic placeholder - no cache +------------------------------ + +(cache.dynamic) + +The template: + +:: + + Dynamic variable: $voom + +The command line and the output: + +:: + + % voom='Voom!' python x.py --env + Dynamic variable: Voom! + +The generated code: + +:: + + write('Dynamic variable: ') + write(filter(VFS(SL,"voom",1))) # generated from '$voom' at line 1, col 20. + write('\n') + +Just what we expected, like any other dynamic placeholder. + +Static placeholder +------------------ + +(cache.static) + +The template: + +:: + + Cached variable: $*voom + +The command line and output: + +:: + + % voom='Voom!' python x.py --env + Cached variable: Voom! + +The generated code, with line numbers: + +:: + + 1 write('Cached variable: ') + 2 ## START CACHE REGION: at line, col (1, 19) in the source. + 3 RECACHE = True + 4 if not self._cacheData.has_key('19760169'): + 5 pass + 6 else: + 7 RECACHE = False + 8 if RECACHE: + 9 orig_trans = trans + 10 trans = cacheCollector = DummyTransaction() + 11 write = cacheCollector.response().write + 12 write(filter(VFS(SL,"voom",1))) # generated from '$*voom' at line 1, + # col 19. + 13 trans = orig_trans + 14 write = trans.response().write + 15 self._cacheData['19760169'] = cacheCollector.response().getvalue() + 16 del cacheCollector + 17 write(self._cacheData['19760169']) + 18 ## END CACHE REGION + + 19 write('\n') + +That one little star generated a whole lotta code. First, instead +of an ordinary {VFS} lookup (searchList) lookup, it converted the +placeholder to a lookup in the {.\_cacheData} dictionary. Cheetah +also generated a unique key ({'19760169'}) for our cached item - +this is its cache ID. + +Second, Cheetah put a pair of if-blocks before the {write}. The +first (lines 3-7) determine whether the cache value is missing or +out of date, and sets local variable {RECACHE} true or false. This +stanza may look unnecessarily verbose - lines 3-7 could be +eliminated if line 8 was changed to + +:: + + if not self._cacheData.has_key('19760169'): + +- but this model is expandable for some of the cache features we'll +see below. + +The second if-block, lines 8-16, do the cache updating if +necessary. Clearly, the programmer is trying to stick as close to +normal (dynamic) workflow as possible. Remember that {write}, even +though it looks like a local function, is actually a method of a +file-like object. So we create a temporary file-like object to +divert the {write} object into, then read the result and stuff it +into the cache. + +Timed-refresh placeholder +------------------------- + +(cache.timed) + +The template: + +:: + + Timed cache: $*.5m*voom + +The command line and the output: + +:: + + % voom='Voom!' python x.py --env + Timed cache: Voom! + +The generated method's docstring: + +:: + + """ + This is the main method generated by Cheetah + This cache will be refreshed every 30.0 seconds. + """ + +The generated code: + +:: + + 1 write('Timed cache: ') + 2 ## START CACHE REGION: at line, col (1, 15) in the source. + 3 RECACHE = True + 4 if not self._cacheData.has_key('55048032'): + 5 self.__cache55048032__refreshTime = currentTime() + 30.0 + 6 elif currentTime() > self.__cache55048032__refreshTime: + 7 self.__cache55048032__refreshTime = currentTime() + 30.0 + 8 else: + 9 RECACHE = False + 10 if RECACHE: + 11 orig_trans = trans + 12 trans = cacheCollector = DummyTransaction() + 13 write = cacheCollector.response().write + 14 write(filter(VFS(SL,"voom",1))) # generated from '$*.5m*voom' at + # line 1, col 15. + 15 trans = orig_trans + 16 write = trans.response().write + 17 self._cacheData['55048032'] = cacheCollector.response().getvalue() + 18 del cacheCollector + 19 write(self._cacheData['55048032']) + 20 ## END CACHE REGION + + 21 write('\n') + +This code is identical to the static cache example except for the +docstring and the first if-block. (OK, so the cache ID is different +and the comment on line 14 is different too. Big deal.) + +Each timed-refresh cache item has a corrsponding private attribute +{.\_\_cache########\_\_refreshTime} giving the refresh time in +ticks (=seconds since January 1, 1970). The first if-block (lines +3-9) checks whether the cache value is missing or its update time +has passed, and if so, sets {RECACHE} to true and also schedules +another refresh at the next interval. + +The method docstring reminds the user how often the cache will be +refreshed. This information is unfortunately not as robust as it +could be. Each timed-cache placeholder blindly generates a line in +the docstring. If all refreshes are at the same interval, there +will be multiple identical lines in the docstring. If the refreshes +are at different intervals, you get a situation like this: + +:: + + """ + This is the main method generated by Cheetah + This cache will be refreshed every 30.0 seconds. + This cache will be refreshed every 60.0 seconds. + This cache will be refreshed every 120.0 seconds. + """ + +The docstring tells only that "something" will be refreshed every +60.0 seconds, but doesn't reveal { which} placeholder that is. Only +if you know the relative order of the placeholders in the template +can you figure that out. + +Timed-refresh placeholder with braces +------------------------------------- + +(cache.timed.braces) + +This example is the same but with the long placeholder syntax. It's +here because it's a Cheetah FAQ whether to put the cache interval +inside or outside the braces. (It's also here so I can look it up +because I frequently forget.) The answer is: outside. The braces go +around only the placeholder name (and perhaps some output-filter +arguments.) + +The template: + +:: + + Timed with {}: $*.5m*{voom} + +The output: + +:: + + Timed with {}: Voom! + +The generated code differs only in the comment. Inside the +cache-refresh if-block: + +:: + + write(filter(VFS(SL,"voom",1))) # generated from '$*.5m*{voom}' at line 1, + #col 17. + +If you try to do it this way: + +:: + + Timed with {}: ${*.5m*voom} ## Wrong! + +you get: + +:: + + Timed with {}: ${*.5m*voom} + +``${`` is not a valid placeholder, so it gets treated as ordinary +text. + +#cache +------ + +(cache.directive) + +The template: + +:: + + #cache + This is a cached region. $voom + #end cache + +The output: + +:: + + This is a cached region. Voom! + +The generated code: + +:: + + 1 ## START CACHE REGION: at line, col (1, 1) in the source. + 2 RECACHE = True + 3 if not self._cacheData.has_key('23711421'): + 4 pass + 5 else: + 6 RECACHE = False + 7 if RECACHE: + 8 orig_trans = trans + 9 trans = cacheCollector = DummyTransaction() + 10 write = cacheCollector.response().write + 11 write('This is a cached region. ') + 12 write(filter(VFS(SL,"voom",1))) # generated from '$voom' at line 2, + # col 27. + 13 write('\n') + 14 trans = orig_trans + 15 write = trans.response().write + 16 self._cacheData['23711421'] = cacheCollector.response().getvalue() + 17 del cacheCollector + 18 write(self._cacheData['23711421']) + 19 ## END CACHE REGION + +This is the same as the {$\*voom} example, except that the plain +text around the placeholder is inside the second if-block. + +#cache with timer and id +------------------------ + +(cache.directive.timer) + +The template: + +:: + + #cache timer='.5m', id='cache1' + This is a cached region. $voom + #end cache + +The output: + +:: + + This is a cached region. Voom! + +The generated code is the same as the previous example except the +first if-block: + +:: + + RECACHE = True + if not self._cacheData.has_key('13925129'): + self._cacheIndex['cache1'] = '13925129' + self.__cache13925129__refreshTime = currentTime() + 30.0 + elif currentTime() > self.__cache13925129__refreshTime: + self.__cache13925129__refreshTime = currentTime() + 30.0 + else: + RECACHE = False + +#cache with test: expression and method conditions +-------------------------------------------------- + +(cache.directive.test) + +The template: + +:: + + #cache test=$isDBUpdated + This is a cached region. $voom + #end cache + +(Analysis postponed: bug in Cheetah produces invalid Python.) + +The template: + +:: + + #cache id='cache1', test=($isDBUpdated or $someOtherCondition) + This is a cached region. $voom + #end cache + +The output: + +:: + + This is a cached region. Voom! + +The first if-block in the generated code: + +:: + + RECACHE = True + if not self._cacheData.has_key('36798144'): + self._cacheIndex['cache1'] = '36798144' + elif (VFS(SL,"isDBUpdated",1) or VFS(SL,"someOtherCondition",1)): + RECACHE = True + else: + RECACHE = False + +The second if-block is the same as in the previous example. If you +leave out the {()} around the test expression, the result is the +same, although it may be harder for the template maintainer to +read. + +You can even combine arguments, although this is of questionable +value. + +The template: + +:: + + #cache id='cache1', timer='30m', test=$isDBUpdated or $someOtherCondition + This is a cached region. $voom + #end cache + +The output: + +:: + + This is a cached region. Voom! + +The first if-block: + +:: + + RECACHE = True + if not self._cacheData.has_key('88939345'): + self._cacheIndex['cache1'] = '88939345' + self.__cache88939345__refreshTime = currentTime() + 1800.0 + elif currentTime() > self.__cache88939345__refreshTime: + self.__cache88939345__refreshTime = currentTime() + 1800.0 + elif VFS(SL,"isDBUpdated",1) or VFS(SL,"someOtherCondition",1): + RECACHE = True + else: + RECACHE = False + +We are planning to add a {'varyBy'} keyword argument in the future +that will allow separate cache instances to be created for a +variety of conditions, such as different query string parameters or +browser types. This is inspired by ASP.net's varyByParam and +varyByBrowser output caching keywords. Since this is not +implemented yet, I cannot provide examples here. + + |