The Ext JS library is a client side JavaScript library which makes lots of client side tasks easy.
In the source distribution there is a file source/state/CookieProvider.js which is a wrapper around cookies and makes cookie reading/writing simple. Using this mechanism strings, numbers, arrays and objects can be stored in a cookie using a serialization defined in source/state/Provider.js.
What if these serialized objects need to be read by Python on the server? Or what if cookies need to be written by Python on the server such that they are understood by the Ext JS library on the client? This is where the following comes in handy.
The module extjs implements the same serialization of strings, numbers, arrays, objects as the javascript implementation in Ext JS. So if you stick {'hello': [5, 1.2, 'xxx'], 'yyy': {5: 'five', 6: 'six'}} into a cookie on the client from JavaScript it will be available to Python code as a genuine Python object and similarly, Python objects written into a cookie will be available to the client side JavaScript as genuine JavaScript objects.
Here is the code of the extjs module:
# extjs.py
#
# This code is in the public domain.
# 2008 Daniel Fetchinson
from urllib import quote, unquote
from UserDict import DictMixin
from datetime import datetime
from cherrypy import request, response
class serialize(object):
"""See ext-2.2/source/state/Provider.js for the serialization algorithm."""
def decode(self, value):
if value[0] in 'snbdao' and value[1:4] == quote(':'):
return getattr(self, '_decode_' + value[0])(unquote(value[4:]))
else:
raise TypeError('encoded strings start with "[snbdao]:"'
' not %s' % unquote(value[:4]))
def encode(self, value):
if isinstance(value, basestring):
t = 's'
elif isinstance(value, (int, float)):
t = 'n'
elif isinstance(value, bool):
t = 'b'
elif isinstance(value, datetime):
t = 'd'
elif isinstance(value, (list, tuple)):
t = 'a'
elif isinstance(value, dict):
t = 'o'
else:
t = 's'
return quote(getattr(self, '_encode_' + t)(value))
# decode methods
def _decode_s(self, value):
return value
def _decode_n(self, value):
try:
return int(value)
except ValueError:
try:
return float(value)
except ValueError:
raise TypeError('n: should be followed by an int or float,'
' not %s' % value)
def _decode_b(self, value):
return value == '1'
def _decode_d(self, value):
return datetime.strptime(value, '%Y-%m-%dT%H:%M:%S')
def _decode_a(self, value):
return [self.decode(item) for item in value.split('^')]
def _decode_o(self, value):
return dict((k, self.decode(v))
for k, v in (item.split('=') for item in value.split('^')))
# encode methods
def _encode_s(self, value):
return 's:' + value
def _encode_n(self, value):
return 'n:' + str(value)
def _encode_b(self, value):
if bool(value):
return 'b:1'
else:
return 'b:0'
def _encode_d(self, value):
return 'd:' + value.strftime('%Y-%m-%dT%H:%M:%S')
def _encode_a(self, value):
return 'a:' + '^'.join(self.encode(item) for item in value)
def _encode_o(self, value):
return 'o:' + '^'.join('='.join([ k, self.encode(v) ])
for k, v in value.items())
# The serialization algorithm can be used through
# serializer.encode and serializer.decode
serializer = serialize()
# every Ext JS cookie name is prefixed by this
prefix = 'ys-'
class _cookie(DictMixin):
"""Manage extjs planted cookies. Keys start with 'ys-', values
are serialized according to ext-2.2/source/state/Provider.js"""
def __init__(self):
self._c = {}
def __call__(self):
"""Get all extjs cookies from request."""
for key in request.simple_cookie.keys():
if key.startswith(prefix):
self._c[key[3:]] = serializer.decode(
request.simple_cookie[key].value)
return self
def __getitem__(self, item):
return self._c[item]
def __setitem__(self, item, value):
self._c[item] = value
response.simple_cookie[prefix + item] = serializer.encode(value)
def __delitem__(self, item):
del self._c[item]
response.simple_cookie[prefix + item]['expires'] = 0
def keys(self):
return self._c.keys()
# Use this in controllers as c = cookie() and all Ext JS planted cookies
# will be available through the dict c as Python objects
cookie = _cookie()