+class Record(object):
+ _identity_field = None
+ def get(self, field):
+ try:
+ return self.__getattribute__(field)
+ except:
+ return None
+ def _formatField(self, field):
+ v = self.get(field)
+ if callable(v):
+ v = v()
+ if hasattr(v, '__iter__'):
+ if len(v) == 0:
+ return '[]'
+ else:
+ return '[%d x %s]'%(len(v), type(v[0]))
+ else:
+ return repr(v)
+ def _fields(self):
+ ignore = self._ignore()
+ keys = sorted(self.__class__.__dict__.keys())
+ return [(k,self._formatField(k)) for k in keys if k[0]!="_" and k not in ignore]
+ def __repr__(self):
+ classname = self.__class__.__name__
+
+ if self._identity_field:
+ identity = self.__dict__.get(self._identity_field)
+ identity = ' ' + (identity and repr(identity) or 'hash=%d'%hash(self))
+ else:
+ identity = ''
+
+ payload = " ".join(["%s=%s" % (k, v) for k,v in self._fields()])
+ if len(payload) > 0:
+ payload = ": "+payload
+
+ return "<%s%s%s>" % (classname, identity, payload)
+ def _ignore(self):
+ return [self._identity_field, 'c', 'query', 'get']
+
+class FormattableRecord(Record):
+ _format = {}
+ def _formatField(self, field):
+ func = self._format.get(field)
+ if func:
+ return func(self.get(field))
+ else:
+ return super(FormattableRecord, self)._formatField(field)
+
+class NullableRecord(FormattableRecord):
+ _default = {}
+ def get(self, field):
+ v = self.__dict__.get(field)
+ if v != None:
+ return v
+ else:
+ return self._default.get(field)