Files @ eb29e7cd0977
Branch filter:

Location: symposion_app/registrasion/controllers/batch.py

Christopher Neugebauer
Adds test cases for basic batch cacheing behaviour
import contextlib
import functools


class BatchController(object):
    ''' Batches are sets of operations where certain queries for users may be
    repeated, but are also unlikely change within the boundaries of the batch.

    Batches are keyed per-user. You can mark the edge of the batch with the
    ``batch`` context manager. If you nest calls to ``batch``, only the
    outermost call will have the effect of ending the batch.

    Batches store results for functions wrapped with ``memoise``. These results
    for the user are flushed at the end of the batch.

    If a return for a memoised function has a callable attribute called
    ``end_batch``, that attribute will be called at the end of the batch.

    '''

    _user_caches = {}

    @classmethod
    @contextlib.contextmanager
    def batch(cls, user):
        ''' Marks the entry point for a batch for the given user. '''
        pass
        # TODO: store nesting count *inside* the cache object. You know it
        # makes sense.

    @classmethod
    def memoise(cls, func):
        ''' Decorator that stores the result of the stored function in the
        user's results cache until the batch completes.

        Arguments:
            func (callable(user, *a, **k)): The function whose results we want
                to store. ``user`` must be the first argument; this is used as
                the cache key.

        Returns:
            callable(user, *a, **k): The memosing version of ``func``.

        '''

        @functools.wraps(func)
        def f(user, *a, **k):

            cache = cls.get_cache(user)
            if func not in cache:
                cache[func] = func(user, *a, **k)

            return cache[func]

        return f

    @classmethod
    def get_cache(cls, user):
        if user not in cls._user_caches:
            return {}  # Return blank cache here, we'll just discard :)

        return cls._user_caches[user]


'''
TODO: memoise CartController.for_user
TODO: memoise user_remainders (Product, Category)
TODO: memoise _filtered_flags
TODO: memoise FlagCounter.count() (doesn't take user, but it'll do for now)
TODO: memoise _filtered_discounts

Tests:
- Correct nesting behaviour
 - do we get different cache objects every time we get a cache in non-batched
   contexts?
 - do we get the same cache object for nested caches?
  - do we get different cache objects when we back out of a batch and enter a
   new one
- are cache clears independent for different users?
- ``end_batch`` behaviour for CartController (use for_user *A LOT*)
  - discounts not calculated until outermost batch point exits.
  - Revision number shouldn't change until outermost batch point exits.
- Make sure memoisation ONLY happens when we're in a batch.

'''