Commit 10dbb4b5 authored by Stefan Cameron's avatar Stefan Cameron

Add new types INT and SAFE_INT, refine existing number types

This was good as it forced a clean-up of the existing
NUMBER, FINITE, and FLOAT.

Fix a few other bugs/typos along the way.

Coverage remains at 100%.
parent 811a4839
......@@ -76,6 +76,7 @@
"block-spacing": ["error", "always"],
"brace-style": ["error", "1tbs", {"allowSingleLine": true}],
"camelcase": ["error", {"properties": "always"}],
"comma-dangle": ["error", "never"],
"comma-spacing": ["error", {"before": false, "after": true}],
"comma-style": ["error", "last"],
"computed-property-spacing": ["error", "never"],
......
......@@ -514,19 +514,29 @@ const defs = {
* In all cases, the value must be a number {@link rtvref.types.primitives primitive}.
* Note that `new Number(1) !== 1` because the former is an _object_, not a number.
*
* An number is not guaranteed to be a
* {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger safe integer}.
*
* Arguments (optional): {@link rtvref.types.numeric_args}
*
* @name rtvref.types.NUMBER
* @const {string}
* @see {@link rtvref.qualifiers}
* @see {@link rtvref.types.FINITE}
* @see {@link rtvref.types.INT}
* @see {@link rtvref.types.SAFE_INT}
* @see {@link rtvref.types.FLOAT}
*/
NUMBER: def('number', true),
/**
* Finite rules per qualifiers: Cannot be `NaN`, `+Infinity`, `-Infinity`. The
* value can be either a safe integer or a {@link rtvref.types.FLOAT floating point number}.
* It must also be a number {@link rtvref.types.primitives primitive}.
* value can be either an {@link rtvref.types.INT integer},
* or a {@link rtvref.types.FLOAT floating point number}. It must also be a
* number {@link rtvref.types.primitives primitive}.
*
* A finite number is not guaranteed to be a
* {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger safe integer}.
*
* Arguments (optional): {@link rtvref.types.numeric_args}
*
......@@ -534,37 +544,65 @@ const defs = {
* @const {string}
* @see {@link rtvref.qualifiers}
* @see {@link rtvref.types.NUMBER}
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger Number.isSafeInteger()}
* @see {@link rtvref.types.INT}
* @see {@link rtvref.types.SAFE_INT}
* @see {@link rtvref.types.FLOAT}
*/
FINITE: def('finite', true),
/**
* Int rules per qualifiers: Must be a {@link rtvref.types.FINITE finite} integer,
* but is not necessarily _safe_. It must also be a number
* {@link rtvref.types.primitives primitive}.
* Int rules per qualifiers: Must be a {@link rtvref.types.FINITE finite} number,
* an integer, and a number {@link rtvref.types.primitives primitive}.
*
* An integer is not guaranteed to be a
* {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger safe integer}.
*
* Arguments (optional): {@link rtvref.types.numeric_args}
*
* @name rtvref.types.INT
* @const {string}
* @see {@link rtvref.qualifiers}
* @see {@link rtvref.types.NUMBER}
* @see {@link rtvref.types.FINITE}
* @see {@link rtvref.types.SAFE_INT}
* @see {@link rtvref.types.FLOAT}
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger Number.isSafeInteger()}
*/
INT: def('int', true),
/**
* Int rules per qualifiers: Must be a {@link rtvref.types.FINITE finite} number, a
* {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger safe integer},
* and a number {@link rtvref.types.primitives primitive}.
*
* An integer is safe if it's an IEEE-754 double precision number which isn't
* the result of a rounded unsafe integer. For example, `2^53 - 1` is safe,
* but `2^53` is not because `2^53 + 1` would be rounded to `2^53`.
*
* Arguments (optional): {@link rtvref.types.numeric_args}
*
* @name rtvref.types.INT
* @const {string}
* @see {@link rtvref.qualifiers}
* @see {@link rtvref.types.NUMBER}
* @see {@link rtvref.types.FINITE}
* @see {@link rtvref.types.INT}
* @see {@link rtvref.types.FLOAT}
*/
SAFE_INT: def('safeInt', true),
/**
* Float rules per qualifiers: Must be a {@link rtvref.types.FINITE finite}
* floating point number. It must also be a number
* {@link rtvref.types.primitives primitive}.
* floating point number, and a number {@link rtvref.types.primitives primitive}.
*
* Arguments (optional): {@link rtvref.types.numeric_args}
*
* @name rtvref.types.FLOAT
* @const {string}
* @see {@link rtvref.qualifiers}
* @see {@link rtvref.types.NUMBER}
* @see {@link rtvref.types.FINITE}
* @see {@link rtvref.types.INT}
* @see {@link rtvref.types.SAFE_INT}
*/
FLOAT: def('float', true),
......
////// isInt validation
import {default as _isInteger} from 'lodash/isInteger';
import types from '../types';
/**
* Type: {@link rtvref.types.INT INT}
* @const {string} rtvref.validation.isInt.type
*/
export const type = types.INT;
/**
* {@link rtvref.validation.method Validation} for the
* {@link rtvref.types.INT INT} type.
*
* Determines if a value is an integer literal __only__ (i.e. a
* {@link rtvref.types.primitives primitive}). It does not validate
* `new Number(1)`, which is an object that is a number.
*
* @function rtvref.validation.isInt.default
* @param {*} v Value to validate.
* @returns {boolean} `true` if validated; `false` otherwise.
*/
export default function isInt(v) {
return _isInteger(v); // eliminates NaN, +/-Infinity, floats
}
////// isSafeInt validation
import {default as _isSafeInteger} from 'lodash/isSafeInteger';
import types from '../types';
/**
* Type: {@link rtvref.types.SAFE_INT SAFE_INT}
* @const {string} rtvref.validation.isSafeInt.type
*/
export const type = types.SAFE_INT;
/**
* {@link rtvref.validation.method Validation} for the
* {@link rtvref.types.SAFE_INT SAFE_INT} type.
*
* Determines if a value is an integer literal __only__ (i.e. a
* {@link rtvref.types.primitives primitive}). It does not validate
* `new Number(1)`, which is an object that is a number.
*
* @function rtvref.validation.isSafeInt.default
* @param {*} v Value to validate.
* @returns {boolean} `true` if validated; `false` otherwise.
*/
export default function isSafeInt(v) {
return _isSafeInteger(v); // eliminates NaN, +/-Infinity, floats, unsafe ints
}
......@@ -13,14 +13,14 @@ let impl; // @type {rtvref.impl}
* [Internal] __FOR UNIT TESTING ONLY:__ The {@link rtvref.impl} instance
* configured on this validator.
* @private
* @name rtvref.validator.valObject._impl
* @name rtvref.validator.valAny._impl
* @type {rtvref.impl}
*/
export {impl as _impl};
/**
* Type: {@link rtvref.types.ANY ANY}
* @const {string} rtvref.validator.isAny.type
* @const {string} rtvref.validator.valAny.type
*/
export {type};
......
......@@ -16,14 +16,14 @@ let impl; // @type {rtvref.impl}
* [Internal] __FOR UNIT TESTING ONLY:__ The {@link rtvref.impl} instance
* configured on this validator.
* @private
* @name rtvref.validator.valObject._impl
* @name rtvref.validator.valAnyObject._impl
* @type {rtvref.impl}
*/
export {impl as _impl};
/**
* Type: {@link rtvref.types.ANY_OBJECT ANY_OBJECT}
* @const {string} rtvref.validator.isAnyObject.type
* @const {string} rtvref.validator.valAnyObject.type
*/
export {type};
......
......@@ -16,14 +16,14 @@ let impl; // @type {rtvref.impl}
* [Internal] __FOR UNIT TESTING ONLY:__ The {@link rtvref.impl} instance
* configured on this validator.
* @private
* @name rtvref.validator.valObject._impl
* @name rtvref.validator.valArray._impl
* @type {rtvref.impl}
*/
export {impl as _impl};
/**
* Type: {@link rtvref.types.ARRAY ARRAY}
* @const {string} rtvref.validator.isArray.type
* @const {string} rtvref.validator.valArray.type
*/
export {type};
......
......@@ -13,14 +13,14 @@ let impl; // @type {rtvref.impl}
* [Internal] __FOR UNIT TESTING ONLY:__ The {@link rtvref.impl} instance
* configured on this validator.
* @private
* @name rtvref.validator.valObject._impl
* @name rtvref.validator.valBoolean._impl
* @type {rtvref.impl}
*/
export {impl as _impl};
/**
* Type: {@link rtvref.types.BOOLEAN BOOLEAN}
* @const {string} rtvref.validator.isBoolean.type
* @const {string} rtvref.validator.valBoolean.type
*/
export {type};
......
......@@ -13,14 +13,14 @@ let impl; // @type {rtvref.impl}
* [Internal] __FOR UNIT TESTING ONLY:__ The {@link rtvref.impl} instance
* configured on this validator.
* @private
* @name rtvref.validator.valObject._impl
* @name rtvref.validator.valFinite._impl
* @type {rtvref.impl}
*/
export {impl as _impl};
/**
* Type: {@link rtvref.types.FINITE FINITE}
* @const {string} rtvref.validator.isFinite.type
* @const {string} rtvref.validator.valFinite.type
*/
export {type};
......@@ -53,19 +53,19 @@ export default function valFinite(v, q = REQUIRED, args) {
return new RtvSuccess();
}
let valid = isFinite(v); // eliminates NaN, +/-Infinity
let valid = isFinite(v);
if (valid && args) { // then check args
if (isFinite(args.exact)) { // ignore if NaN, +/-Infinity
if (valid && args) { // then check args against normal type range
if (isFinite(args.exact)) {
valid = (v === args.exact);
} else {
let min;
if (valid && isFinite(args.min)) { // ignore if NaN, +/-Infinity
if (valid && isFinite(args.min)) {
min = args.min;
valid = (v >= min);
}
if (valid && isFinite(args.max)) { // ignore if NaN, +/-Infinity
if (valid && isFinite(args.max)) {
if (min === undefined || args.max >= min) {
valid = (v <= args.max);
} // else, ignore
......
......@@ -11,7 +11,7 @@ let impl; // @type {rtvref.impl}
/**
* Type: {@link rtvref.types.FUNCTION FUNCTION}
* @const {string} rtvref.validator.isFunction.type
* @const {string} rtvref.validator.valFunction.type
*/
export {type};
......
////// isInt validator
import {type, default as isInt} from '../validation/isInt';
import {default as qualifiers, nilPermitted} from '../qualifiers';
import RtvSuccess from '../RtvSuccess';
import RtvError from '../RtvError';
const {REQUIRED} = qualifiers;
let impl; // @type {rtvref.impl}
/**
* [Internal] __FOR UNIT TESTING ONLY:__ The {@link rtvref.impl} instance
* configured on this validator.
* @private
* @name rtvref.validator.valInt._impl
* @type {rtvref.impl}
*/
export {impl as _impl};
/**
* Type: {@link rtvref.types.INT INT}
* @const {string} rtvref.validator.valInt.type
*/
export {type};
/**
* {@link rtvref.validator.validator_config Configuration Function}
* @function rtvref.validator.valInt.config
* @param {rtvref.validator.validator_config_settings} settings Configuration settings.
*/
export const config = function(settings) {
impl = settings.impl;
};
/**
* {@link rtvref.validator.type_validator Validator} for the
* {@link rtvref.types.INT INT} type.
*
* Determines if a value is a number literal __only__ (i.e. a
* {@link rtvref.types.primitives primitive}). It does not validate
* `new Number(1)`, which is an object that is a number.
*
* @function rtvref.validator.valInt.default
* @param {*} v Value to validate.
* @param {string} [q] Validation qualifier. Defaults to
* {@link rtvref.qualifiers.REQUIRED REQUIRED}.
* @param {rtvref.types.numeric_args} [args] Type arguments.
* @returns {(rtvref.RtvSuccess|rtvref.RtvError)} An `RtvSuccess` if valid; `RtvError` if not.
*/
export default function valInt(v, q = REQUIRED, args) {
if (nilPermitted(v, q)) {
return new RtvSuccess();
}
let valid = isInt(v);
if (valid && args) { // then check args against normal type range
if (isInt(args.exact)) {
valid = (v === args.exact);
} else {
let min;
if (valid && isInt(args.min)) {
min = args.min;
valid = (v >= min);
}
if (valid && isInt(args.max)) {
if (min === undefined || args.max >= min) {
valid = (v <= args.max);
} // else, ignore
}
}
}
if (valid) {
return new RtvSuccess();
}
return new RtvError(v, impl.toTypeset(type, q, args), [],
impl.toTypeset(type, q, args, true));
}
......@@ -19,14 +19,14 @@ let impl; // @type {rtvref.impl}
* [Internal] __FOR UNIT TESTING ONLY:__ The {@link rtvref.impl} instance
* configured on this validator.
* @private
* @name rtvref.validator.valObject._impl
* @name rtvref.validator.valMap._impl
* @type {rtvref.impl}
*/
export {impl as _impl};
/**
* Type: {@link rtvref.types.MAP MAP}
* @const {string} rtvref.validator.isMap.type
* @const {string} rtvref.validator.valMap.type
*/
export {type};
......
......@@ -9,6 +9,15 @@ import RtvError from '../RtvError';
const {REQUIRED} = qualifiers;
let impl; // @type {rtvref.impl}
/**
* [Internal] __FOR UNIT TESTING ONLY:__ The {@link rtvref.impl} instance
* configured on this validator.
* @private
* @name rtvref.validator.valNull._impl
* @type {rtvref.impl}
*/
export {impl as _impl};
/**
* Type: {@link rtvref.types.NULL NULL}
* @const {string} rtvref.validator.valNull.type
......
......@@ -15,14 +15,14 @@ let impl; // @type {rtvref.impl}
* [Internal] __FOR UNIT TESTING ONLY:__ The {@link rtvref.impl} instance
* configured on this validator.
* @private
* @name rtvref.validator.valObject._impl
* @name rtvref.validator.valNumber._impl
* @type {rtvref.impl}
*/
export {impl as _impl};
/**
* Type: {@link rtvref.types.NUMBER NUMBER}
* @const {string} rtvref.validator.isNumber.type
* @const {string} rtvref.validator.valNumber.type
*/
export {type};
......@@ -62,8 +62,9 @@ export default function valNumber(v, q = REQUIRED, args) {
valid = true;
}
if (valid && args) { // then check args
if (isNumber(args.exact) || _isNaN(args.exact)) { // NaN OK for this arg (careful: NaN !== NaN...)
if (valid && args) { // then check args against normal type range
// NOTE: NaN is OK for the exact arg (careful: NaN !== NaN...)
if (isNumber(args.exact) || _isNaN(args.exact)) {
valid = (v === args.exact) || (_isNaN(v) && _isNaN(args.exact));
} else {
let min;
......
......@@ -23,7 +23,7 @@ export {impl as _impl};
/**
* Type: {@link rtvref.types.OBJECT OBJECT}
* @const {string} rtvref.validator.isObject.type
* @const {string} rtvref.validator.valObject.type
*/
export {type};
......
......@@ -13,14 +13,14 @@ let impl; // @type {rtvref.impl}
* [Internal] __FOR UNIT TESTING ONLY:__ The {@link rtvref.impl} instance
* configured on this validator.
* @private
* @name rtvref.validator.valObject._impl
* @name rtvref.validator.valRegExp._impl
* @type {rtvref.impl}
*/
export {impl as _impl};
/**
* Type: {@link rtvref.types.REGEXP REGEXP}
* @const {string} rtvref.validator.isRegExp.type
* @const {string} rtvref.validator.valRegExp.type
*/
export {type};
......
////// isSafeInt validator
import {type, default as isSafeInt} from '../validation/isSafeInt';
import {default as qualifiers, nilPermitted} from '../qualifiers';
import RtvSuccess from '../RtvSuccess';
import RtvError from '../RtvError';
const {REQUIRED} = qualifiers;
let impl; // @type {rtvref.impl}
/**
* [Internal] __FOR UNIT TESTING ONLY:__ The {@link rtvref.impl} instance
* configured on this validator.
* @private
* @name rtvref.validator.valSafeInt._impl
* @type {rtvref.impl}
*/
export {impl as _impl};
/**
* Type: {@link rtvref.types.SAFE_INT SAFE_INT}
* @const {string} rtvref.validator.valSafeInt.type
*/
export {type};
/**
* {@link rtvref.validator.validator_config Configuration Function}
* @function rtvref.validator.valSafeInt.config
* @param {rtvref.validator.validator_config_settings} settings Configuration settings.
*/
export const config = function(settings) {
impl = settings.impl;
};
/**
* {@link rtvref.validator.type_validator Validator} for the
* {@link rtvref.types.SAFE_INT SAFE_INT} type.
*
* Determines if a value is a number literal __only__ (i.e. a
* {@link rtvref.types.primitives primitive}). It does not validate
* `new Number(1)`, which is an object that is a number.
*
* @function rtvref.validator.valSafeInt.default
* @param {*} v Value to validate.
* @param {string} [q] Validation qualifier. Defaults to
* {@link rtvref.qualifiers.REQUIRED REQUIRED}.
* @param {rtvref.types.numeric_args} [args] Type arguments.
* @returns {(rtvref.RtvSuccess|rtvref.RtvError)} An `RtvSuccess` if valid; `RtvError` if not.
*/
export default function valSafeInt(v, q = REQUIRED, args) {
if (nilPermitted(v, q)) {
return new RtvSuccess();
}
let valid = isSafeInt(v);
if (valid && args) { // then check args against normal type range
if (isSafeInt(args.exact)) {
valid = (v === args.exact);
} else {
let min;
if (valid && isSafeInt(args.min)) {
min = args.min;
valid = (v >= min);
}
if (valid && isSafeInt(args.max)) {
if (min === undefined || args.max >= min) {
valid = (v <= args.max);
} // else, ignore
}
}
}
if (valid) {
return new RtvSuccess();
}
return new RtvError(v, impl.toTypeset(type, q, args), [],
impl.toTypeset(type, q, args, true));
}
......@@ -17,14 +17,14 @@ let impl; // @type {rtvref.impl}
* [Internal] __FOR UNIT TESTING ONLY:__ The {@link rtvref.impl} instance
* configured on this validator.
* @private
* @name rtvref.validator.valObject._impl
* @name rtvref.validator.valSet._impl
* @type {rtvref.impl}
*/
export {impl as _impl};
/**
* Type: {@link rtvref.types.SET SET}
* @const {string} rtvref.validator.isSet.type
* @const {string} rtvref.validator.valSet.type
*/
export {type};
......
......@@ -15,14 +15,14 @@ let impl; // @type {rtvref.impl}
* [Internal] __FOR UNIT TESTING ONLY:__ The {@link rtvref.impl} instance
* configured on this validator.
* @private
* @name rtvref.validator.valObject._impl
* @name rtvref.validator.valString._impl
* @type {rtvref.impl}
*/
export {impl as _impl};
/**
* Type: {@link rtvref.types.STRING STRING}
* @const {string} rtvref.validator.isString.type
* @const {string} rtvref.validator.valString.type
*/
export {type};
......
......@@ -13,14 +13,14 @@ let impl; // @type {rtvref.impl}
* [Internal] __FOR UNIT TESTING ONLY:__ The {@link rtvref.impl} instance
* configured on this validator.
* @private
* @name rtvref.validator.valObject._impl
* @name rtvref.validator.valSymbol._impl
* @type {rtvref.impl}
*/
export {impl as _impl};
/**
* Type: {@link rtvref.types.SYMBOL SYMBOL}
* @const {string} rtvref.validator.isSymbol.type
* @const {string} rtvref.validator.valSymbol.type
*/
export {type};
......
......@@ -13,14 +13,14 @@ let impl; // @type {rtvref.impl}
* [Internal] __FOR UNIT TESTING ONLY:__ The {@link rtvref.impl} instance
* configured on this validator.
* @private
* @name rtvref.validator.valObject._impl
* @name rtvref.validator.valWeakMap._impl
* @type {rtvref.impl}
*/
export {impl as _impl};
/**
* Type: {@link rtvref.types.WEAK_MAP WEAK_MAP}
* @const {string} rtvref.validator.isWeakMap.type
* @const {string} rtvref.validator.valWeakMap.type
*/
export {type};
......
......@@ -13,14 +13,14 @@ let impl; // @type {rtvref.impl}
* [Internal] __FOR UNIT TESTING ONLY:__ The {@link rtvref.impl} instance
* configured on this validator.
* @private
* @name rtvref.validator.valObject._impl
* @name rtvref.validator.valWeakSet._impl
* @type {rtvref.impl}
*/
export {impl as _impl};
/**
* Type: {@link rtvref.types.WEAK_SET WEAK_SET}
* @const {string} rtvref.validator.isWeakSet.type
* @const {string} rtvref.validator.valWeakSet.type
*/
export {type};
......
////// Validator Module
// DEBUG TODO keep the note at line 14, or change it...?
/**
* <h2>RTV Type Validators</h2>
*
......
......@@ -15,11 +15,13 @@ import * as valArray from './lib/validator/valArray';
import * as valBoolean from './lib/validator/valBoolean';
import * as valFinite from './lib/validator/valFinite';
import * as valFunction from './lib/validator/valFunction';
import * as valInt from './lib/validator/valInt';
import * as valMap from './lib/validator/valMap';
import * as valNull from './lib/validator/valNull';
import * as valNumber from './lib/validator/valNumber';
import * as valObject from './lib/validator/valObject';
import * as valRegExp from './lib/validator/valRegExp';
import * as valSafeInt from './lib/validator/valSafeInt';
import * as valSet from './lib/validator/valSet';
import * as valString from './lib/validator/valString';
import * as valSymbol from './lib/validator/valSymbol';
......@@ -277,11 +279,13 @@ export default rtv;
valBoolean,
valFinite,
valFunction,
valInt,
valNull,
valMap,
valNumber,
valObject,
valRegExp,
valSafeInt,
valSet,
valString,
valSymbol,
......
......@@ -22,7 +22,7 @@ describe('module: lib/impl', function() {
value: impl._validatorMap
});
expect(isObject(impl._validatorMap)).to.be.true;
expect(Object.keys(impl._validatorMap).length).to.equal(16); // # of known types
expect(Object.keys(impl._validatorMap).length).to.equal(18); // # of known types
});
});
......
......@@ -47,6 +47,7 @@ describe('module: lib/types', function() {
types.NUMBER,
types.FINITE,
types.INT,
types.SAFE_INT,
types.FLOAT,
types.ARRAY,
types.ANY_OBJECT,
......
......@@ -16,8 +16,9 @@ describe('module: lib/validation/isAnyObject', function() {
const validTypes = Object.keys(validValues); // @type {Array}
// remove primitives
_.pull(validTypes, types.ANY, types.STRING, types.BOOLEAN, types.NUMBER,
types.FINITE, types.INT, types.FLOAT, types.SYMBOL);
_.pull(validTypes, types.ANY, types.NULL, types.STRING, types.BOOLEAN,
types.NUMBER, types.FINITE, types.INT, types.SAFE_INT, types.FLOAT,
types.SYMBOL);
let values = [];
_.forEach(validTypes, function(type) {
......
......@@ -19,19 +19,22 @@ describe('module: lib/validation/isFinite', function() {
const validValues = vtu.getValidValues(); // @type {Object}
const invalidTypes = Object.keys(validValues); // @type {Array}
// remove subset types (NUMBER is partial subset)
_.pull(invalidTypes, types.NUMBER, types.FINITE, types.INT, types.FLOAT);
// remove subset types
_.pull(invalidTypes, types.NUMBER, types.FINITE, types.INT, types.SAFE_INT, types.FLOAT);
// build a list of all remaining invalid values
let invalidValues = [];
let invalidValues = [
// put some NUMBER values back in which aren't overlaps
NaN,
Infinity,
Number.POSITIVE_INFINITY,
-Infinity,
Number.NEGATIVE_INFINITY
];
_.forEach(invalidTypes, function(type) {
invalidValues = invalidValues.concat(validValues[type]);
});
// add some invalid NUMBER values back in