Created
October 23, 2017 19:18
-
-
Save andrejewski/e9e13728c2674adfa3c7cb9b3dd465ad to your computer and use it in GitHub Desktop.
Clojure Spec, but for CanJs
This file contains hidden or 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
| export function getNestedChecks (check) { | |
| if (typeof check !== 'object') { | |
| return check; | |
| } | |
| return (x, key) => { | |
| const props = Object.keys(check); | |
| for (let i = 0; i < props.length; i++) { | |
| const prop = props[i]; | |
| const subCheck = getNestedChecks(check[prop]); | |
| const error = subCheck(x[prop], `${key}.${prop}`); | |
| if (error) { | |
| return error; | |
| } | |
| } | |
| }; | |
| } | |
| export function getTypeCheck (type, key) { | |
| switch (type) { | |
| case Boolean: return (x, key) => { | |
| if (typeof x !== 'boolean') { | |
| return new Error(`Property "${key}" must be a boolean, not ${x}`); | |
| } | |
| }; | |
| case Number: return (x, key) => { | |
| if (typeof x !== 'number') { | |
| return new Error(`Property "${key}" must be a number, not ${x}`); | |
| } | |
| }; | |
| case String: return (x, key) => { | |
| if (typeof x !== 'string') { | |
| return new Error(`Property "${key}" must be a string, not ${x}`); | |
| } | |
| }; | |
| case Function: return (x, key) => { | |
| if (typeof x !== 'function') { | |
| return new Error(`Property "${key}" must be a function, not ${x}`); | |
| } | |
| }; | |
| case Error: return (x, key) => { | |
| if (!(x instanceof Error)) { | |
| return new Error(`Property "${key}" must be an error, not ${x}`); | |
| } | |
| }; | |
| } | |
| if (type instanceof Maybe) { | |
| const subType = getNestedChecks(getTypeCheck(type.type, key)); | |
| return (x, key) => { | |
| if (x !== null) { | |
| return subType(x, key); | |
| } | |
| }; | |
| } | |
| if (Array.isArray(type)) { | |
| const subType = getNestedChecks(getTypeCheck(type[0])); | |
| return (x, key) => { | |
| if (!Array.isArray(x)) { | |
| return new Error(`Property "${key}" must be an array, not ${x}`); | |
| } | |
| for (let i = 0; i < x.length; i++) { | |
| const error = subType(x[i], `${key}[${i}]`); | |
| if (error) { | |
| return error; | |
| } | |
| } | |
| }; | |
| } | |
| if (typeof type === 'object') { | |
| return Object.keys(type).reduce((checks, key) => { | |
| const subType = type[key]; | |
| const check = getTypeCheck(subType, key); | |
| checks[key] = check; | |
| return checks; | |
| }, {}); | |
| } | |
| if (typeof type === 'function') { | |
| return type; | |
| } | |
| throw new Error(`Spec key "${key}" value ${type} is not a valid type check`); | |
| } | |
| function isDev () { | |
| let isDev = false; | |
| // !steal-remove-start | |
| isDev = true; | |
| // !steal-remove-end | |
| return isDev; | |
| } | |
| export function Maybe (type) { | |
| if (!(this instanceof Maybe)) { | |
| return new Maybe(type); | |
| } | |
| this.type = type; | |
| } | |
| export function getCheck (spec, keys) { | |
| const [key, ...rest] = keys; | |
| const check = spec[key]; | |
| if (typeof check === 'function') { | |
| return check; | |
| } else if (typeof check === 'object') { | |
| if (rest.length < 1) { | |
| return getNestedChecks(check); | |
| } | |
| return getCheck(check, rest); | |
| } | |
| } | |
| export default function specMap (getSpec) { | |
| const spec = getSpec({maybe: Maybe}); | |
| const specChecks = getTypeCheck(spec); | |
| const listener = (event, attribute, how, newValue) => { | |
| const check = getCheck(specChecks, attribute.split('.')); | |
| if (check) { | |
| const error = check(newValue, attribute); | |
| if (error) { | |
| throw error; | |
| } | |
| } | |
| }; | |
| return baseMap => !isDev() ? baseMap : baseMap.extend({ | |
| init () { | |
| if (!this.__specListener) { | |
| this.__specListener = listener; | |
| this.bind('change', listener); | |
| } | |
| } | |
| }); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment