influentialpublishers/awesomize

View on GitHub
lib/check.js

Summary

Maintainability
A
0 mins
Test Coverage

const _        = require('ramda');
const Bluebird = require('bluebird');
const v        = require('validator');

const MSG  = {
  REQUIRED                : 'required'
, REQUIRE_ARRAY           : 'require_array'
, CANNOT_BE_EQUAL         : 'cannot_be_equal'
, NOT_INT                 : 'not_int'
, NOT_STRING              : 'not_string'
, NOT_FUNCTION            : 'not_function'
, NOT_ARRAY               : 'not_array'
, LIST_ITEM_NOT_SPEC      : 'list_item_not_spec'
, NOT_NULLABLE            : 'cannot_be_null'
, IMMUTABLE               : 'cannot_change_immutable'
, NOT_IN_RANGE            : 'not_in_range'
, NOT_IN                  : 'not_in'
, NOT_BOOL                : 'not_bool'
, MINIMUM_NOT_MET         : 'minimum_not_met'
, MAXIMUM_EXCEEDED        : 'maximum_exceeded'
, MINIMUM_LENGTH_NOT_MET  : 'min_length_not_met'
, MAXIMUM_LENGTH_EXCEEDED : 'maximum_length_exceeded'
, DOES_NOT_MATCH          : 'does_not_match'
, INVALID_EMAIL           : 'invalid_email'
};


// basic : (a -> Bool), String -> (a -> Maybe String)
const basic = (test, msg) => (...args) => {
  return test.apply(null, args) ? null : msg
}

// extern : (a -> Bool), String -> (a -> Maybe String)
const extern = (test, msg) => _.composeP(
  basic(_.equals(true), msg)
, _.when(_.isNil, _.T)
, _.compose(Bluebird.resolve, test)
);


// required : a -> Maybe String
const required = basic(
  _.compose(_.not, _.either(_.isEmpty, _.isNil))
, MSG.REQUIRED
);
required.always_run = true;


// requireArray : a -> Maybe String
const requireArray = basic(_.is(Array), MSG.REQUIRE_ARRAY);
requireArray.always_run = true;


// notEqual : a -> Maybe String
const notEqual = (x) => basic(
  _.compose(_.not, _.equals(x))
, MSG.CANNOT_BE_EQUAL
);


// isInt : a -> Maybe String
const isInt = basic(_.unary(v.isInt), MSG.NOT_INT);


// isString : a -> Maybe String
const isString = basic(_.is(String), MSG.NOT_STRING);


// isFunction : a -> Maybe String
const isFunction = basic(_.is(Function), MSG.NOT_FUNCTION);


// isArray : a -> Maybe String
const isArray = basic(_.is(Array), MSG.NOT_ARRAY);


// listOf : (a -> Bool), String -> Array a -> Promise Maybe String
const listOf  = (check, msg) => (list) => {

  const validator = extern(check, msg || MSG.LIST_ITEM_NOT_SPEC);
  const reducer   = (result, item) => result ? result : validator(item);

  return Bluebird.reduce(list, reducer, null);
};


// notNullable : a -> Maybe String
const notNullable = basic(
  (value, request, current, key) => { return _.all(
    _.compose(_.not, _.equals(null))
    , [ value, _.prop(key, current) ]
    )
  }
  , MSG.NOT_NULLABLE
);

// immutable : a -> Maybe String
const immutable = basic(
  (value, request, current, key) => { return _.propEq(key, value, current) }
  , MSG.IMMUTABLE
);


// _satisfies : Function -> [ String ] -> Bool
const _satisfies = (condFn) => (keys) => _.compose(
  _.not
  , condFn(_.compose(_.not, _.isNil)),
  _.props(keys)
)


// reqIf : Function -> [ String ] -> Maybe String
const _reqIf = (condFn) => (keys) => basic(
  _.useWith(_.or, [ _.identity , _satisfies(condFn)(keys) ])
  , MSG.REQUIRED
)


// reqIfHasAny : [ String ] -> a , Request -> Maybe String
const reqIfHasAny = _reqIf(_.any);


// reqIfHasAll : [ String ] -> a , Request -> Maybe String
const reqIfHasAll = _reqIf(_.all);


// notReqIfHasAny : [ String ] -> a , Request -> Maybe String
const notReqIfHasAny = _reqIf(_.curryN(2, _.compose(_.not, _.any)))


// notReqIfHasAll : [ String ] -> a , Request -> Maybe String
const notReqIfHasAll = _reqIf(_.curryN(2, _.compose(_.not, _.all)))


// _isInRangeInc : Int, Int -> (Int -> Bool)
const _isBetweenInc = (min, max) => _.allPass([ _.lte(min), _.gte(max) ])


// _isBetween : Int -> Maybe String
const _betweenIf = _.ifElse(
  _.identity
, _.always(null)
, _.always(MSG.NOT_IN_RANGE)
);


// isLength : Int, Int -> (a -> Maybe String)
const isLength = (min, max) => _.compose(
  _betweenIf
, _isBetweenInc(min, max)
, _.length
)


// Convert the string to an array to accomodate for unicode symbols, that count
// as more than one character in type String, but should be counted only as one.
// This funciton behaves exactly as isLength, but accounts for Unicode
// isLengthUnicode : Int, Int -> (String -> Maybe String)
const isLengthUnicode = (min, max) => (str) => {
  return isLength(min, max)(Array.from(str))
}


// isIn :: Array -> * -> Maybe String
const isIn = (options) => _.ifElse(
  _.flip(_.contains)(options)
, _.always(null)
, _.always(MSG.NOT_IN)
)

// isBool :: Integer -> Maybe String
const isBool = _.ifElse(
 _.either(
  _.flip(_.contains)([true, false]),
  _.compose(
    _.flip(_.contains)([0,1]),
    (i) => parseInt(i, 10)
  )
 )
, _.always(null)
, _.always(MSG.NOT_BOOL)
)

// isInRangeInclusive :: Integer, Integer -> value -> Maybe String
const isInRangeInclusive = (min, max) => _.ifElse(
  _.compose(
    _isBetweenInc(min, max)
  , (i) => parseInt(i,10)
  )
, _.always(null)
, _.always(MSG.NOT_IN_RANGE)
)

// _isNot :: a -> (a -> Bool)
const _isNot = (val) => _.compose(_.not, _.equals(val))

// defined "" a -> Maybe String
const defined = basic( _isNot(undefined), MSG.REQUIRED)

// min :: Integer -> Integer -> Maybe String
const min = basic(_.lte, MSG.MINIMUM_NOT_MET)

// max :: Integer -> Integer -> Maybe String
const max = basic(_.gte, MSG.MAXIMUM_EXCEEDED)

// _isAtLeastMin :: Integer -> String -> Bool
const _isAtLeastMin = (min, text) => _.compose(_.lte(min), _.length)(text)

// minLength :: Integer -> String -> Maybe String
const minLength = _.curryN(2, basic(_isAtLeastMin, MSG.MINIMUM_LENGTH_NOT_MET))

// _isLessThanMax :: Integer -> String -> Bool
const _isLessThanMax = (max, text) => _.compose(_.gte(max), _.length)(text)

// maxLength :: Integer -> String -> Maybe String
const maxLength = _.curryN(2, basic(_isLessThanMax, MSG.MAXIMUM_LENGTH_EXCEEDED))


// isEqualTo :: String -> Value, Object -> Maybe String
const isEqualTo = _.curryN(3, basic(_.propEq, MSG.DOES_NOT_MATCH))

// checkEmail :: String -> Bool
const checkEmail = (val) => (v.isEmail(val))

// isValidEmail :: String -> Maybe String
const isValidEmail = basic(checkEmail, MSG.INVALID_EMAIL)

module.exports = {
  MSG
, extern
, required
, requireArray
, notEqual
, isInt
, isString
, isFunction
, isArray
, listOf
, notNullable
, immutable
, reqIfHasAny
, reqIfHasAll
, notReqIfHasAny
, notReqIfHasAll
, isLength
, isLengthUnicode
, isIn
, isBool
, isInRangeInclusive
, defined
, min
, max
, minLength
, maxLength
, isEqualTo
, isValidEmail
};