Skip to content

Commit 75739b8

Browse files
committed
[added] context sensitive reach()
1 parent afe783f commit 75739b8

File tree

5 files changed

+83
-14
lines changed

5 files changed

+83
-14
lines changed

README.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,13 @@ yup.addMethod
139139
yup.ValidationError
140140
```
141141

142-
#### `.reach(Schema schema, String path, Object options)`
142+
#### `.reach(Schema schema, String path, [Object value, Object context])`
143143

144144
For nested schema's `yup.reach` will retrieve a nested schema based on the provided path.
145145

146+
For nested schema that need to resolve dynamically, you can provide a `value` and optionally
147+
a `context` object.
148+
146149
```js
147150
var schema = object().shape({
148151
nested: object()

src/array.js

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ function ArraySchema(){
2020

2121
MixedSchema.call(this, { type: 'array'})
2222

23+
this._subType = null;
24+
2325
this.withMutation(() => {
2426
this.transform(function(values) {
2527
if (typeof values === 'string')

src/util/reach.js

+31-12
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,38 @@
11
'use strict';
2-
let { forEach } = require('property-expr');
2+
let { forEach } = require('property-expr')
3+
, { has } = require('./_');
34

45
let trim = part => part.substr(0, part.length - 1).substr(1)
56

6-
module.exports = function (obj, path) {
7-
forEach(path, (part, isBracket, isArray) => {
8-
if( isArray)
9-
obj = obj._subType
10-
else {
11-
if (obj._subType) // we skipped an array
12-
obj = obj._subType
13-
14-
obj = obj.fields[isBracket ? trim(part) : part]
15-
}
7+
module.exports = function (obj, path, value, context) {
8+
// if only one "value" arg then use it for both
9+
context = context || value;
10+
11+
let parent, lastPart;
12+
13+
forEach(path, (_part, isBracket, isArray) => {
14+
let part = isBracket ? trim(_part) : _part;
15+
16+
if (isArray || has(obj, '_subType')) { // we skipped an array
17+
obj = obj._resolve(context, parent)._subType;
18+
value = value && value[0]
19+
}
20+
21+
if (!isArray) {
22+
obj = obj._resolve(context, parent);
23+
24+
if (!has(obj, 'fields'))
25+
throw new Error(
26+
`The schema does not contain the path: ${path}. ` +
27+
`(failed at: ${lastPart} which is a type: "${obj._type}") `
28+
)
29+
30+
obj = obj.fields[part]
31+
parent = value;
32+
value = value && value[part]
33+
lastPart = isBracket ? '[' + _part + ']' : '.' + _part
34+
}
1635
})
1736

18-
return obj
37+
return obj._resolve(parent)
1938
}

src/util/reference.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export default class Ref {
1818
this.prefix = prefix;
1919
this.isContext = key.indexOf(prefix) === 0
2020
this.path = this.isContext ? this.key.slice(this.prefix.length) : this.key
21+
this._get = getter(this.path)
2122
this.map = mapFn || (value => value);
2223
}
2324

@@ -27,7 +28,7 @@ export default class Ref {
2728
if ((isContext && !context) || (!isContext && !context && !parent))
2829
throw new Error('missing the context necessary to cast this value')
2930

30-
let value = getter(this.path)(isContext ? context : (parent || context))
31+
let value = this._get(isContext ? context : (parent || context))
3132

3233
return this.map(value)
3334
}

test/yup.js

+44
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,50 @@ describe('Yup', function(){
8585
})
8686
})
8787

88+
it('should REACH conditionally correctly', function(){
89+
var num = number()
90+
, inst = object().shape({
91+
num: number().max(4),
92+
nested: object()
93+
.shape({
94+
arr: array().when('$bar', function(bar) {
95+
return bar !== 3
96+
? array().of(number())
97+
: array().of(
98+
object().shape({
99+
foo: number(),
100+
num: number().when('foo', (foo) => {
101+
if (foo === 5)
102+
return num
103+
})
104+
})
105+
)
106+
})
107+
})
108+
})
109+
110+
let context = { bar: 3 }
111+
let value = {
112+
bar: 3,
113+
nested: {
114+
arr: [{ foo: 5 }]
115+
}
116+
}
117+
118+
reach(inst, 'nested.arr.num', value).should.equal(num)
119+
reach(inst, 'nested.arr[].num', value).should.equal(num)
120+
121+
reach(inst, 'nested.arr.num', value, context).should.equal(num)
122+
reach(inst, 'nested.arr[].num', value, context).should.equal(num)
123+
reach(inst, 'nested.arr[1].num', value, context).should.equal(num)
124+
reach(inst, 'nested["arr"][1].num', value, context).should.not.equal(number())
125+
126+
return reach(inst, 'nested.arr[].num', value, context).isValid(5)
127+
.then((valid) => {
128+
valid.should.equal(true)
129+
})
130+
})
131+
88132
describe('BadSet', function(){
89133
it('should preserve primitive types', function(){
90134
var set = new BadSet()

0 commit comments

Comments
 (0)