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)
