Skip to content

Instantly share code, notes, and snippets.

@jomido
Created June 1, 2017 16:10
Show Gist options
  • Save jomido/1fbbb05b2211ff9820b3734ce8b768e4 to your computer and use it in GitHub Desktop.
Save jomido/1fbbb05b2211ff9820b3734ce8b768e4 to your computer and use it in GitHub Desktop.
Beginnings of a validation lib
from functools import partial as part
# some utils
def partial(func, *args, **kwargs):
# replace partial
partial_func = part(func, *args, **kwargs)
partial_func.__name__ = ' '.join([
func.__name__,
' '.join(['{}'.format(a) for a in args]) if args else ''
' '.join(['{}={}'.format(k, v) for k, v in kwargs.items()]) if kwargs else ''
])
return partial_func
def try_print(fn, *args, **kwargs):
try:
print(fn(*args, **kwargs))
except Exception as ex:
print(ex)
# predicates
def check_phone(text):
# magical regex or import lib-phonenumbers, etc.
return True
def is_text(o):
return isinstance(o, str)
def is_num(o):
return isinstance(o, (int, float))
def is_color(o):
return o in ('red', 'green', 'blue')
def is_phone(o):
return check_phone(o)
def starts_with(s, o):
return o.startswith(s)
def ends_with(s, o):
return o.endswith(s)
# validation class
class Validation(object):
def __init__(self, *fns, **kwargs):
self.fns = fns
fail_message = kwargs.get('fail_message')
if fail_message:
self.fail_message = fail_message
def __call__(self, o):
for fn in self.fns:
if not fn(o):
raise Exception(
getattr(fn, 'fail_message', None) or "not {}".format(
fn.__name__
)
)
return True
# partial application
starts_with_999 = partial(starts_with, '999')
ends_with_4567 = partial(ends_with, '4567')
# composition
valid_phone = Validation(
is_text,
is_phone,
)
valid_999_phone = Validation(
valid_phone,
starts_with_999
)
try_print(valid_phone, '123-4567')
try_print(valid_999_phone, '123-4567')
try_print(valid_999_phone, '999-4567')
is_preferred_color = Validation(
is_text,
is_color
)
# declarative
spec = {
'conf_phone': {
'pretty': 'Conference Phone',
'validations': [
valid_phone,
ends_with_4567
],
'required': True
},
'preferred_color': {
'pretty': 'Preferred Color',
'validations': [is_preferred_color]
}
}
# computation over declarative spec
def process(data, spec=None):
spec = spec or {}
for field, value in data.items():
definition = spec.get(field)
for validation in definition['validations']:
validation(value)
required = [field for field in spec.keys() if spec[field].get('required')]
for field in required:
if field not in data:
raise Exception('Missing field "{}".'.format(field))
return True
jacks_data = {
'conf_phone': '123-4567',
'preferred_color': 'blue'
}
jills_data = {
'conf_phone': '123-4567',
'preferred_color': 'yellow'
}
joes_data = {
'preferred_color': 'red'
}
my_process = part(process, spec=spec)
try_print(my_process, jacks_data)
try_print(my_process, jills_data)
try_print(my_process, joes_data)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment