Adds design for BatchController
This commit is contained in:
parent
162a1f23dd
commit
78a41970ea
2 changed files with 88 additions and 3 deletions
85
registrasion/controllers/batch.py
Normal file
85
registrasion/controllers/batch.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
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.
|
||||
|
||||
'''
|
|
@ -50,7 +50,7 @@ class DiscountController(object):
|
|||
categories and products. The discounts also list the available quantity
|
||||
for this user, not including products that are pending purchase. '''
|
||||
|
||||
filtered_clauses = cls._filtered_clauses(user, categories, products)
|
||||
filtered_clauses = cls._filtered_clauses(user)
|
||||
|
||||
# clauses that match provided categories
|
||||
categories = set(categories)
|
||||
|
@ -103,8 +103,8 @@ class DiscountController(object):
|
|||
'''
|
||||
|
||||
Returns:
|
||||
Sequence[discountbase]: All discounts that passed the filter
|
||||
function.
|
||||
Sequence[DiscountForProduct | DiscountForCategory]: All clauses
|
||||
that passed the filter function.
|
||||
|
||||
'''
|
||||
|
||||
|
|
Loading…
Reference in a new issue