[Checkins] SVN: mongopersist/trunk/src/mongopersist/ * Added some more tests to improve test coverage.
Stephen Richter
cvs-admin at zope.org
Tue Mar 13 00:09:31 UTC 2012
Log message for revision 124589:
* Added some more tests to improve test coverage.
* Renamed processSpec() to process_spec(), since we are using PEP8 naming.
* Added ProcessSpecDecorator.
* Added processSpec to the CollectionWrapper. Removed manual calls to
processSpec() and make sure the wrapper is used everywhere.
Changed:
U mongopersist/trunk/src/mongopersist/README.txt
U mongopersist/trunk/src/mongopersist/datamanager.py
U mongopersist/trunk/src/mongopersist/mapping.py
U mongopersist/trunk/src/mongopersist/tests/test_datamanager.py
U mongopersist/trunk/src/mongopersist/zope/container.py
-=-
Modified: mongopersist/trunk/src/mongopersist/README.txt
===================================================================
--- mongopersist/trunk/src/mongopersist/README.txt 2012-03-13 00:06:58 UTC (rev 124588)
+++ mongopersist/trunk/src/mongopersist/README.txt 2012-03-13 00:09:27 UTC (rev 124589)
@@ -115,8 +115,9 @@
MongoPersist supports a special attribute called ``_p_mongo_collection``,
which allows you to specify a custom collection to use.
- >>> dm.root['stephan'].address = Address('Maynard', '01754')
- >>> dm.root['stephan'].address
+ >>> stephan = dm.root['stephan']
+ >>> stephan.address = Address('Maynard', '01754')
+ >>> stephan.address
<Address Maynard (01754)>
Note that the address is not immediately saved in the database:
Modified: mongopersist/trunk/src/mongopersist/datamanager.py
===================================================================
--- mongopersist/trunk/src/mongopersist/datamanager.py 2012-03-13 00:06:58 UTC (rev 124588)
+++ mongopersist/trunk/src/mongopersist/datamanager.py 2012-03-13 00:09:27 UTC (rev 124589)
@@ -27,7 +27,7 @@
None, obj,
(new_doc.get('_py_serial', 0), serialize.u64(obj._p_serial)))
-def processSpec(collection, spec):
+def process_spec(collection, spec):
try:
adapter = interfaces.IMongoSpecProcessor(None)
except TypeError:
@@ -46,10 +46,32 @@
self.datamanager.flush()
return self.function(*args, **kwargs)
+class ProcessSpecDecorator(object):
+
+ def __init__(self, collection, function):
+ self.collection = collection
+ self.function = function
+
+ def __call__(self, *args, **kwargs):
+ if args:
+ args = (process_spec(self.collection, args[0]),) + args[1:]
+ # find()
+ if 'spec' in kwargs:
+ kwargs['spec'] = process_spec(self.collection, kwargs['spec'])
+ # find_one()
+ elif 'spec_or_id' in kwargs:
+ kwargs['spec_or_id'] = process_spec(
+ self.collection, kwargs['spec_or_id'])
+ # find_and_modify()
+ elif 'query' in kwargs:
+ kwargs['query'] = process_spec(self.collection, kwargs['query'])
+ return self.function(*args, **kwargs)
+
class CollectionWrapper(object):
QUERY_METHODS = ['group', 'map_reduce', 'inline_map_reduce', 'find_one',
- 'find', 'count', 'find_and_modify']
+ 'find', 'find_and_modify']
+ PROCESS_SPEC_METHODS = ['find_and_modify', 'find_one', 'find']
def __init__(self, collection, datamanager):
self.__dict__['collection'] = collection
@@ -59,6 +81,8 @@
attr = getattr(self.collection, name)
if name in self.QUERY_METHODS:
attr = FlushDecorator(self._datamanager, attr)
+ if name in self.PROCESS_SPEC_METHODS:
+ attr = ProcessSpecDecorator(self.collection, attr)
return attr
def __setattr__(self, name, value):
@@ -80,26 +104,24 @@
if collection is not None:
self.collection = collection
db = self._jar._conn[self.database]
- self._collection_inst = db[self.collection]
+ self._collection_inst = CollectionWrapper(db[self.collection], jar)
def __getitem__(self, key):
- doc = self._collection_inst.find_one(
- processSpec(self._collection_inst, {'name': key}))
+ doc = self._collection_inst.find_one({'name': key})
if doc is None:
raise KeyError(key)
return self._jar.load(doc['ref'])
def __setitem__(self, key, value):
- dbref = self._jar.dump(value)
+ dbref = self._jar.insert(value)
if self.get(key) is not None:
del self[key]
doc = {'ref': dbref, 'name': key}
self._collection_inst.insert(doc)
def __delitem__(self, key):
- doc = self._collection_inst.find_one(
- processSpec(self._collection_inst, {'name': key}))
- coll = self._jar._get_collection(
+ doc = self._collection_inst.find_one({'name': key})
+ coll = self._jar.get_collection(
doc['ref'].database, doc['ref'].collection)
coll.remove(doc['ref'].id)
self._collection_inst.remove({'name': key})
@@ -190,6 +212,7 @@
if obj in self._registered_objects:
obj._p_changed = False
self._registered_objects.remove(obj)
+ return res
def load(self, dbref):
return self._reader.get_ghost(dbref)
Modified: mongopersist/trunk/src/mongopersist/mapping.py
===================================================================
--- mongopersist/trunk/src/mongopersist/mapping.py 2012-03-13 00:06:58 UTC (rev 124588)
+++ mongopersist/trunk/src/mongopersist/mapping.py 2012-03-13 00:09:27 UTC (rev 124589)
@@ -16,7 +16,6 @@
import pymongo
from mongopersist import interfaces
-from mongopersist.datamanager import processSpec
class MongoCollectionMapping(UserDict.DictMixin, object):
__mongo_database__ = None
@@ -37,7 +36,7 @@
filter = self.__mongo_filter__()
filter[self.__mongo_mapping_key__] = key
coll = self.get_mongo_collection()
- doc = coll.find_one(processSpec(coll, filter))
+ doc = coll.find_one(filter)
if doc is None:
raise KeyError(key)
db_name = self.__mongo_database__ or self._m_jar.default_database
@@ -64,4 +63,4 @@
coll = self.get_mongo_collection()
return [
doc[self.__mongo_mapping_key__]
- for doc in coll.find(processSpec(coll, filter))]
+ for doc in coll.find(filter)]
Modified: mongopersist/trunk/src/mongopersist/tests/test_datamanager.py
===================================================================
--- mongopersist/trunk/src/mongopersist/tests/test_datamanager.py 2012-03-13 00:06:58 UTC (rev 124588)
+++ mongopersist/trunk/src/mongopersist/tests/test_datamanager.py 2012-03-13 00:09:27 UTC (rev 124589)
@@ -188,6 +188,22 @@
ObjectId('4eb2eb7437a08e0156000000'),
'mongopersist_test')
+ When the object is modified, ``dump()`` will remove it from the list of
+ registered objects.
+
+ >>> foo.name = 'Foo'
+ >>> foo._p_changed
+ True
+ >>> dm._registered_objects
+ [<mongopersist.tests.test_datamanager.Foo object at 0x2fe1f50>]
+
+ >>> foo_ref = dm.dump(foo)
+
+ >>> foo._p_changed
+ False
+ >>> dm._registered_objects
+ []
+
Let's now reset the data manager, so we do not hit a cache while loading
the object again:
@@ -641,17 +657,23 @@
('MongoDataManager', 0)
"""
-def doctest_processSpec():
- r"""processSpec(): General test
+def doctest_process_spec():
+ r"""process_spec(): General test
A simple helper function that returns the spec itself if no
- IMongoSpecProcessor adapter is registered.
+ ``IMongoSpecProcessor`` adapter is registered. If a processor is found it
+ is applied. The spec processor can be used for:
+ * Additional logging.
+
+ * Modifying the spec, for example providing additional parameters.
+
+ Let's now call the function:
+
>>> from zope.testing.cleanup import CleanUp as PlacelessSetup
>>> PlacelessSetup().setUp()
-
- >>> datamanager.processSpec('a_collection', {'life': 42})
+ >>> datamanager.process_spec('a_collection', {'life': 42})
{'life': 42}
Now let's register an adapter
@@ -665,21 +687,105 @@
>>> import zope.interface
>>> from zope.component import provideAdapter
- >>> provideAdapter(Processor, (zope.interface.Interface,), interfaces.IMongoSpecProcessor)
+ >>> provideAdapter(
+ ... Processor,
+ ... (zope.interface.Interface,), interfaces.IMongoSpecProcessor)
- And see what happens on processSpec:
+ And see what happens on calling ``process_spec()``:
- >>> datamanager.processSpec('a_collection', {'life': 42})
+ >>> datamanager.process_spec('a_collection', {'life': 42})
passed in: a_collection {'life': 42}
{'life': 24}
We get the processed spec in return.
-
>>> PlacelessSetup().tearDown()
"""
+def doctest_FlushDecorator_basic():
+ r"""class FlushDecorator: basic functionality
+
+ The FlushDecorator class can be used to ensure that data is flushed before
+ a given function is called. Let's create an object and modify it:
+
+ >>> foo = Foo('foo')
+ >>> foo_ref = dm.dump(foo)
+ >>> dm.reset()
+ >>> foo_new = dm.load(foo._p_oid)
+ >>> foo_new.name = 'Foo'
+
+ The database is not immediately updated:
+
+ >>> coll = conn[DBNAME]['mongopersist.tests.test_datamanager.Foo']
+ >>> list(coll.find())
+ [{u'_id': ObjectId('4e7ddf12e138237403000000'), u'name': u'foo'}]
+
+
+ But when I use the decorator, all outstanding changes are updated at
+ first:
+
+ >>> flush_find = datamanager.FlushDecorator(dm, coll.find)
+ >>> list(flush_find())
+ [{u'_id': ObjectId('4e7ddf12e138237403000000'), u'name': u'Foo'}]
+
+ """
+
+def doctest_ProcessSpecDecorator_basic():
+ r"""class ProcessSpecDecorator: basic
+
+ The ``ProcessSpecDecorator`` decorator processes the spec before passing
+ it to the function. Currently the following collection methods are
+ supported: ``find_one()``, ``find()``, ``find_and_modify``.
+
+ Now let's register an adapter
+
+ >>> from zope.testing.cleanup import CleanUp as PlacelessSetup
+ >>> PlacelessSetup().setUp()
+
+ >>> class Processor(object):
+ ... def __init__(self, context):
+ ... pass
+ ... def process(self, collection, spec):
+ ... print 'passed in:', spec
+ ... return spec
+
+ >>> import zope.interface
+ >>> from zope.component import provideAdapter
+ >>> provideAdapter(
+ ... Processor,
+ ... (zope.interface.Interface,), interfaces.IMongoSpecProcessor)
+
+ Let's now create the decorator:
+
+ >>> coll = conn[DBNAME]['mongopersist.tests.test_datamanager.Foo']
+ >>> process_find = datamanager.ProcessSpecDecorator(coll, coll.find)
+ >>> list(process_find({'life': 42}))
+ passed in: {'life': 42}
+ []
+
+ Keyword arguments are also supported:
+
+ >>> process_find = datamanager.ProcessSpecDecorator(coll, coll.find)
+ >>> list(process_find(spec={'life': 42}))
+ passed in: {'life': 42}
+ []
+
+ >>> process_find_one = datamanager.ProcessSpecDecorator(
+ ... coll, coll.find_one)
+ >>> process_find_one(spec_or_id={'life': 42})
+ passed in: {'life': 42}
+
+ >>> process_find_one = datamanager.ProcessSpecDecorator(
+ ... coll, coll.find_one)
+ >>> process_find_one(query={'life': 42})
+ passed in: {'life': 42}
+
+ We get the processed spec in return.
+
+ >>> PlacelessSetup().tearDown()
+ """
+
def test_suite():
return doctest.DocTestSuite(
setUp=testing.setUp, tearDown=testing.tearDown,
Modified: mongopersist/trunk/src/mongopersist/zope/container.py
===================================================================
--- mongopersist/trunk/src/mongopersist/zope/container.py 2012-03-13 00:06:58 UTC (rev 124588)
+++ mongopersist/trunk/src/mongopersist/zope/container.py 2012-03-13 00:09:27 UTC (rev 124589)
@@ -22,7 +22,6 @@
from mongopersist import interfaces, serialize
from mongopersist.zope import interfaces as zinterfaces
-from mongopersist.datamanager import processSpec, CollectionWrapper
class MongoContained(contained.Contained):
@@ -165,7 +164,7 @@
filter = self._m_get_items_filter()
filter[self._m_mapping_key] = key
coll = self.get_collection()
- doc = coll.find_one(processSpec(coll, filter))
+ doc = coll.find_one(filter)
if doc is None:
raise KeyError(key)
return self._load_one(doc)
@@ -211,15 +210,14 @@
filter[self._m_mapping_key] = {'$ne': None}
coll = self.get_collection()
return [doc[self._m_mapping_key]
- for doc in coll.find(processSpec(coll, filter),
- fields=(self._m_mapping_key,))]
+ for doc in coll.find(filter, fields=(self._m_mapping_key,))]
def raw_find(self, spec=None, *args, **kwargs):
if spec is None:
spec = {}
spec.update(self._m_get_items_filter())
coll = self.get_collection()
- return coll.find(processSpec(coll, spec), *args, **kwargs)
+ return coll.find(spec, *args, **kwargs)
def find(self, spec=None, *args, **kwargs):
# Search for matching objects.
@@ -235,7 +233,7 @@
spec_or_id = {'_id': spec_or_id}
spec_or_id.update(self._m_get_items_filter())
coll = self.get_collection()
- return coll.find_one(processSpec(coll, spec_or_id), *args, **kwargs)
+ return coll.find_one(spec_or_id, *args, **kwargs)
def find_one(self, spec_or_id=None, *args, **kwargs):
doc = self.raw_find_one(spec_or_id, *args, **kwargs)
More information about the checkins
mailing list