Skip to content

Commit

Permalink
[added] context sensitive reach()
Browse files Browse the repository at this point in the history
  • Loading branch information
jquense committed Apr 5, 2016
1 parent afe783f commit 75739b8
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 14 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,13 @@ yup.addMethod
yup.ValidationError
```

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

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

For nested schema that need to resolve dynamically, you can provide a `value` and optionally
a `context` object.

```js
var schema = object().shape({
nested: object()
Expand Down
2 changes: 2 additions & 0 deletions src/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ function ArraySchema(){

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

this._subType = null;

this.withMutation(() => {
this.transform(function(values) {
if (typeof values === 'string')
Expand Down
43 changes: 31 additions & 12 deletions src/util/reach.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,38 @@
'use strict';
let { forEach } = require('property-expr');
let { forEach } = require('property-expr')
, { has } = require('./_');

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

module.exports = function (obj, path) {
forEach(path, (part, isBracket, isArray) => {
if( isArray)
obj = obj._subType
else {
if (obj._subType) // we skipped an array
obj = obj._subType

obj = obj.fields[isBracket ? trim(part) : part]
}
module.exports = function (obj, path, value, context) {
// if only one "value" arg then use it for both
context = context || value;

let parent, lastPart;

forEach(path, (_part, isBracket, isArray) => {
let part = isBracket ? trim(_part) : _part;

if (isArray || has(obj, '_subType')) { // we skipped an array
obj = obj._resolve(context, parent)._subType;
value = value && value[0]
}

if (!isArray) {
obj = obj._resolve(context, parent);

if (!has(obj, 'fields'))
throw new Error(
`The schema does not contain the path: ${path}. ` +
`(failed at: ${lastPart} which is a type: "${obj._type}") `
)

obj = obj.fields[part]
parent = value;
value = value && value[part]
lastPart = isBracket ? '[' + _part + ']' : '.' + _part
}
})

return obj
return obj._resolve(parent)
}
3 changes: 2 additions & 1 deletion src/util/reference.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default class Ref {
this.prefix = prefix;
this.isContext = key.indexOf(prefix) === 0
this.path = this.isContext ? this.key.slice(this.prefix.length) : this.key
this._get = getter(this.path)
this.map = mapFn || (value => value);
}

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

let value = getter(this.path)(isContext ? context : (parent || context))
let value = this._get(isContext ? context : (parent || context))

return this.map(value)
}
Expand Down
44 changes: 44 additions & 0 deletions test/yup.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,50 @@ describe('Yup', function(){
})
})

it('should REACH conditionally correctly', function(){
var num = number()
, inst = object().shape({
num: number().max(4),
nested: object()
.shape({
arr: array().when('$bar', function(bar) {
return bar !== 3
? array().of(number())
: array().of(
object().shape({
foo: number(),
num: number().when('foo', (foo) => {
if (foo === 5)
return num
})
})
)
})
})
})

let context = { bar: 3 }
let value = {
bar: 3,
nested: {
arr: [{ foo: 5 }]
}
}

reach(inst, 'nested.arr.num', value).should.equal(num)
reach(inst, 'nested.arr[].num', value).should.equal(num)

reach(inst, 'nested.arr.num', value, context).should.equal(num)
reach(inst, 'nested.arr[].num', value, context).should.equal(num)
reach(inst, 'nested.arr[1].num', value, context).should.equal(num)
reach(inst, 'nested["arr"][1].num', value, context).should.not.equal(number())

return reach(inst, 'nested.arr[].num', value, context).isValid(5)
.then((valid) => {
valid.should.equal(true)
})
})

describe('BadSet', function(){
it('should preserve primitive types', function(){
var set = new BadSet()
Expand Down

0 comments on commit 75739b8

Please sign in to comment.