Commits

Andy Mikhailenko  committed 4cfc654

Added tests+fixes for a couple of edge cases. Added support for pymongo.DBRef in modeling. Bumped version.

  • Participants
  • Parent commits 64f5674
  • Tags 0.1.0

Comments (0)

Files changed (4)

File monk/manipulation.py

                 value = spec[key]
 
             # special handling of dict and list instances
-            if isinstance(value, dict):
+            if (spec[key] == dict or isinstance(spec[key], dict)) and isinstance(value, dict):
                 # nested dictionary
                 value = merged(spec.get(key, {}), value)
-            elif isinstance(value, list):
+            if (spec[key] == list or isinstance(spec[key], list)) and isinstance(value, list):
                 # nested list
                 item_spec = spec[key][0] if spec[key] else None
                 if isinstance(item_spec, type):
                 else:
                     # probably default list item like [1]
                     pass
+            else:
+                # some value from spec that can be checked for type
+                pass
         else:
             # never mind if there are nested structures: anyway we cannot check
             # them as they aren't in the spec
             value = data[key]
+
         if isinstance(value, type):
             # there's no default value for this key, just a restriction on type
             value = None
+
         result[key] = value
+
     return result

File monk/modeling.py

         if not attr.startswith('_') and attr in self:
             self[attr] = value
 
+    def __setitem__(self, key, value):
+        if isinstance(value, dict) and \
+           not isinstance(value, DotExpandedDict):
+            value = make_dot_expanded(value)
+        super(DotExpandedDictMixin, self).__setitem__(key, value)
+
+
 
 class DotExpandedDict(DotExpandedDictMixin, dict):
     def __init__(self, *args, **kwargs):
 
     @classmethod
     def wrap_incoming(cls, data, db):
-        return cls(dict_from_db(data, db))
+        # XXX self.structure belongs to StructuredDictMixin !!
+        return cls(dict_from_db(cls.structure, data, db))
 
     @classmethod
     def find(cls, db, *args, **kwargs):
 
     def save(self, db):
         assert self.collection
+        # XXX self.structure belongs to StructuredDictMixin !!
         outgoing = dict(dict_to_db(self, self.structure))
         db[self.collection].save(outgoing)
 
         for key, value in with_defaults.iteritems():
             self[key] = value
 
+    def _validate_structure_spec(self):
+        validation.validate_structure_spec(self.structure)
+
     def validate(self):
         validation.validate_structure(self.structure, self)
 
     """
     def __init__(self, *args, **kwargs):
         super(Document, self).__init__(*args, **kwargs)
+# TODO
+#        self._validate_structure_spec()
         self._insert_defaults()
         self._make_dot_expanded()
 
         super(Document, self).save(db)
 
 
-def dict_from_db(data, db):
-    def generate():
-        for key, value in data.iteritems():
-            if isinstance(value, dbref.DBRef):
-                yield key, dict(
-                    db.dereference(value),
-                    _id = value['_id']
-                )
-            else:
-                yield key, value
-    return dict(generate())
+def _db_to_dict_pairs(spec, data, db):
+    for key, value in data.iteritems():
+        if isinstance(value, dict):
+            yield key, dict(_db_to_dict_pairs(spec.get(key, {}), value, db))
+        elif isinstance(value, dbref.DBRef):
+            yield key, dict(
+                db.dereference(value),
+                _id = value['_id']
+            )
+        else:
+            yield key, value
 
 
-def dict_to_db(data, structure={}):
-    def generate():
-        for key, value in data.iteritems():
-            if hasattr(value, '_id'):
-                collection = structure[key].collection
+def dict_from_db(spec, data, db):
+    return dict(_db_to_dict_pairs(spec, data, db))
+
+
+def _dict_to_db_pairs(spec, data):
+    for key, value in data.iteritems():
+        if isinstance(value, dict):
+            if '_id' in value:
+                collection = spec[key].collection
                 yield key, dbref.DBRef(collection, value['_id'])
             else:
-                yield key, value
-    return dict(generate())
+                yield key, dict(_dict_to_db_pairs(spec.get(key, {}), value))
+        else:
+            yield key, value
+
+
+def dict_to_db(data, spec={}):
+    return dict(_dict_to_db_pairs(spec, data))
     long_description = readme,
 
     # technical info
-    version  = '0.0.1',
+    version  = '0.1.0',
 #    packages = ['monk'],
     requires = ['python (>= 2.7)'],
     provides = ['monk'],

File unittests/test_manipulation.py

         "custom keys should not be lost even if they are not in spec"
         data = {'a': [{'b': {'c': 123}}]}
         assert data == merged({}, data)
+
+    def test_unexpected_dict(self):
+        """ Non-dictionary in spec, dict in data.
+        Data is preserved though won't validate.
+        """
+        assert {'a': {'b': 123}} == merged({'a': unicode}, {'a': {'b': 123}})
+
+    def test_unexpected_list(self):
+        """ Non-list in spec, list in data.
+        Data is preserved though won't validate.
+        """
+        assert {'a': [123]} == merged({'a': unicode}, {'a': [123]})