# -*- coding: utf-8 -*- """ Using TokyoTyrant as a Model class Soldier(TokyoTyrantStorage): hp = TokyoTyrantStorage.Field(default=0) mp = TokyoTyrantStorage.Field(default=0) cain = Soldier('Cain') cain.hp = 100 cain.mp = 50 cain.save() abel = Soldier('Abel') abel.hp = 50 abel.mp = 100 abel.save() cain = Soldier('Cain') cain.hp -= 80 cain.save() abel = Soldier('Abel') abel.mp -= 20 abel.save() cain = Soldier('Cain') cain.hp = min(cain.hp + 50, 100) cain.save() cain = Soldier('Cain') cain.hp Out: 70 """ import logging from tokyotyrant import get_client import msgpack class TokyoTyrantStorage(object): """ Base class for using TokyoTyrant as a dictionary Please use it by inheritance """ packer = msgpack.Packer() tt_client = get_client() def __new__(cls, *args, **kwargs): for k, v in vars(cls).items(): if isinstance(v, TokyoTyrantStorage.Field): v.set_field_name(k) return object.__new__(cls) def __init__(self, key_object): """ @param key_object: Django model instance, etc. player or osuser Override and if you don't want to call super's __init__, just call self.after_init(key_part) """ self.key_object = key_object key_part = getattr(key_object, 'pk', str(key_object)) self.after_init(key_part) def after_init(self, key_part): """ Second half of initialization process @param (str)key_part: Unique string used as the base for key generation """ self.kvs_key = '%s::%s' % (self.__class__.__name__, key_part) bulk_storage = self.tt_client.get(self.kvs_key, None) if bulk_storage: self.storage = msgpack.unpackb(bulk_storage) else: self.storage = {} def get(self, k, default=None): return self.storage.get(k,default) def set(self, k, value): self.storage[k] = value def save(self): """ Save storage """ self.tt_client.set(self.kvs_key, self.packer.pack(self.storage)) def delete(self): """ Delete storage """ self.tt_client.out(self.kvs_key) self.storage = {} def dump(self): logging.debug("[TokyoTyrantStorage] dump: kvs_key=%s, storage=%r" % (self.kvs_key, self.storage)) class Field(object): """ Field that can be used in TokyoTyrantStorage Use it like score = TokyoTyrantStorage.Field(default=0). """ def __init__(self, key_name=None, default=None): self._key_name = key_name self.default = default def __get__(self, inst, type=None): assert isinstance(inst, TokyoTyrantStorage), '%s use only TokyoTyrantStorage class.' % self.__class__.__name__ return inst.get(self.key_name, self.default) def __set__(self, inst, value): assert isinstance(inst, TokyoTyrantStorage), '%s use only TokyoTyrantStorage class.' % self.__class__.__name__ inst.set(self.key_name, value) def set_field_name(self, field_name): # Called from TokyoTyrantStorage.__new__ self.field_name = field_name @property def key_name(self): return self._key_name or self.field_name
Comments