diff --git a/README.md b/README.md index 3776dd3e3..7b35b1bfe 100644 --- a/README.md +++ b/README.md @@ -332,6 +332,19 @@ You should use `isType` for all Schema type checks. Sets the `strict` option to `true`. Strict schemas skip coercion and transformation attempts, validating the value "as is". +#### `mixed.strip(stripField: boolean = true): Schema` + +Marks a schema to be removed from an output object. Only works as a nested schema. + +```js +let schema = object({ + useThis: number(), + notThis: string().strip() +}) + +schema.cast({ notThis: 'foo', useThis: 4 }) // { useThis: 4 } +``` + #### `mixed.withMutation(builder: (current: Schema) => void): void` First the legally required Rich Hickey quote: @@ -817,6 +830,19 @@ yup.object().shape({ }) ``` +You can also pass a shape to the object constructor as a convenience. + +```js +object().shape({ + num: number() +}) +//or +object({ + num: number() +}) +``` + + #### `object.shape(fields: object, noSortEdges: ?Array<[string, string]>): Schema` Define the keys of the object and the schemas for said keys. diff --git a/lib/date.js b/lib/date.js index 53a8528d3..9e632d894 100644 --- a/lib/date.js +++ b/lib/date.js @@ -4,6 +4,7 @@ var MixedSchema = require('./mixed'); var isoParse = require('./util/isodate'); var locale = require('./locale.js').date; var isAbsent = require('./util/isAbsent'); +var Ref = require('./util/reference'); var _require = require('./util/_'); @@ -37,9 +38,12 @@ inherits(DateSchema, MixedSchema, { return isDate(v) && !isNaN(v.getTime()); }, min: function min(_min, msg) { - var limit = this.cast(_min); + var limit = _min; - if (!this._typeCheck(limit)) throw new TypeError('`min` must be a Date or a value that can be `cast()` to a Date'); + if (!Ref.isRef(limit)) { + limit = this.cast(_min); + if (!this._typeCheck(limit)) throw new TypeError('`min` must be a Date or a value that can be `cast()` to a Date'); + } return this.test({ name: 'min', @@ -52,9 +56,12 @@ inherits(DateSchema, MixedSchema, { }); }, max: function max(_max, msg) { - var limit = this.cast(_max); + var limit = _max; - if (!this._typeCheck(limit)) throw new TypeError('`max` must be a Date or a value that can be `cast()` to a Date'); + if (!Ref.isRef(limit)) { + limit = this.cast(_max); + if (!this._typeCheck(limit)) throw new TypeError('`max` must be a Date or a value that can be `cast()` to a Date'); + } return this.test({ name: 'max', diff --git a/src/locale.js b/src/locale.js index 8e1c2bee8..44a04f699 100644 --- a/src/locale.js +++ b/src/locale.js @@ -18,7 +18,7 @@ module.exports = { url: '${path} must be a valid URL', trim: '${path} must be a trimmed string', lowercase: '${path} must be a lowercase string', - uppercase: '${path} must be a uppercase string' + uppercase: '${path} must be a upper case string' }, number: { diff --git a/src/mixed.js b/src/mixed.js index 8bb2a3077..f88739f83 100644 --- a/src/mixed.js +++ b/src/mixed.js @@ -382,6 +382,12 @@ SchemaType.prototype = { return next }, + strip(strip = true) { + let next = this.clone() + next._strip = strip + return next + }, + _option(key, overrides){ return _.has(overrides, key) ? overrides[key] : this._options[key] diff --git a/src/object.js b/src/object.js index 368d10bb6..4706fd677 100644 --- a/src/object.js +++ b/src/object.js @@ -49,6 +49,10 @@ function ObjectSchema(spec) { } }) + this.fields = Object.create(null) + this._nodes = [] + this._excludedEdges = [] + this.withMutation(() => { this.transform(function coerce(value) { if (typeof value === 'string') { @@ -61,14 +65,10 @@ function ObjectSchema(spec) { return value return null }) - }) - - this.fields = Object.create(null) - this._nodes = [] - this._excludedEdges = [] - if ( spec ) - return this.shape(spec); + if (spec) + this.shape(spec); + }) } inherits(ObjectSchema, MixedSchema, { @@ -106,17 +106,13 @@ inherits(ObjectSchema, MixedSchema, { obj[prop] = refValue } else if (exists && field) { - // ugly optimization avoiding a clone. clears default for recursive - // cast and resets it below; - let hasDflt = has(schema, '_default') - , dflt = schema._default; - - let fieldSchema = childSchema(field, schema.default(undefined)) - - obj[prop] = fieldSchema.cast(value[prop], innerOptions) + tempClearDefault(schema, () => { + let fieldSchema = childSchema(field, schema.default(undefined)) - if (hasDflt) schema.default(dflt) - else delete schema._default + if (fieldSchema._strip !== true) { + obj[prop] = fieldSchema.cast(value[prop], innerOptions) + } + }) } else if (exists && !strip) obj[prop] = value[prop] @@ -253,6 +249,18 @@ function unknown(ctx, value) { .filter(key => known.indexOf(key) === -1) } +// ugly optimization avoiding a clone. clears default for recursive +// cast and resets it below; +function tempClearDefault(schema, fn) { + let hasDflt = has(schema, '_default') + , dflt = schema._default; + + fn(schema) + + if (hasDflt) schema.default(dflt) + else delete schema._default +} + function sortFields(fields, excludes = []){ var edges = [], nodes = [] diff --git a/test/object.js b/test/object.js index f3813e3a7..1f81e5a19 100644 --- a/test/object.js +++ b/test/object.js @@ -229,6 +229,18 @@ describe('Object types', function(){ ]) }) + it('should strip specific fields', function(){ + var inst = object().shape({ + prop: mixed().strip(false), + other: mixed().strip() + }) + + inst.cast({ other: 'boo', prop: 'bar'}) + .should.eql({ + prop: 'bar' + }) + }) + it('should handle custom validation', function(){ var inst = object().shape({ prop: mixed(),