Skip to content

Instantly share code, notes, and snippets.

@mattupstate
Last active August 29, 2015 13:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mattupstate/8712081 to your computer and use it in GitHub Desktop.
Save mattupstate/8712081 to your computer and use it in GitHub Desktop.
Naive attempt at porting Oat (https://github.com/ismasan/oat) to Python
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)
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