Last active
August 29, 2015 13:55
-
-
Save mattupstate/8712081 to your computer and use it in GitHub Desktop.
Naive attempt at porting Oat (https://github.com/ismasan/oat) to Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import warnings | |
from collections import OrderedDict | |
from itertools import chain | |
from inflection import pluralize | |
class Adapter(object): | |
def __init__(self): | |
self.data = OrderedDict() | |
def to_dict(self): | |
return self.data | |
class HAL(Adapter): | |
def property(self, key, value): | |
self.data[key] = value | |
def relationship(self, key, value): | |
self.data.setdefault('_embedded', OrderedDict())[key] = value | |
def link(self, rel, opts): | |
if 'href' in opts: | |
self.data.setdefault('_links', OrderedDict())[rel] = opts | |
class Siren(Adapter): | |
def __init__(self): | |
super(Siren, self).__init__() | |
self.data['links'] = [] | |
self.data['entities'] = [] | |
self.data['actions'] = [] | |
def type(self, *types): | |
self.data['class'] = types | |
def property(self, key, value): | |
self.data.setdefault('properties', OrderedDict())[key] = value | |
def relationship(self, key, value): | |
if not isinstance(value, list): | |
value = [value] | |
self.data['entities'].extend(value) | |
def link(self, rel, opts): | |
opts.update({'rel': [rel]}) | |
self.data['links'].append(opts) | |
class JSON_API(Adapter): | |
def __init__(self): | |
super(JSON_API, self).__init__() | |
self.entities = OrderedDict() | |
def type(self, *types): | |
self.data['__type__'] = pluralize(types[0]) | |
def property(self, key, value): | |
self.data[key] = value | |
def relationship(self, key, value): | |
if not isinstance(value, list): | |
value = [value] | |
linked_key = pluralize(value[0].keys()[0]) | |
value = list(chain.from_iterable([item.pop(item.keys()[0]) for item in value])) | |
self.link(key, {'href': [i['id'] for i in value]}) | |
self.entities.setdefault(linked_key, []).extend(value) | |
def link(self, rel, opts): | |
self.data.setdefault('links', OrderedDict())[rel] = opts['href'] | |
def to_dict(self): | |
root_name = self.data.pop('__type__', None) | |
if not root_name: | |
raise Exception('JSON API schemas must define a type') | |
return OrderedDict([ | |
(root_name, [self.data]), | |
('linked', self.entities) | |
]) | |
def _getattr(obj, attr, coerce=None): | |
if not coerce: | |
coerce = lambda v: v | |
if callable(attr): | |
rv = attr(obj) | |
else: | |
rv = reduce(getattr, attr.split('.'), obj) | |
return coerce(rv) | |
def Relationship(name, attr=None, many=False, schema=None): | |
attr = attr or name | |
def _Relationship(obj, adapter): | |
_obj = _getattr(obj, attr) | |
if many: | |
val = [schema(o, adapter) for o in _obj] | |
else: | |
val = schema(_obj, adapter) | |
adapter.relationship(name, val) | |
return _Relationship | |
def Link(rel, **opts): | |
def _Link(obj, adapter): | |
for k, v in opts.iteritems(): | |
if callable(v): | |
opts[k] = v(obj) | |
adapter.link(rel, opts) | |
return _Link | |
def Property(name, attr=None, coerce=None): | |
attr = attr or name | |
def _Property(obj, adapter): | |
adapter.property(name, _getattr(obj, attr, coerce)) | |
return _Property | |
def Properties(*properties): | |
def _Properties(obj, adapter): | |
for p in properties: | |
name = attr = p | |
if isinstance(p, tuple): | |
name, attr = p | |
adapter.property(name, _getattr(obj, attr)) | |
return _Properties | |
def Type(*types): | |
def _Type(obj, adapter): | |
try: | |
adapter.type(*types) | |
except AttributeError: | |
msg = '%s adapter does not implement `type`' % adapter.__class__.__name__ | |
warnings.warn(msg, UserWarning) | |
return _Type | |
class _Schema(object): | |
def __init__(self, handlers): | |
self.handlers = handlers | |
def __call__(self, obj, adapter): | |
if isinstance(adapter, type): | |
adapter = adapter() | |
elif isinstance(adapter, Adapter): | |
adapter = adapter.__class__() | |
for handler in self.handlers: | |
handler(obj, adapter) | |
return adapter.to_dict() | |
def Schema(*handlers): | |
return _Schema(handlers) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import json | |
from oat import * | |
class Obj(object): | |
def __init__(self, **kwargs): | |
for k, v in kwargs.iteritems(): | |
setattr(self, k, v) | |
obj = Obj( | |
id=1, | |
first_name='Joe', | |
address=Obj(line1='1 Street Rd'), | |
child=Obj(id='2', name='Matt'), | |
children=[ | |
Obj(id='3', name='Tim'), | |
Obj(id='4', name='Mike') | |
] | |
) | |
schema = Schema( | |
Type('author'), | |
Link('self', href='http://google.com'), | |
Link('derp', href=lambda o: 'http://somewhere.com/%s' % o.id), | |
Property('id'), | |
Property('id1', lambda o: o.id, coerce=str), | |
Properties( | |
('name', 'first_name'), | |
('name1', lambda o: o.first_name), | |
('address', 'address.line1') | |
), | |
Relationship('child', schema=Schema( | |
Type('user'), | |
Properties('id', 'name') | |
)), | |
Relationship('children', many=True, schema=Schema( | |
Type('user'), | |
Properties('id', 'name') | |
)) | |
) | |
for a in HAL, Siren, JSON_API: | |
print json.dumps(schema(obj, a), indent=2) | |
print '-' * 50 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment