summaryrefslogtreecommitdiff
path: root/warlock/model.py
diff options
context:
space:
mode:
authorRob <rob.s.brit@gmail.com>2012-11-27 11:16:13 -0500
committerBrian Waldon <bcwaldon@gmail.com>2012-12-22 15:39:09 -0800
commit98a44c660136b7089cf3e8ed95254b475ab4d085 (patch)
treed5ddbceef40053632558b96e72fffcbeaa123f07 /warlock/model.py
parente8f0a5ade79156cfe18ae5ec25c5737cc1f7e532 (diff)
downloadwarlock-98a44c660136b7089cf3e8ed95254b475ab4d085.tar.gz
Break up core pieces into separate modules
- Create model.py to hold the core Model class - Create exceptions.py to hold all custom exceptions
Diffstat (limited to 'warlock/model.py')
-rw-r--r--warlock/model.py108
1 files changed, 108 insertions, 0 deletions
diff --git a/warlock/model.py b/warlock/model.py
new file mode 100644
index 0000000..6dff70a
--- /dev/null
+++ b/warlock/model.py
@@ -0,0 +1,108 @@
+"""Self-validating model for arbitrary objects"""
+
+import copy
+
+import jsonpatch
+
+import exceptions
+
+
+class Model(dict):
+ def __init__(self, *args, **kwargs):
+ # Load the validator from the kwargs
+ #self.__dict__['validator'] = kwargs.pop('validator', self.default_validator)
+
+ # we overload setattr so set this manually
+ d = dict(*args, **kwargs)
+
+ try:
+ self.validator(d)
+ except exceptions.ValidationError as exc:
+ raise ValueError(str(exc))
+ else:
+ dict.__init__(self, d)
+
+ self.__dict__['changes'] = {}
+ self.__dict__['__original__'] = copy.deepcopy(d)
+
+ def __getattr__(self, key):
+ try:
+ return self.__getitem__(key)
+ except KeyError:
+ raise AttributeError(key)
+
+ def __setitem__(self, key, value):
+ mutation = dict(self.items())
+ mutation[key] = value
+ try:
+ self.validator(mutation)
+ except exceptions.ValidationError:
+ msg = "Unable to set '%s' to '%s'" % (key, value)
+ raise exceptions.InvalidOperation(msg)
+
+ dict.__setitem__(self, key, value)
+
+ self.__dict__['changes'][key] = value
+
+ def __setattr__(self, key, value):
+ self.__setitem__(key, value)
+
+ def clear(self):
+ raise exceptions.InvalidOperation()
+
+ def pop(self, key, default=None):
+ raise exceptions.InvalidOperation()
+
+ def popitem(self):
+ raise exceptions.InvalidOperation()
+
+ def __delitem__(self, key):
+ mutation = dict(self.items())
+ del mutation[key]
+ try:
+ self.validator(mutation)
+ except exceptions.ValidationError:
+ msg = "Unable to delete attribute '%s'" % (key)
+ raise exceptions.InvalidOperation(msg)
+
+ dict.__delitem__(self, key)
+
+ def __delattr__(self, key):
+ self.__delitem__(key)
+
+ # NOTE(termie): This is kind of the opposite of what copy usually does
+ def copy(self):
+ return copy.deepcopy(dict(self))
+
+ def update(self, other):
+ mutation = dict(self.items())
+ mutation.update(other)
+ try:
+ self.validator(mutation)
+ except exceptions.ValidationError as exc:
+ raise exceptions.InvalidOperation(str(exc))
+ dict.update(self, other)
+
+ def iteritems(self):
+ return copy.deepcopy(dict(self)).iteritems()
+
+ def items(self):
+ return copy.deepcopy(dict(self)).items()
+
+ def itervalues(self):
+ return copy.deepcopy(dict(self)).itervalues()
+
+ def values(self):
+ return copy.deepcopy(dict(self)).values()
+
+ @property
+ def changes(self):
+ return copy.deepcopy(self.__dict__['changes'])
+
+ @property
+ def patch(self):
+ original = self.__dict__['__original__']
+ return jsonpatch.make_patch(original, dict(self)).to_string()
+
+ def default_validator(self, *args, **kwargs):
+ return True