Skip to content
This repository has been archived by the owner on May 13, 2020. It is now read-only.

Commit

Permalink
- Feature: Added a hook via the IMongoSpecProcessor adapter that gets…
Browse files Browse the repository at this point in the history
… called before each find to process/log spec.
  • Loading branch information
Adam Groszer committed Mar 5, 2012
1 parent e7e9fcc commit 086a778
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 9 deletions.
14 changes: 12 additions & 2 deletions src/mongopersist/datamanager.py
Expand Up @@ -27,6 +27,14 @@ def create_conflict_error(obj, new_doc):
None, obj,
(new_doc.get('_py_serial', 0), serialize.u64(obj._p_serial)))

def processSpec(collection, spec):
try:
adapter = interfaces.IMongoSpecProcessor(None)
except TypeError:
# by default nothing is registered, handle that case
return spec

return adapter.process(collection, spec)

class Root(UserDict.DictMixin):

Expand All @@ -43,7 +51,8 @@ def __init__(self, jar, database=None, collection=None):
self._collection_inst = db[self.collection]

def __getitem__(self, key):
doc = self._collection_inst.find_one({'name': key})
doc = self._collection_inst.find_one(
processSpec(self._collection_inst, {'name': key}))
if doc is None:
raise KeyError(key)
return self._jar.load(doc['ref'])
Expand All @@ -56,7 +65,8 @@ def __setitem__(self, key, value):
self._collection_inst.insert(doc)

def __delitem__(self, key):
doc = self._collection_inst.find_one({'name': key})
doc = self._collection_inst.find_one(
processSpec(self._collection_inst, {'name': key}))
coll = self._jar._conn[doc['ref'].database][doc['ref'].collection]
coll.remove(doc['ref'].id)
self._collection_inst.remove({'name': key})
Expand Down
7 changes: 7 additions & 0 deletions src/mongopersist/interfaces.py
Expand Up @@ -166,3 +166,10 @@ class IMongoDataManagerProvider(zope.interface.Interface):

def get():
"""Return a mongo data manager."""


class IMongoSpecProcessor(zope.interface.Interface):
"""An adapter to process find/update spec's"""

def process(collection, spec):
"""return the processed spec here"""
7 changes: 5 additions & 2 deletions src/mongopersist/mapping.py
Expand Up @@ -16,6 +16,7 @@
import pymongo

from mongopersist import interfaces
from mongopersist.datamanager import processSpec

class MongoCollectionMapping(UserDict.DictMixin, object):
__mongo_database__ = None
Expand All @@ -35,7 +36,8 @@ def get_mongo_collection(self):
def __getitem__(self, key):
filter = self.__mongo_filter__()
filter[self.__mongo_mapping_key__] = key
doc = self.get_mongo_collection().find_one(filter)
coll = self.get_mongo_collection()
doc = coll.find_one(processSpec(coll, filter))
if doc is None:
raise KeyError(key)
db_name = self.__mongo_database__ or self._m_jar.default_database
Expand All @@ -59,6 +61,7 @@ def __delitem__(self, key):
def keys(self):
filter = self.__mongo_filter__()
filter[self.__mongo_mapping_key__] = {'$ne': None}
coll = self.get_mongo_collection()
return [
doc[self.__mongo_mapping_key__]
for doc in self.get_mongo_collection().find(filter)]
for doc in coll.find(processSpec(coll, filter))]
41 changes: 40 additions & 1 deletion src/mongopersist/tests/test_datamanager.py
Expand Up @@ -18,7 +18,7 @@
import transaction
from pymongo import dbref, objectid

from mongopersist import testing, datamanager
from mongopersist import interfaces, testing, datamanager

class Foo(persistent.Persistent):
def __init__(self, name=None):
Expand Down Expand Up @@ -346,6 +346,45 @@ def doctest_MongoDataManager_sortKey():
('MongoDataManager', 0)
"""

def doctest_processSpec():
r"""processSpec(): General test
A simple helper function that returns the spec itself if no
IMongoSpecProcessor adapter is registered.
>>> from zope.testing.cleanup import CleanUp as PlacelessSetup
>>> PlacelessSetup().setUp()
>>> datamanager.processSpec('a_collection', {'life': 42})
{'life': 42}
Now let's register an adapter
>>> class Processor(object):
... def __init__(self, context):
... pass
... def process(self, collection, spec):
... print 'passed in:', collection, spec
... return {'life': 24}
>>> import zope.interface
>>> from zope.component import provideAdapter
>>> provideAdapter(Processor, (zope.interface.Interface,), interfaces.IMongoSpecProcessor)
And see what happens on processSpec:
>>> datamanager.processSpec('a_collection', {'life': 42})
passed in: a_collection {'life': 42}
{'life': 24}
We get the processed spec in return.
>>> PlacelessSetup().tearDown()
"""

def test_suite():
return doctest.DocTestSuite(
setUp=testing.setUp, tearDown=testing.tearDown,
Expand Down
13 changes: 9 additions & 4 deletions src/mongopersist/zope/container.py
Expand Up @@ -22,6 +22,7 @@

from mongopersist import interfaces, serialize
from mongopersist.zope import interfaces as zinterfaces
from mongopersist.datamanager import processSpec

class MongoContained(contained.Contained):

Expand Down Expand Up @@ -156,7 +157,8 @@ def __getitem__(self, key):
raise KeyError(key)
filter = self._m_get_items_filter()
filter[self._m_mapping_key] = key
doc = self.get_collection().find_one(filter, fields=())
coll = self.get_collection()
doc = coll.find_one(processSpec(coll, filter), fields=())
if doc is None:
raise KeyError(key)
dbref = pymongo.dbref.DBRef(
Expand Down Expand Up @@ -197,9 +199,10 @@ def __delitem__(self, key):
def keys(self):
filter = self._m_get_items_filter()
filter[self._m_mapping_key] = {'$ne': None}
coll = self.get_collection()
keys = [
doc[self._m_mapping_key]
for doc in self.get_collection().find(filter)
for doc in coll.find(processSpec(coll, filter))
if not doc[self._m_mapping_key] in self._deleted]
keys += self._added.keys()
return keys
Expand All @@ -208,7 +211,8 @@ def raw_find(self, spec=None, *args, **kwargs):
if spec is None:
spec = {}
spec.update(self._m_get_items_filter())
return self.get_collection().find(spec, *args, **kwargs)
coll = self.get_collection()
return coll.find(processSpec(coll, spec), *args, **kwargs)

def find(self, spec=None, fields=None, *args, **kwargs):
# If fields were not specified, we only request the oid and the key.
Expand All @@ -230,7 +234,8 @@ def raw_find_one(self, spec_or_id=None, *args, **kwargs):
if not isinstance(spec_or_id, dict):
spec_or_id = {'_id': spec_or_id}
spec_or_id.update(self._m_get_items_filter())
return self.get_collection().find_one(spec_or_id, *args, **kwargs)
coll = self.get_collection()
return coll.find_one(processSpec(coll, spec_or_id), *args, **kwargs)

def find_one(self, spec_or_id=None, fields=None, *args, **kwargs):
# If fields were not specified, we only request the oid and the key.
Expand Down

0 comments on commit 086a778

Please sign in to comment.