Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 21 additions & 14 deletions restless/preparers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ class Preparer(object):
"""
A plain preparation object which just passes through data.

It also is relevant as the protocol subclasses should implement to work with
Restless.
It also is relevant as the protocol subclasses should implement to
work with Restless.
"""
def __init__(self):
super(Preparer, self).__init__()
Expand Down Expand Up @@ -41,8 +41,8 @@ def __init__(self, fields):

def prepare(self, data):
"""
Handles transforming the provided data into the fielded data that should
be exposed to the end user.
Handles transforming the provided data into the fielded data that
should be exposed to the end user.

Uses the ``lookup_data`` method to traverse dotted paths.

Expand All @@ -51,6 +51,7 @@ def prepare(self, data):
result = {}

if not self.fields:

# No fields specified. Serialize everything.
return data

Expand All @@ -61,15 +62,16 @@ def prepare(self, data):

def lookup_data(self, lookup, data):
"""
Given a lookup string, attempts to descend through nested data looking for
the value.
Given a lookup string, attempts to descend through nested data looking
for the value.

Can work with either dictionary-alikes or objects (or any combination of
those).
Can work with either dictionary-alikes or objects (or any combination
of those).

Lookups should be a string. If it is a dotted path, it will be split on
``.`` & it will traverse through to find the final value. If not, it will
simply attempt to find either a key or attribute of that name & return it.
``.`` & it will traverse through to find the final value. If not, it
will simply attempt to find either a key or attribute of that name &
return it.

Example::

Expand All @@ -90,7 +92,10 @@ def lookup_data(self, lookup, data):
'hello'
>>> lookup_data('person.name', data)
'daniel'

>>> lookup_data('greeting.se', data)
None
>>> lookup_data('idontexist', data)
None
"""
value = data
parts = lookup.split('.')
Expand All @@ -102,13 +107,15 @@ def lookup_data(self, lookup, data):
remaining_lookup = '.'.join(parts[1:])

if hasattr(data, 'keys') and hasattr(data, '__getitem__'):

# Dictionary enough for us.
value = data[part]
value = data.get(part, None)
else:

# Assume it's an object.
value = getattr(data, part)
value = getattr(data, part, None)

if not remaining_lookup:
if not remaining_lookup or not value:
return value

# There's more to lookup, so dive in recursively.
Expand Down
17 changes: 10 additions & 7 deletions tests/test_preparers.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def setUp(self):
awesome=True,
depth=3
),
},
}
}

def test_dict_simple(self):
Expand All @@ -51,18 +51,21 @@ def test_obj_nested(self):
self.assertEqual(self.preparer.lookup_data('moof.buried.id', self.obj_data), 7)
self.assertEqual(self.preparer.lookup_data('moof.buried.data.yes', self.obj_data), 'no')

def test_dict_nullable_fk(self):
self.assertEqual(self.preparer.lookup_data('more.this_does_not_exist', self.dict_data), None)

def test_obj_nullable_fk(self):
self.assertEqual(self.preparer.lookup_data('moof.this_does_not_exist', self.obj_data), None)

def test_dict_miss(self):
with self.assertRaises(KeyError):
self.preparer.lookup_data('another', self.dict_data)
self.assertEqual(self.preparer.lookup_data('another', self.dict_data), None)

def test_obj_miss(self):
with self.assertRaises(AttributeError):
self.preparer.lookup_data('whee', self.obj_data)
self.assertEqual(self.preparer.lookup_data('whee', self.obj_data), None)

def test_empty_lookup(self):
# We could possibly get here in the recursion.
self.assertEqual(self.preparer.lookup_data('', 'Last value'), 'Last value')

def test_complex_miss(self):
with self.assertRaises(AttributeError):
self.preparer.lookup_data('more.nested.nope', self.dict_data)
self.assertEquals(self.preparer.lookup_data('more.nested.nope', self.dict_data), None)