Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksey Lim <alsroot@sugarlabs.org>2012-09-24 02:53:20 (GMT)
committer Aleksey Lim <alsroot@sugarlabs.org>2012-09-24 02:53:20 (GMT)
commitaa4b0d8b0a0b29df0085eace476bb6526c54ab7d (patch)
treebacecf4a45315bbaaeb816a0c2e3d3f5030f73e8
parent9985556a26cb51e927e28b385337b2a67bb1c3f6 (diff)
Use properties' attribute setter for all sets
-rw-r--r--active_document/__init__.py4
-rw-r--r--active_document/directory.py4
-rw-r--r--active_document/document.py15
-rw-r--r--active_document/metadata.py38
-rw-r--r--active_document/storage.py66
-rw-r--r--active_document/volume.py104
-rwxr-xr-xtests/units/document.py12
-rwxr-xr-xtests/units/volume.py300
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)