Skip to content

Commit 6c72ed9

Browse files
committed
add functional when
1 parent 8eb37fd commit 6c72ed9

File tree

10 files changed

+200
-7
lines changed

10 files changed

+200
-7
lines changed

jsconfig.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/n4s/src/compounds/isArrayOf.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,15 @@ function isArrayOf(value, ruleChains) {
2323
}
2424

2525
for (let i = 0; i < plainValue.length; i++) {
26-
// Set result per each item in the array|
26+
// Set result per each item in the array
2727
result.setChild(
2828
i,
2929
runCompoundChain(
30-
EnforceContext.wrap(plainValue[i]).setFailFast(value.failFast),
30+
new EnforceContext({
31+
value: plainValue[i],
32+
obj: plainValue,
33+
key: i,
34+
}).setFailFast(value.failFast),
3135
ruleChains,
3236
{ mode: MODE_ANY }
3337
)

packages/n4s/src/meta/__tests__/__snapshots__/ruleMeta.test.js.snap

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,67 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`Rule with "when" Should skip all excluded array items 1`] = `
4+
RuleResult {
5+
"children": Object {
6+
"items": RuleResult {
7+
"children": Object {
8+
"0": RuleResult {
9+
"children": Object {
10+
"checked": RuleResult {
11+
"failed": false,
12+
"hasErrors": false,
13+
"hasWarnings": false,
14+
"warn": false,
15+
},
16+
},
17+
"failed": false,
18+
"hasErrors": false,
19+
"hasWarnings": false,
20+
"when": false,
21+
},
22+
"3": RuleResult {
23+
"children": Object {
24+
"checked": RuleResult {
25+
"failed": false,
26+
"hasErrors": false,
27+
"hasWarnings": false,
28+
"warn": false,
29+
},
30+
},
31+
"failed": false,
32+
"hasErrors": false,
33+
"hasWarnings": false,
34+
"when": false,
35+
},
36+
},
37+
"failed": false,
38+
"hasErrors": false,
39+
"hasWarnings": false,
40+
"isArray": true,
41+
},
42+
},
43+
"failed": false,
44+
"hasErrors": false,
45+
"hasWarnings": false,
46+
}
47+
`;
48+
49+
exports[`Rule with "when" Should skip excluded object items 1`] = `
50+
RuleResult {
51+
"children": Object {
52+
"example1": RuleResult {
53+
"failed": false,
54+
"hasErrors": false,
55+
"hasWarnings": false,
56+
"warn": false,
57+
},
58+
},
59+
"failed": false,
60+
"hasErrors": false,
61+
"hasWarnings": false,
62+
}
63+
`;
64+
365
exports[`Rule with a message Should add message to result 1`] = `
466
RuleResult {
567
"children": Object {

packages/n4s/src/meta/__tests__/ruleMeta.test.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import RuleResult from 'RuleResult';
12
import enforce from 'enforce';
23

34
describe('Rule with a message', () => {
@@ -62,3 +63,91 @@ describe('Rule with a warning', () => {
6263
expect(res).toMatchSnapshot();
6364
});
6465
});
66+
67+
describe('Rule with "when"', () => {
68+
it('Should skip all excluded array items', () => {
69+
const Schema = enforce.shape({
70+
items: enforce.isArrayOf(
71+
enforce.shape({ checked: enforce.isBoolean() }).when(value => {
72+
return value.checked;
73+
})
74+
),
75+
});
76+
77+
const res = Schema.run({
78+
items: [
79+
{ checked: true },
80+
{ checked: false },
81+
{ checked: false },
82+
{ checked: true },
83+
],
84+
});
85+
86+
expect(res.children.items.children[0]).toBeInstanceOf(RuleResult);
87+
expect(res.children.items.children[1]).toBeUndefined();
88+
expect(res.children.items.children[2]).toBeUndefined();
89+
expect(res.children.items.children[3]).toBeInstanceOf(RuleResult);
90+
91+
expect(res).toMatchSnapshot();
92+
});
93+
94+
it('Should skip excluded object items', () => {
95+
const Schema = enforce.shape({
96+
example: enforce.isString().when(() => false),
97+
example1: enforce.isString(),
98+
});
99+
100+
const res = Schema.run({
101+
example: 'something',
102+
example1: 'something',
103+
});
104+
105+
expect(res.children.example).toBeUndefined();
106+
expect(res.children.example1).toBeInstanceOf(RuleResult);
107+
108+
expect(res).toMatchSnapshot();
109+
});
110+
it('Should pass each item the value, key and parent', () => {
111+
const whenItems = jest.fn();
112+
const whenName = jest.fn();
113+
const Schema = enforce.shape({
114+
name: enforce
115+
.shape({
116+
first: enforce.isString(),
117+
})
118+
.when(whenName),
119+
items: enforce.isArrayOf(
120+
enforce.shape({ checked: enforce.isBoolean() }).when(whenItems)
121+
),
122+
});
123+
124+
const data = {
125+
name: {
126+
first: 'example',
127+
},
128+
items: [
129+
{ checked: true },
130+
{ checked: false },
131+
{ checked: false },
132+
{ checked: true },
133+
],
134+
};
135+
136+
const res = Schema.run(data);
137+
138+
expect(whenName).toHaveBeenCalledWith(data.name, 'name', data);
139+
140+
expect(whenItems.mock.calls[0][0]).toBe(data.items[0]);
141+
expect(whenItems.mock.calls[1][0]).toBe(data.items[1]);
142+
expect(whenItems.mock.calls[2][0]).toBe(data.items[2]);
143+
expect(whenItems.mock.calls[3][0]).toBe(data.items[3]);
144+
expect(whenItems.mock.calls[0][1]).toBe(0); // key
145+
expect(whenItems.mock.calls[1][1]).toBe(1); // key
146+
expect(whenItems.mock.calls[2][1]).toBe(2); // key
147+
expect(whenItems.mock.calls[3][1]).toBe(3); // key
148+
expect(whenItems.mock.calls[0][2]).toBe(data.items); // parent
149+
expect(whenItems.mock.calls[1][2]).toBe(data.items); // parent
150+
expect(whenItems.mock.calls[2][2]).toBe(data.items); // parent
151+
expect(whenItems.mock.calls[3][2]).toBe(data.items); // parent
152+
});
153+
});

packages/n4s/src/meta/ruleMeta.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
12
import message from 'ruleMessage';
23
import warn from 'ruleWarn';
4+
import when from 'ruleWhen';
35

4-
export default { warn, message };
6+
export default { warn, message, when };

packages/n4s/src/meta/ruleWhen.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import EnforceContext from 'EnforceContext';
2+
import optionalFunctionValue from 'optionalFunctionValue';
3+
4+
export default function when(value, condition, bail) {
5+
const shouldBail = !optionalFunctionValue(
6+
condition,
7+
[EnforceContext.unwrap(value)].concat(
8+
EnforceContext.is(value) ? [value.key, value.obj] : []
9+
)
10+
);
11+
12+
return bail(shouldBail);
13+
}

packages/n4s/src/runtime/RuleResult.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import hasOwnProperty from 'hasOwnProperty';
22

33
import { isBoolean } from 'isBoolean';
44
import { isEmpty } from 'isEmpty';
5+
import { isNull } from 'isNull';
56
import { isUndefined } from 'isUndefined';
67
import { HAS_WARNINGS, HAS_ERRORS } from 'sharedKeys';
78

@@ -65,6 +66,10 @@ RuleResult.prototype.setFailed = function (failed) {
6566
* @param {RuleResult} child
6667
*/
6768
RuleResult.prototype.setChild = function (key, child) {
69+
if (isNull(child)) {
70+
return null;
71+
}
72+
6873
const isWarning =
6974
this[HAS_WARNINGS] || child[HAS_WARNINGS] || child.warn || this.warn;
7075
this.setAttribute(HAS_WARNINGS, (isWarning && child.failed) || false);
@@ -96,6 +101,10 @@ RuleResult.prototype.getChild = function (key) {
96101
* @param {Boolean|RuleResult} newRes
97102
*/
98103
RuleResult.prototype.extend = function (newRes) {
104+
if (isNull(newRes)) {
105+
return this;
106+
}
107+
99108
const res = RuleResult.is(newRes)
100109
? newRes
101110
: new RuleResult().setAttribute('warn', !!this.warn).setFailed(!newRes);

packages/n4s/src/runtime/bindLazyRule.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ export default function bindLazyRule(ruleName) {
3030

3131
// Add a meta function
3232
if (ruleMeta[rule.name] === rule) {
33-
meta.push((value, ruleResult) => {
34-
ruleResult.setAttribute(rule.name, rule(value, args[0]));
33+
meta.push((value, ruleResult, bail) => {
34+
ruleResult.setAttribute(rule.name, rule(value, args[0], bail));
3535
});
3636
} else {
3737
// Register a rule
@@ -57,11 +57,17 @@ export default function bindLazyRule(ruleName) {
5757
returnvalue[RUN_RULE] = value => {
5858
const result = new RuleResult(true);
5959

60+
let bailed = false;
61+
6062
// Run meta chains
6163
meta.forEach(fn => {
62-
fn(value, result);
64+
fn(value, result, shouldBail => (bailed = shouldBail));
6365
});
6466

67+
if (bailed) {
68+
return null;
69+
}
70+
6571
// Iterate over all the registered rules
6672
// This runs the function that's inside `addFn`
6773
for (const fn of registeredRules) {

packages/n4s/src/runtime/runCompoundChain.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import RuleResult from 'RuleResult';
22
import { MODE_ALL, MODE_ONE, MODE_ANY } from 'enforceKeywords';
33
import { isEmpty } from 'isEmpty';
4+
import { isNull } from 'isNull';
45
import { runLazyRule } from 'runLazyRules';
56

67
/**
@@ -24,6 +25,10 @@ export default function runCompoundChain(value, rules, options) {
2425
// Inner result for each iteration
2526
const currentResult = runLazyRule(chain, value);
2627

28+
if (isNull(currentResult)) {
29+
return null;
30+
}
31+
2732
const pass = currentResult.pass;
2833

2934
if (pass) {

packages/vest/src/__tests__/test_types/fixtures/rules.ts

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)