Skip to content

Commit

Permalink
Merge pull request #16 from gadventures/variation-id
Browse files Browse the repository at this point in the history
Disambiguate cached resources by variation_id
  • Loading branch information
bartek committed Sep 15, 2015
2 parents f8a6f59 + 6abff51 commit 9b1a81c
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 29 deletions.
37 changes: 21 additions & 16 deletions gapipy/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@
import pickle


def make_key(resource_name, resource_id=None):
def make_key(resource_name, resource_id=None, variation_id=None):
if not resource_id:
return resource_name

current_client = client_module.current_client

parts = [resource_name, str(resource_id)]
if variation_id:
parts.append(str(variation_id))

if current_client.api_language:
parts.append(current_client.api_language)
return ':'.join(parts)
Expand Down Expand Up @@ -57,7 +60,7 @@ class BaseCache(object):
def __init__(self, default_timeout=300, **kwargs):
self.default_timeout = default_timeout

def get(self, resource_name, resource_id=None):
def get(self, resource_name, resource_id=None, variation_id=None):
return None

def set(self, resource_name, data_dict):
Expand Down Expand Up @@ -110,23 +113,24 @@ def _prune(self):
if expires <= now or idx % 3 == 0:
self._cache.pop(key, None)

def get(self, resource_name, resource_id=None):
key = make_key(resource_name, resource_id)
def get(self, resource_name, resource_id=None, variation_id=None):
key = make_key(resource_name, resource_id, variation_id)
expires, value = self._cache.get(key, (0, None))
if expires > time():
return pickle.loads(value)

def set(self, resource_name, data_dict, timeout=None):
key = make_key(resource_name, data_dict.get('id'))
key = make_key(resource_name,
data_dict.get('id'), data_dict.get('variation_id'))
if timeout is None:
timeout = self.default_timeout
self._prune()

self._cache[key] = (time() + timeout, pickle.dumps(data_dict,
pickle.HIGHEST_PROTOCOL))

def delete(self, resource_name, resource_id=None):
key = make_key(resource_name, resource_id)
def delete(self, resource_name, resource_id=None, variation_id=None):
key = make_key(resource_name, resource_id, variation_id)
return self._cache.pop(key, None)

def clear(self):
Expand All @@ -135,8 +139,8 @@ def clear(self):
def count(self):
return len(self._cache)

def is_cached(self, resource_name, resource_id):
key = make_key(resource_name, resource_id)
def is_cached(self, resource_name, resource_id, variation_id=None):
key = make_key(resource_name, resource_id, variation_id)
return key in self._cache


Expand All @@ -163,19 +167,20 @@ def load_object(self, value):
def dump_object(self, value):
return pickle.dumps(value)

def get(self, resource_name, resource_id=None):
key = make_key(resource_name, resource_id)
def get(self, resource_name, resource_id=None, variation_id=None):
key = make_key(resource_name, resource_id, variation_id)
return self.load_object(self._client.get(self.key_prefix + key))

def set(self, resource_name, data_dict, timeout=None):
key = make_key(resource_name, data_dict.get('id', None))
key = make_key(resource_name,
data_dict.get('id', None), data_dict.get('variation_id'))
if timeout is None:
timeout = self.default_timeout
data = self.dump_object(data_dict)
return self._client.setex(self.key_prefix + key, data, timeout)

def delete(self, resource_name, resource_id=None):
key = make_key(resource_name, resource_id)
def delete(self, resource_name, resource_id=None, variation_id=None):
key = make_key(resource_name, resource_id, variation_id)
return self._client.delete(self.key_prefix + key)

def clear(self):
Expand All @@ -185,6 +190,6 @@ def clear(self):
def info(self):
return self._client.info()

def is_cached(self, resource_name, resource_id):
key = make_key(resource_name, resource_id)
def is_cached(self, resource_name, resource_id, variation_id=None):
key = make_key(resource_name, resource_id, variation_id)
return self._client.exists(self.key_prefix + key)
23 changes: 14 additions & 9 deletions gapipy/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@ def _to_dict(self):
# serializable types.
return self._raw_data

def get(self, resource_id, cached=True):
def get(self, resource_id, variation_id=None, cached=True):
"""
Returns an instance of the query resource with the given `resource_id`,
or `None` if the resource with the given id does not exist.
Returns an instance of the query resource with the given `resource_id`
(and optional `variation_id`) or `None` if the resource with the given
id does not exist.
If the resource is available in the cache and `cached` is True, no http
request will be made. If `cached` is False, a fresh request will be
Expand All @@ -46,7 +47,8 @@ def get(self, resource_id, cached=True):
webhook that a resource has changed.
"""
try:
data = self.get_resource_data(resource_id, cached=cached)
data = self.get_resource_data(resource_id,
variation_id=variation_id, cached=cached)
except HTTPError as e:
if e.response.status_code == 404:
return None
Expand All @@ -55,7 +57,7 @@ def get(self, resource_id, cached=True):
self._client._cache.set(self.resource._resource_name, resource_object.to_dict())
return resource_object

def get_resource_data(self, resource_id, cached=True):
def get_resource_data(self, resource_id, variation_id=None, cached=True):
'''
Returns a dictionary of resource data, which is used to initialize
a Resource object in the `get` method.
Expand All @@ -65,24 +67,27 @@ def get_resource_data(self, resource_id, cached=True):
resource_data = self._client._cache.get(
self.resource._resource_name,
resource_id,
variation_id,
)
if resource_data is not None:
return resource_data

# Cache miss; get fresh data from the backend.
requestor = APIRequestor(self._client, self.resource._resource_name)
return requestor.get(resource_id)
return requestor.get(resource_id, variation_id=variation_id)

def purge_cached(self, resource_id):
def purge_cached(self, resource_id, variation_id=None):
return self._client._cache.delete(
self.resource._resource_name,
resource_id,
variation_id,
)

def is_cached(self, resource_id):
def is_cached(self, resource_id, variation_id=None):
return self._client._cache.is_cached(
self.resource._resource_name,
resource_id
resource_id,
variation_id,
)

@_check_listable
Expand Down
14 changes: 11 additions & 3 deletions gapipy/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,22 @@ def _make_call(self, method, url, headers, data, params):
response.reason = response.text
return response.raise_for_status()

def get(self, resource_id=None, uri=None):
"""Get a single resource with the given resource_id or uri"""
def get(self, resource_id=None, uri=None, variation_id=None):
"""
Get a single resource with the given resource_id or uri
If a resource_id is supplied, a variation_id may also be given -- when
generating the URI, the variation_id will come after the resource_id,
separated by a slash.
"""
if resource_id is None and uri is None:
raise ValueError(
'Need to provide at least one of `resource_id` or `uri` as argument')
if not uri:
uri = '/{0}/{1}'.format(self.resource, resource_id)
components = ['', self.resource, str(resource_id)]
if variation_id:
components.append(str(variation_id))
uri = '/'.join(components)
return self._request(uri, 'GET')

def update(self, resource_id, data, partial=True, uri=None):
Expand Down
4 changes: 3 additions & 1 deletion gapipy/resources/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ def fetch(self):
self.is_stub = False

# Fetch the resource using the client bound on it, which handles cache get/set.
resource_obj = getattr(self._client, self._resource_name).get(self.id)
resource_obj = getattr(self._client, self._resource_name).get(
self.id,
variation_id=getattr(self, 'variation_id', None))
if resource_obj:
self._fill_fields(resource_obj._raw_data)

Expand Down
17 changes: 17 additions & 0 deletions tests/test_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ def test_make_key_language(self):
Client(api_language='de')
self.assertEquals(make_key('foo', 1), 'foo:1:de')

def test_make_key_variation_id(self):
Client()
self.assertEqual(make_key('foo', 1, 9000), 'foo:1:9000')


class SimpleCacheTestCase(TestCase):

def test_get_set(self):
Expand All @@ -28,6 +33,12 @@ def test_get_set_resource_id(self):
data = c.get('foo', 100)
self.assertEquals(data, {'id': 100, 'xyz': 'bar'})

def test_get_set_resource_and_variation_id(self):
c = cache.SimpleCache()
c.set('foo', {'id': 100, 'xyz': 'bar', 'variation_id': 9000})
data = c.get('foo', 100, 9000)
self.assertEquals(data, {'id': 100, 'xyz': 'bar', 'variation_id': 9000})

def test_get_many_resource_id(self):
c = cache.SimpleCache()
c.set('a', {'id': 1, 0: 0})
Expand Down Expand Up @@ -82,6 +93,12 @@ def test_get_set(self):
c.set('a', {'results': (1, 2, 3)})
self.assertEquals(c.get('a'), {'results': (1, 2, 3)})

def test_get_set_resource_and_variation_id(self):
c = self.make_cache()
c.set('foo', {'id': 100, 'xyz': 'bar', 'variation_id': 9000})
data = c.get('foo', 100, 9000)
self.assertEquals(data, {'id': 100, 'xyz': 'bar', 'variation_id': 9000})

def test_get_many(self):
c = self.make_cache()
c.set('a', {'id': 1})
Expand Down

0 comments on commit 9b1a81c

Please sign in to comment.