KvsModel: KVS(TokyoTyrantなど)をモデルっぽく使う

投稿者: ytyng 12年, 10ヶ月 前
# -*- coding: utf-8 -*-

"""
KVS(TokyoTyrantなど)をモデルっぽく使う
今回は Django のキャッシュを扱う

モデルといってもリレーションや検索などを提供するわけではなく
フィールド定義がmodels.Modelみたいにできる。
(要は、中でもってるディクショナリ(storage)のキー名を変数名から自動的につけてくれる)


class Soldier(KvsModel):
    hp = KvsModel.Field(default=0)
    mp = KvsModel.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

cain.dump()
Out: "[Soldier] dump: kvs_key=Soldier::Cain, storage={'hp': 70, 'mp': 50}"

abel.dump()
Out: "[Soldier] dump: kvs_key=Soldier::Abel, storage={'hp': 50, 'mp': 80}"

"""

import msgpack #pip install msgpack-python
from django.core.cache import cache as kvs_backend

class KvsModel(object):
    """
    TokyoTyrantなどをディクショナリとして使う基底クラス
    継承して使ってください
    """
    packer = msgpack.Packer()

    def __new__(cls, *args, **kwargs):
        for k, v in vars(cls).iteritems():
            if isinstance(v, KvsModel.Field):
                v.set_field_name(k)
        return object.__new__(cls)

    def __init__(self, key_object):
        """
        @param key_object djangoのモデルインスタンスなど。
        オーバーライドして、superの__init__を呼ぶ必要が無い時は
        そのまま 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):
        """
        初期化処理の後半
        @param (str)key_part キー生成の元となるユニークな文字列
        """
        self.kvs_key = '%s::%s' % (self.__class__.__name__, key_part)
        bulk_storage = kvs_backend.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):
        """
        storageを保存
        """
        kvs_backend.set(self.kvs_key, self.packer.pack(self.storage))

    def delete(self):
        """
        storageを削除
        """
        kvs_backend.delete(self.kvs_key)
        self.storage = {}

    def dump(self):
        return "[%s] dump: kvs_key=%s, storage=%r" % (self.__class__.__name__, self.kvs_key, self.storage)


    class Field(object):
        """
        KvsModel に使えるフィールド
        score = KvsModel.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, KvsModel), '%s use only KvsModel class.' % self.__class__.__name__
            return inst.get(self.key_name, self.default)

        def __set__(self, inst, value):
            assert isinstance(inst, KvsModel), '%s use only KvsModel class.' % self.__class__.__name__
            inst.set(self.key_name, value)

        def set_field_name(self, field_name):
            #TokyoTyrantStorage.__new__ から呼ばれる
            self.field_name = field_name

        @property
        def key_name(self):
            return self._key_name or self.field_name

現在未評価

コメント

アーカイブ

2024
2023
2022
2021
2020
2019
2018
2017
2016
2015
2014
2013
2012
2011