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

(コメント)

# -*- 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

現在未評価

コメント

最近のツイート

  • ytyng

    ytyng @ytyng

    俺もスタバアプリにログインできないよ
    2 ヶ月, 1 週間 前

  • 安藤拓郎 Takuro Ando

    安藤拓郎 Takuro Ando @takuroando

    ytyng

    これまでいろんなグッズを作ってきたけど、今回は「お米」と聞いて買うしかないなと。今夜の夕食はコシヒカリ!箸もセットだし^^ https://t.co/01ucQx9qtw #腰乃展 #マンガ展 https://t.co/4VL2vOe0Og
    2 ヶ月, 3 週間 前

  • ytyng

    ytyng @ytyng

    講談社さんとやった全部入り電子書籍セットがギネスブックに登録されたよー https://t.co/rbkd3IYub0
    2 ヶ月, 3 週間 前