Package: collective.beaker
collective.beaker - Beaker integration for Zope 2 and Plone.This package provides a means to configure theBeakersession management and caching framework for
use within a Zope 2 (and Plone) environment.Ordinarily, Beaker is configured using WSGI middleware. However, Zope 2 does
not (yet) run WSGI by default (unless you userepoze.zope2). This package
provides an alternative configuration syntax, based in zope.conf.InstallationTo use this package, you need to install it. Typically, you would do this
via theinstall_requiresline in your own package’ssetup.py:install_requires=[
      ...
      'collective.beaker',
  ],You can also install it using theeggsline in buildout.cfg, e.g.:[instance]
...
eggs =
    ...
    collective.beakerIf you are on Zope 2.10 (e.g. for Plone 3), you will also need to install theZPublisherEventsBackportpackage. You can
get that as a dependency by depending in the[Zope2.10]extra, e.g.:[instance]
...
eggs =
    ...
    collective.beaker [Zope2.10]If you are in Zope 2.12 or later, the relevant events are included by default,
and you shouldnotdepend onZPublisherEventsBackportor the[Zope2.10]extra.Configuring BeakerTo configure Beaker, add a section to yourzope.conflike this:<product-config beaker>
    cache.type              file
    cache.data_dir          /tmp/cache/data
    cache.lock_dir          /tmp/cache/lock
    cache.regions           short, long
    cache.short.expire      60
    cache.long.expire       3600

    session.type            file
    session.data_dir        /tmp/sessions/data
    session.lock_dir        /tmp/sessions/lock
    session.key             beaker.session
    session.secret          secret
</product-config>If you are using buildout andplone.recipe.zope2instanceto generate yourzope.conf, you can use the following option:[instance]
...
zope-conf-additional =
    <product-config beaker>
        cache.type              file
        cache.data_dir          ${buildout:directory}/var/cache/data
        cache.lock_dir          ${buildout:directory}/var/cache/lock
        cache.regions           short, long
        cache.short.expire      60
        cache.long.expire       3600

        session.type            file
        session.data_dir        ${buildout:directory}/var/sessions/data
        session.lock_dir        ${buildout:directory}/var/sessions/lock
        session.key             beaker.session
        session.secret          secret
    </product-config>Here, we have also used a buildout substitution to put the cache and session
directories inside the buildout directory.You also need to load the configuration for thecollective.beakerpackage.
This can be done with a ZCML line like this:<include package="collective.beaker" />This could be in your ownconfigure.zcml, or in a ZCML slug. If you are
using buildout andplone.recipe.zope2instance, you can install a slug
by adding azcmlline like:[instance]
...
zcml =
    collective.beakerThe settings within the<product-configbeaker>section are passed
directly to Beaker. See theBeaker configuration documentationfor more details about the
available options.Please note that:All cache settings must be prefixed withcache.All session settings must be prefixed withsession.For the session settings, the following defaults apply:invalidate_corrupt=True, so corrupt sessions are invalidatedtype=Noneanddata_dir=None, thus defaulting to an in-memory
sessionkey=beaker.session.id- this is the cookie keytimeout=None, so sessions don’t time outsecret=None, so session cookies are not encryptedlog_file=None, so there is no loggingUsing sessionsTo obtain a Beaker session from a request, use the following pattern:>>> from collective.beaker.interfaces import ISession
>>> session = ISession(request)See theBeaker session documentationfor details on the resultant
session object. You can more or less treat it as a dictionary with string
keys:>>> session['username'] = currentUserNameIf you modify the session, you need to manually save it:>>> session.save()Alternatively, you can set thesession.autoconfiguration key toon,
and sessions will be automatically saved on each request.If you want to delete the session, use:>>> session.delete()Note that Beaker does not automatically expire/remove sessions, so you may
need to do this yourself.If you want to invalidate the session and create a new one, use:>>> session.invalidate()Note that the session is configured when each request is begun, based on the
session settings read fromzope.conf. It is possible to override these
by registering a utility providingISessionConfigfrom this package. The
utility must implement the dict API (you can use a regular dict, or a
persistent mapping object, for example). This allows, for example, a site-
local utility to provide per-site session data.Using cachingThe Beaker documentation illustrates how to create a cache manager as a
global variable. TheCacheManagerinstance provides decorators and
functions to use the cache. You can still use this pattern, but this will
not use any of the configuration managed bycollective.beakerin zope.confYou can, however, use cache regions, as well as the explicit caching API. At
runtime (butnotin module scope) you can obtain a BeakerCacheManagerthat is configured as perzope.conflike so:>>> from zope.component import getUtility
>>> from collective.beaker.interfaces import ICacheManager

>>> cacheManager = getUtility(ICacheManager)You can now use this programmatically as per the Beaker documentation, e.g.:>>> myCache = cacheManager.get_cache('mynamespace', expire=1800)Refer to theBeaker caching documentationfor details.You can also use caching region decorators, e.g. with:>>> from beaker.cache import cache_region
>>> @cache_region('short')
... def my_function():
...     ...Provided that the ‘short’ region is configured (as in thezope.confexample above), this will lazily look up the region settings and use those
for caching.To invalidate the cache, you could call:>>> from beaker.cache import region_invalidate
>>> region_invalidate(my_function, 'short')Again, refer to the Beaker documentation for details.TestingIf you are writing integration tests for code that uses beaker sessions or
caches, you need to ensure that beaker is configured before you call the
relevant code. Otherwise, you are liable to get component lookup errors
onISessionConfigorICacheManagerlayers. This is because integration
tests written withZopeTestCase/PloneTestCasedo not read yourzope.confand so thecollective.beakerconfiguration code does not
have any configuration data when it is loaded.You can deal with this in one of two ways:Register your ownISessionConfigand/orICacheManagerutilities.
Seeinterfaces.pyfor details.Provide “fake” ZConfig settings before ZCML processing takes place.You can use the test layer incollective.beaker.testing.BeakerConfigLayerto do the latter. You need to make sure that this layer is mixed in before
any layer that executes ZCML processing. For example:from colective.beaker.testing import BeakerConfigLayer
from Products.PloneTestCase.layer import PloneSiteLayer
from Products.PloneTestCase.ptc import PloneTestCase

class MyLayer(BeakerConfigLayer, PloneSiteLayer):
    pass

class TestCase(PloneTestCase):

    layer = MyLayerYou can of course add your ownsetUpandtearDownmethods to the
layer. The important thing is that theBeakerConfigLayercomes before
thePloneSiteLayer, which will configure the site.This setup will use default settings (seetesting.pyfor the exact
values), but you can manipulate these on a per-setting basis. For example:from zope.component import getUtility
from collective.beaker.interfaces import ISessionConfig

config = getUtility(ISessionConfig)
config['secret'] = 'password'Bear in mind that this is normally a global utility, so any changes will
cross test boundaries unless you also tear down your settings properly. Thus,
it is probably more appropriate to do this setup in a layer than in an
individual test.When writing tests that use Beaker sessions, if you are not performing
functional testing using something likezope.testbrowser, you may also
need to simulate the request start/end events thatcollective.beakerlistens to in order to configure the session.For example:from collective.beaker.session import initializeSession, closeSession

...

class TestCase(PloneTestCase):

    layer = MyLayer

    def test_something(self):
        request = self.app.REQUEST
        initializeSession(request)

        # perform your test here

        closeSession(request)In a unit test, it is probably easier to just provide a mockISessionadapter for the request. There is a mock session implementation in this
package which can help you with that:import unittest
import zope.component.testing

from zope.component import provideAdapter
from collective.beaker.testing import testingSession

from collective.beaker.interfaces import ISession
from zope.publisher.browser import TestRequest


class MyUnitTestCase(unittest.TestCase):

    def setUp(self):
        provideAdapter(testingSession)
        ...

    def tearDown(self):
        zope.component.testing.tearDown()

    def test_something(self):
        request = TestRequest()
        session = ISession(request)
        ...Like the “real” session, the test session is tied to the request, so you
should get the same object back each time you look up the adapter on the
request. You can also check the following properties to see how the session
has been used:_savedis True ifsave()has been called once._invalidatedis True ifinvalidate()has been called once._deletedis True ifdelete()has been called once.Finally,accessed()will return True and thelast_accessedattribute
will be set to the current date/time when common dictionary operations are
used.Changelog for collective.beaker1.0b3 (2011-05-11)fixed spelling issue in setup.py
[ajung]1.0b2 (2010-01-23)Fix support for signed cookies (thesecretparameter) in sessions.
[optilude]Provide better testing tools and more resilient test setup.
[optilude]Make the ZCML directive more resilient to configurations where there is
no product_config. This can happen in test setup, for example.
[optilude]1.0b1 (2009-12-10)Initial release
[optilude]