UNPKG

3.2 kBJavaScriptView Raw
1'use strict';
2
3var isObject = require('./object'),
4 handleCurry = require('./utils/handleCurry'),
5 objectOf = require('./objectOf'),
6 isFunction = require('./function');
7
8var isObjectOfPredicates = objectOf(isFunction);
9
10/**
11 * Checks whether an object satisfies predicates defined in structure
12 *
13 * NOTE: All predicates defined in structure must be satisfied.
14 * If some of the properties are optional use [undefinedOr](#undefinedOr)
15 *
16 * You shouldn't use this function to validate input from the user and expect complex report "what's wrong with this is object".
17 * There are few reasons for that:
18 * * it's just a predicate (that returns only true or false)
19 * * breaks [the design rule](design.md#user-content-defined-and-generated-predicates-will-not-throw-any-errors)
20 *
21 * See examples for inspiration how you can use _structure_
22 *
23 * @function structure
24 *
25 * @example
26 * // simple object matching
27 * var is = require('predicates');
28 *
29 * var schema = {
30 * name: is.string, // only string
31 * phone: is.or(is.string, is.number), // string or number
32 * surname: is.undefinedOr(is.string) // optional
33 * },
34 * isPerson = is.structure(schema);
35 *
36 * var person = {name: 'Tommy', phone: 80129292};
37 * isPerson(person); // true
38 * // same as
39 * is.structure(schema, person); // true
40 * isPerson({name: 'Tommy'});
41 *
42 * @example
43 * // filtering
44 * var is = require('predicates');
45 *
46 * var people = [
47 * {name: 'Prof. Bend Ovah', age: 55, sex: 'male'},
48 * {name: 'Dr. Supa Kaki', age: 34, sex: 'female'},
49 * {name: 'Prof. Anti Santy', age: 46, sex: 'male'}
50 * ];
51 *
52 * var professors = people.filter(is.structure({
53 * name: is.startsWith('Prof.')
54 * }));
55 *
56 * // [
57 * // {name: 'Prof. Bend Ovah', age: 55, sex: 'male'},
58 * // {name: 'Prof. Anti Santy', age: 46, sex: 'male'}
59 * // ]
60 *
61 * @example
62 * // duck typing
63 *
64 * var isDuck = is.structure({
65 * quack: is.function,
66 * walk: is.function
67 * });
68 *
69 * isDuck({
70 * say: function() { return 'woof! woof!';
71 * }}); // not a duck
72 *
73 * isDuck({
74 * quack: function() { return 'quack!'; },
75 * walk: function() { return 'tup tup tup'; }
76 * }); // yep, it's a duck
77 *
78 * @param {Object} structure
79 * @param {Object} [value]
80 * @param {...*} [additionalArgs] additional arguments passed to the predicates
81 * @return {(Boolean|Predicate)} returns bool if more than 1 argument provided, otherwise a predicate
82 */
83module.exports = function isStructure(structure) {
84 if (!isObject(structure)) {
85 throw new TypeError('Structure must be an object');
86 }
87
88 var keys = Object.keys(structure);
89 if (keys.length === 0) {
90 throw new Error('Structure object cannot be empty. No enumerable properties found');
91 }
92
93 if (!isObjectOfPredicates(structure)) {
94 throw new TypeError('Structure object must consist of predicates');
95 }
96
97 return handleCurry.call(this, arguments, function isStructurePredicate(value) {
98 var args = Array.prototype.slice.call(arguments, 1);
99 return isObject(value) && keys.every(function structurePredicateTestingProperty(key) {
100 return structure[key].apply(this, [value[key]].concat(args));
101 }, this);
102 });
103};