diff options
author | Aleksey Lim <alsroot@sugarlabs.org> | 2012-09-24 02:53:20 (GMT) |
---|---|---|
committer | Aleksey Lim <alsroot@sugarlabs.org> | 2012-09-24 02:53:20 (GMT) |
commit | aa4b0d8b0a0b29df0085eace476bb6526c54ab7d (patch) | |
tree | bacecf4a45315bbaaeb816a0c2e3d3f5030f73e8 | |
parent | 9985556a26cb51e927e28b385337b2a67bb1c3f6 (diff) |
Use properties' attribute setter for all sets
-rw-r--r-- | active_document/__init__.py | 4 | ||||
-rw-r--r-- | active_document/directory.py | 4 | ||||
-rw-r--r-- | active_document/document.py | 15 | ||||
-rw-r--r-- | active_document/metadata.py | 38 | ||||
-rw-r--r-- | active_document/storage.py | 66 | ||||
-rw-r--r-- | active_document/volume.py | 104 | ||||
-rwxr-xr-x | tests/units/document.py | 12 | ||||
-rwxr-xr-x | tests/units/volume.py | 300 |
8 files changed, 369 insertions, 174 deletions
diff --git a/active_document/__init__.py b/active_document/__init__.py index fd52f90..2ebf9ef 100644 --- a/active_document/__init__.py +++ b/active_document/__init__.py @@ -23,12 +23,10 @@ from active_document.env import ACCESS_CREATE, ACCESS_WRITE, ACCESS_READ, \ NotFound, Forbidden, Redirect, Seqno, DEFAULT_LANG, \ uuid, default_lang -from active_document.metadata import Metadata, Property, \ +from active_document.metadata import Metadata, PropertyMeta, Property, \ StoredProperty, ActiveProperty, BlobProperty, BrowsableProperty, \ active_property -from active_document.storage import Meta - from active_document.commands import to_int, to_list, \ volume_command, volume_command_pre, volume_command_post, \ directory_command, directory_command_pre, directory_command_post, \ diff --git a/active_document/directory.py b/active_document/directory.py index 3a0d644..4f822de 100644 --- a/active_document/directory.py +++ b/active_document/directory.py @@ -14,7 +14,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import os -import json import shutil import logging from os.path import exists, join @@ -196,8 +195,7 @@ class Directory(object): _logger.debug('Received %r BLOB property from %s[%s]', prop.name, self.metadata.name, guid) - if prop.mime_type == 'application/json' and not hasattr(data, 'read'): - data = json.dumps(data) + record.set_blob(prop.name, data, size, seqno=seqno, mime_type=prop.mime_type, **kwargs) diff --git a/active_document/document.py b/active_document/document.py index 45be43b..b35e242 100644 --- a/active_document/document.py +++ b/active_document/document.py @@ -24,14 +24,14 @@ from active_toolkit import enforce _logger = logging.getLogger('active_document.document') -class Document(object): +class Document(dict): #: `Metadata` object that describes the document metadata = None def __init__(self, guid, record, cached_props=None): - self._guid = guid - self._props = cached_props or {} + dict.__init__(self, cached_props or {}) + self.guid = guid self._record = record @active_property(slot=1000, prefix='IC', typecast=int, @@ -49,11 +49,6 @@ class Document(object): def seqno(self, value): return value - @property - def guid(self): - """Document GUID.""" - return self._guid - def get(self, prop, accept_language=None): """Get document's property value. @@ -65,14 +60,14 @@ class Document(object): """ prop = self.metadata[prop] - value = self._props.get(prop.name) + value = dict.get(self, prop.name) if value is None: enforce(isinstance(prop, StoredProperty), 'No way to get %r property from %s[%s]', prop.name, self.metadata.name, self.guid) meta = self._record.get(prop.name) value = prop.default if meta is None else meta['value'] - self._props[prop.name] = value + self[prop.name] = value if accept_language and prop.localized: value = self._localize(value, accept_language) diff --git a/active_document/metadata.py b/active_document/metadata.py index 01b3b60..8bc59dc 100644 --- a/active_document/metadata.py +++ b/active_document/metadata.py @@ -13,7 +13,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +import os +import json import types +from os.path import exists from active_document import env from active_toolkit import enforce @@ -31,6 +34,7 @@ def active_property(property_class=None, *args, **kwargs): def decorate_setter(func, attr): attr.prop.setter = lambda self, value: \ self.set(attr.name, func(self, value)) + attr.prop.on_set = func return attr def decorate_getter(func): @@ -102,6 +106,39 @@ class Metadata(dict): return dict.__getitem__(self, prop_name) +class PropertyMeta(dict): + + BLOB_SUFFIX = '.blob' + + def __init__(self, path_=None, **meta): + if path_: + with file(path_) as f: + meta.update(json.load(f)) + if exists(path_ + PropertyMeta.BLOB_SUFFIX): + meta['path'] = path_ + PropertyMeta.BLOB_SUFFIX + meta['mtime'] = os.stat(path_).st_mtime + dict.__init__(self, meta) + + def url(self, part=None): + url = self.get('url') + if url is None or isinstance(url, basestring): + return url + + if part: + file_meta = url.get(part) + enforce(file_meta and 'url' in file_meta, + env.NotFound, 'No BLOB for %r', part) + return file_meta['url'] + + return sorted(url.values(), + cmp=lambda x, y: cmp(x.get('order'), y.get('order'))) + + @classmethod + def is_blob(cls, blob): + return isinstance(blob, (type(None), basestring, cls)) or \ + hasattr(blob, 'read') + + class Property(object): """Bacis class to collect information about document property.""" @@ -109,6 +146,7 @@ class Property(object): reprcast=None, default=None): self.setter = None self.on_get = lambda self, x: x + self.on_set = lambda self, x: x self._name = name self._permissions = permissions self._typecast = typecast diff --git a/active_document/storage.py b/active_document/storage.py index 148e3df..a3b132d 100644 --- a/active_document/storage.py +++ b/active_document/storage.py @@ -22,12 +22,9 @@ import hashlib from os.path import exists, join, isdir, basename, relpath, lexists, isabs from active_document import env -from active_document.metadata import BlobProperty +from active_document.metadata import PropertyMeta, BlobProperty from active_toolkit.sockets import BUFFER_SIZE -from active_toolkit import util, enforce - - -_BLOB_SUFFIX = '.blob' +from active_toolkit import util class Storage(object): @@ -127,7 +124,7 @@ class Storage(object): else: # TODO calculate new digest meta['digest'] = '' - shutil.move(path, path + _BLOB_SUFFIX) + shutil.move(path, path + PropertyMeta.BLOB_SUFFIX) meta['mime_type'] = prop.mime_type else: if exists(path + '.sha1'): @@ -176,7 +173,7 @@ class Record(object): def get(self, prop): path = join(self._root, prop) if exists(path): - return Meta(path) + return PropertyMeta(path) def set(self, prop, mtime=None, **kwargs): if not exists(self._root): @@ -198,17 +195,18 @@ class Record(object): def set_blob(self, prop, data=None, size=None, **kwargs): if not exists(self._root): os.makedirs(self._root) - path = join(self._root, prop + _BLOB_SUFFIX) + path = join(self._root, prop + PropertyMeta.BLOB_SUFFIX) + meta = PropertyMeta(**kwargs) - if 'digest' not in kwargs: - digest = hashlib.sha1() + if data is None: + if exists(path): + os.unlink(path) + elif isinstance(data, PropertyMeta): + data.update(meta) + meta = data else: - digest = None - - try: - if data is None: - digest = None - elif hasattr(data, 'read'): + digest = hashlib.sha1() + if hasattr(data, 'read'): if size is None: size = sys.maxint self._set_blob_by_stream(digest, data, size, path) @@ -218,15 +216,9 @@ class Record(object): with util.new_file(path) as f: f.write(data) digest.update(data) - except Exception, error: - util.exception() - raise RuntimeError('Fail to set BLOB %r property for %r: %s' % - (prop, self.guid, error)) - - if digest is not None: - kwargs['digest'] = digest.hexdigest() + meta['digest'] = digest.hexdigest() - self.set(prop, **kwargs) + self.set(prop, **meta) def _set_blob_by_stream(self, digest, stream, size, path): with util.new_file(path) as f: @@ -260,29 +252,3 @@ class Record(object): hash_file(path) else: hash_file(dst_path) - - -class Meta(dict): - - def __init__(self, path_=None, **meta): - if path_: - with file(path_) as f: - meta.update(json.load(f)) - if exists(path_ + _BLOB_SUFFIX): - meta['path'] = path_ + _BLOB_SUFFIX - meta['mtime'] = os.stat(path_).st_mtime - dict.__init__(self, meta) - - def url(self, part=None): - url = self.get('url') - if url is None or isinstance(url, basestring): - return url - - if part: - file_meta = url.get(part) - enforce(file_meta and 'url' in file_meta, - env.NotFound, 'No BLOB for %r', part) - return file_meta['url'] - - return sorted(url.values(), - cmp=lambda x, y: cmp(x.get('order'), y.get('order'))) diff --git a/active_document/volume.py b/active_document/volume.py index 0dc9716..a09e42b 100644 --- a/active_document/volume.py +++ b/active_document/volume.py @@ -16,6 +16,7 @@ import os import time import logging +from contextlib import contextmanager from os.path import exists, join, abspath, isdir from active_document import env @@ -25,6 +26,7 @@ from active_document.commands import document_command, directory_command from active_document.commands import CommandsProcessor, property_command from active_document.commands import to_int, to_list from active_document.metadata import BlobProperty, BrowsableProperty +from active_document.metadata import PropertyMeta from active_toolkit import coroutine, util, sockets, enforce @@ -130,29 +132,13 @@ class VolumeCommands(CommandsProcessor): @directory_command(method='POST', permissions=env.ACCESS_AUTH) - def create(self, document, request): - directory = self.volume[document] - props = request.content - blobs = {} - - enforce('guid' not in props, env.Forbidden, - 'Property "guid" cannot be set manually') - - for name, value in props.items(): - prop = directory.metadata[name] - prop.assert_access(env.ACCESS_CREATE) - if isinstance(prop, BlobProperty): - blobs[name] = props.pop(name) - else: - props[name] = self._prepost(request, prop, value) - - self.before_create(request, props) - guid = directory.create(props) - - for name, value in blobs.items(): - directory.set_blob(guid, name, value) - - return guid + def create(self, request): + with self._post(request, env.ACCESS_CREATE) as (directory, doc): + enforce('guid' not in doc, env.Forbidden, + "Property 'guid' cannot be set manually") + self.before_create(request, doc) + doc.guid = directory.create(doc) + return doc.guid @directory_command(method='GET', arguments={'offset': to_int, 'limit': to_int, 'reply': to_list}) @@ -172,44 +158,19 @@ class VolumeCommands(CommandsProcessor): @document_command(method='PUT', permissions=env.ACCESS_AUTH | env.ACCESS_AUTHOR) - def update(self, document, guid, request): - directory = self.volume[document] - props = request.content - blobs = {} - - for name, value in props.items(): - prop = directory.metadata[name] - prop.assert_access(env.ACCESS_WRITE) - if isinstance(prop, BlobProperty): - blobs[name] = props.pop(name) - else: - props[name] = self._prepost(request, prop, value) - - self.before_update(request, props) - directory.update(guid, props) - - for name, value in blobs.items(): - directory.set_blob(guid, name, value) + def update(self, request): + with self._post(request, env.ACCESS_WRITE) as (directory, doc): + self.before_update(request, doc) + directory.update(doc.guid, doc) @property_command(method='PUT', permissions=env.ACCESS_AUTH | env.ACCESS_AUTHOR) - def update_prop(self, document, guid, prop, request, url=None): - directory = self.volume[document] - - prop = directory.metadata[prop] - prop.assert_access(env.ACCESS_WRITE) - - if not isinstance(prop, BlobProperty): - request.content = {prop.name: request.content} - return self.update(document, guid, request) - - if url is not None: - directory.set_blob(guid, prop.name, url=url) - elif request.content is not None: - directory.set_blob(guid, prop.name, request.content) + def update_prop(self, request, prop, url=None): + if url: + request.content = {prop: PropertyMeta(url=url)} else: - directory.set_blob(guid, prop.name, request.content_stream, - request.content_length) + request.content = {prop: request.content or request.content_stream} + return self.update(request) @document_command(method='DELETE', permissions=env.ACCESS_AUTH | env.ACCESS_AUTHOR) @@ -246,6 +207,8 @@ class VolumeCommands(CommandsProcessor): return url raise env.Redirect(url) + enforce('path' in meta, env.NotFound, 'BLOB does not exist') + if seqno is not None and seqno >= meta['seqno']: response.content_length = 0 response.content_type = prop.mime_type @@ -270,11 +233,28 @@ class VolumeCommands(CommandsProcessor): def before_update(self, request, props): props['mtime'] = int(time.time()) - def _prepost(self, request, prop, value): - if prop.localized and isinstance(value, basestring): - return {(request.accept_language or self._lang)[0]: value} - else: - return value + @contextmanager + def _post(self, request, access): + directory = self.volume[request['document']] + doc = directory.document_class(request.get('guid'), {}) + blobs = [] + + for name, value in request.content.items(): + prop = directory.metadata[name] + prop.assert_access(access) + value = prop.on_set(doc, value) + if isinstance(prop, BlobProperty): + enforce(PropertyMeta.is_blob(value), 'Invalid BLOB value') + blobs.append((name, value)) + else: + if prop.localized and isinstance(value, basestring): + value = {(request.accept_language or self._lang)[0]: value} + doc[name] = value + + yield directory, doc + + for name, value in blobs: + directory.set_blob(doc.guid, name, value) def _preget(self, request): metadata = self.volume[request['document']].metadata diff --git a/tests/units/document.py b/tests/units/document.py index 18a3705..883a20f 100755 --- a/tests/units/document.py +++ b/tests/units/document.py @@ -187,18 +187,6 @@ class DocumentTest(tests.Test): directory.get(guid).meta('blob')) self.assertEqual(data, file(blob_path + '.blob').read()) - data = json.dumps({'foo': -1}) - directory.set_blob(guid, 'blob', {'foo': -1}) - self.assertEqual({ - 'seqno': 3, - 'mtime': os.stat(blob_path).st_mtime, - 'digest': hashlib.sha1(data).hexdigest(), - 'path': join(tests.tmpdir, guid[:2], guid, 'blob.blob'), - 'mime_type': 'application/json', - }, - directory.get(guid).meta('blob')) - self.assertEqual(data, file(blob_path + '.blob').read()) - def test_update(self): class Document(document.Document): diff --git a/tests/units/volume.py b/tests/units/volume.py index 65b9671..d8ee5b0 100755 --- a/tests/units/volume.py +++ b/tests/units/volume.py @@ -14,9 +14,10 @@ src_root = abspath(dirname(__file__)) from __init__ import tests import active_document as ad -from active_document import env, volume, document, SingleVolume, \ +from active_document import env, document, SingleVolume, \ Request, Response, Document, active_property, \ BlobProperty, NotFound, Redirect +from active_document.volume import VolumeCommands from active_toolkit import sockets, coroutine @@ -24,22 +25,7 @@ class VolumeTest(tests.Test): def setUp(self): tests.Test.setUp(self) - - class TestDocument(Document): - - @active_property(slot=1, default='') - def prop(self, value): - return value - - @active_property(BlobProperty) - def blob(self, value): - return value - - @active_property(prefix='L', localized=True, default='') - def localized_prop(self, value): - return value - - self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + self.response = Response() def test_Populate(self): self.touch( @@ -108,6 +94,18 @@ class VolumeTest(tests.Test): self.assertEqual('default', volume['document'].get('1')['prop']) def test_Commands(self): + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) self.volume['testdocument'].create(guid='guid') self.assertEqual({ @@ -160,14 +158,47 @@ class VolumeTest(tests.Test): 'value_3', self.call('GET', document='testdocument', guid=guid_1, prop='prop')) - self.call('PUT', document='testdocument', guid=guid_1, prop='blob', content_stream=StringIO('blob-value')) + def test_SetBLOBs(self): + + class TestDocument(Document): + + @active_property(BlobProperty) + def blob(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + guid = self.call('POST', document='testdocument', content={}) + + self.assertRaises(RuntimeError, self.call, 'PUT', document='testdocument', guid=guid, prop='blob', content={'path': '/'}) - stream = self.call('GET', document='testdocument', guid=guid_1, prop='blob') - self.assertEqual('blob-value', ''.join([i for i in stream])) - self.assertEqual('application/octet-stream', self.response.content_type) - self.assertEqual(len('blob-value'), self.response.content_length) + self.call('PUT', document='testdocument', guid=guid, prop='blob', content='blob1') + stream = self.call('GET', document='testdocument', guid=guid, prop='blob') + self.assertEqual('blob1', ''.join([i for i in stream])) + + self.call('PUT', document='testdocument', guid=guid, prop='blob', content_stream=StringIO('blob2')) + stream = self.call('GET', document='testdocument', guid=guid, prop='blob') + self.assertEqual('blob2', ''.join([i for i in stream])) + + self.call('PUT', document='testdocument', guid=guid, prop='blob', content=None) + self.assertRaises(NotFound, self.call, 'GET', document='testdocument', guid=guid, prop='blob') def test_CommandsGetBlobDirectory(self): + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(BlobProperty) + def blob(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) guid = self.call('POST', document='testdocument', content={}) blob_path = tests.tmpdir + '/testdocument/%s/%s/blob' % (guid[:2], guid) @@ -195,11 +226,40 @@ class VolumeTest(tests.Test): sorted([(name, content.read()) for name, content in files])) def test_CommandsGetAbsentBlobs(self): + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(BlobProperty) + def blob(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + guid = self.call('POST', document='testdocument', content={'prop': 'value'}) self.assertEqual('value', self.call('GET', document='testdocument', guid=guid, prop='prop')) self.assertRaises(NotFound, self.call, 'GET', document='testdocument', guid=guid, prop='blob') def test_Command_ReplyForGET(self): + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) guid = self.call('POST', document='testdocument', content={'prop': 'value'}) self.assertEqual( @@ -223,6 +283,23 @@ class VolumeTest(tests.Test): sorted(self.call('GET', document='testdocument', reply=['prop'])['result'][0].keys())) def test_Command_GetBlobBySeqno(self): + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(BlobProperty) + def blob(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + # seqno == 1 guid = self.call('POST', document='testdocument', content={}) # seqno == 2 @@ -249,6 +326,17 @@ class VolumeTest(tests.Test): def test_LocalizedSet(self): env.DEFAULT_LANG = 'en' + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) directory = self.volume['testdocument'] guid = directory.create({'localized_prop': 'value_raw'}) @@ -279,6 +367,18 @@ class VolumeTest(tests.Test): [i.guid for i in directory.find(0, 100, localized_prop='value_en')[0]]) def test_LocalizedGet(self): + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) directory = self.volume['testdocument'] guid = self.call('POST', document='testdocument', content={ @@ -390,6 +490,22 @@ class VolumeTest(tests.Test): volume.close() def test_Command_GetBlobSetByUrl(self): + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(BlobProperty) + def blob(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) guid = self.call('POST', document='testdocument', content={}) self.call('PUT', document='testdocument', guid=guid, prop='blob', url='http://sugarlabs.org') @@ -400,6 +516,22 @@ class VolumeTest(tests.Test): self.assertEqual('http://sugarlabs.org', redirect.location) def test_CompositeBlobs(self): + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(BlobProperty) + def blob(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) guid = self.call('POST', document='testdocument', content={}) self.call('PUT', document='testdocument', guid=guid, prop='blob', url={ 'file3': {'order': 3, 'url': 'url3', 'foo': 'bar'}, @@ -422,6 +554,19 @@ class VolumeTest(tests.Test): self.assertEqual('url2', redirect.location) def test_before_create(self): + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + ts = time.time() guid = self.call(method='POST', document='testdocument', content={}) assert self.volume['testdocument'].get(guid)['ctime'] in range(ts - 1, ts + 1) @@ -429,25 +574,48 @@ class VolumeTest(tests.Test): def test_before_create_Override(self): - class VolumeCommands(volume.VolumeCommands): + class Commands(VolumeCommands): def before_create(self, request, props): props['prop'] = 'overriden' - volume.VolumeCommands.before_create(self, request, props) + VolumeCommands.before_create(self, request, props) - cp = VolumeCommands(self.volume) + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + volume = SingleVolume(tests.tmpdir, [TestDocument]) + cp = Commands(volume) request = Request(method='POST', document='testdocument') request.content = {'prop': 'foo'} guid = cp.call(request, Response()) - self.assertEqual('overriden', self.volume['testdocument'].get(guid)['prop']) + self.assertEqual('overriden', volume['testdocument'].get(guid)['prop']) request = Request(method='PUT', document='testdocument', guid=guid) request.content = {'prop': 'bar'} cp.call(request, Response()) - self.assertEqual('bar', self.volume['testdocument'].get(guid)['prop']) + self.assertEqual('bar', volume['testdocument'].get(guid)['prop']) def test_before_update(self): + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) guid = self.call(method='POST', document='testdocument', content={}) prev_mtime = self.volume['testdocument'].get(guid)['mtime'] @@ -458,25 +626,48 @@ class VolumeTest(tests.Test): def test_before_update_Override(self): - class VolumeCommands(volume.VolumeCommands): + class Commands(VolumeCommands): def before_update(self, request, props): props['prop'] = 'overriden' - volume.VolumeCommands.before_update(self, request, props) + VolumeCommands.before_update(self, request, props) - cp = VolumeCommands(self.volume) + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + volume = SingleVolume(tests.tmpdir, [TestDocument]) + cp = Commands(volume) request = Request(method='POST', document='testdocument') request.content = {'prop': 'foo'} guid = cp.call(request, Response()) - self.assertEqual('foo', self.volume['testdocument'].get(guid)['prop']) + self.assertEqual('foo', volume['testdocument'].get(guid)['prop']) request = Request(method='PUT', document='testdocument', guid=guid) request.content = {'prop': 'bar'} cp.call(request, Response()) - self.assertEqual('overriden', self.volume['testdocument'].get(guid)['prop']) + self.assertEqual('overriden', volume['testdocument'].get(guid)['prop']) def test_DoNotPassGuidsForCreate(self): + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) self.assertRaises(env.Forbidden, self.call, method='POST', document='testdocument', content={'guid': 'foo'}) guid = self.call(method='POST', document='testdocument', content={}) assert guid @@ -649,7 +840,7 @@ class VolumeTest(tests.Test): @active_property(BlobProperty) def empty_blob(self, meta): - return ad.Meta(url='http://sugarlabs.org') + return ad.PropertyMeta(url='http://sugarlabs.org') self.volume = SingleVolume(tests.tmpdir, [TestDocument]) guid = self.call('POST', document='testdocument', content={}) @@ -675,6 +866,47 @@ class VolumeTest(tests.Test): except Redirect, redirect: self.assertEqual('http://sugarlabs.org', redirect.location) + def test_properties_OverrideSet(self): + + class TestDocument(Document): + + @active_property(slot=1, default='1') + def prop(self, value): + return value + + @prop.setter + def prop(self, value): + return '_%s' % value + + @active_property(BlobProperty) + def blob(self, meta): + return meta + + @blob.setter + def blob(self, value): + return ad.PropertyMeta(url=value) + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + guid = self.call('POST', document='testdocument', content={}) + + self.assertEqual('1', self.call('GET', document='testdocument', guid=guid, prop='prop')) + self.assertRaises(NotFound, self.call, 'GET', document='testdocument', guid=guid, prop='blob') + + self.call('PUT', document='testdocument', guid=guid, prop='prop', content='2') + self.assertEqual('_2', self.call('GET', document='testdocument', guid=guid, prop='prop')) + self.assertRaises(NotFound, self.call, 'GET', document='testdocument', guid=guid, prop='blob') + + self.call('PUT', document='testdocument', guid=guid, content={'prop': 3}) + self.assertEqual('_3', self.call('GET', document='testdocument', guid=guid, prop='prop')) + self.assertRaises(NotFound, self.call, 'GET', document='testdocument', guid=guid, prop='blob') + + self.call('PUT', document='testdocument', guid=guid, prop='blob', content='blob2') + try: + self.call('GET', document='testdocument', guid=guid, prop='blob') + assert False + except Redirect, redirect: + self.assertEqual('blob2', redirect.location) + def call(self, method, document=None, guid=None, prop=None, accept_language=None, **kwargs): @@ -696,7 +928,7 @@ class VolumeTest(tests.Test): request.content_length = len(request.content_stream.getvalue()) self.response = Response() - cp = volume.VolumeCommands(self.volume) + cp = VolumeCommands(self.volume) return cp.call(request, self.response) |