1 | ;
|
2 |
|
3 | var isObject = require('./object'),
|
4 | handleCurry = require('./utils/handleCurry'),
|
5 | objectOf = require('./objectOf'),
|
6 | isFunction = require('./function');
|
7 |
|
8 | var 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 | */
|
83 | module.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 | };
|