Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

deprecate array.includes and array.excludes, use array.items and allow required and forbidden within #559

Merged
merged 1 commit into from Feb 25, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 8 additions & 13 deletions README.md
Expand Up @@ -43,8 +43,7 @@ Lead Maintainer: [Nicolas Morel](https://github.com/marsup)
- [`array`](#array)
- [`array.sparse(enabled)`](#arraysparseenabled)
- [`array.single(enabled)`](#arraysingleenabled)
- [`array.includes(type)`](#arrayincludestype)
- [`array.excludes(type)`](#arrayexcludestype)
- [`array.items(type)`](#arrayitemstype)
- [`array.min(limit)`](#arrayminlimit)
- [`array.max(limit)`](#arraymaxlimit)
- [`array.length(limit)`](#arraylengthlimit)
Expand Down Expand Up @@ -540,22 +539,18 @@ schema.validate([4]); // returns `{ error: null, value: [ 4 ] }`
schema.validate(4); // returns `{ error: null, value: [ 4 ] }`
```

#### `array.includes(type)`
#### `array.items(type)`

List the types allowed for the array values where:
- `type` - a **joi** schema object to validate each array item against. `type` can be an array of values, or multiple values can be passed as individual arguments.

```javascript
var schema = Joi.array().includes(Joi.string(), Joi.number());
```

#### `array.excludes(type)`

List the types forbidden for the array values where:
- `type` - a **joi** schema object to validate each array item against. `type` can be an array of values, or multiple values can be passed as individual arguments.
If a given type is `.required()` then there must be a matching item in the array. If a type is `.forbidden()` then it cannot appear in the array. Required items
can be added multiple times to signify that multiple items must be found.

```javascript
var schema = Joi.array().excludes(Joi.object());
var schema = Joi.array().items(Joi.string(), Joi.number()); // array may contain strings and numbers
var schema = Joi.array().items(Joi.string().required(), Joi.string().required()); // array must contain at least two strings
var schema = Joi.array().items(Joi.string().valid('not allowed').forbidden(), Joi.string()); // array may contain strings, but none of those strings can match 'not allowed'
```

#### `array.min(limit)`
Expand Down Expand Up @@ -1289,4 +1284,4 @@ var schema = Joi.object().keys({
});

Joi.validate({ a: 5, b: { c: 5 } }, schema, { context: { x: 5 } }, function (err, value) {});
```
```
2 changes: 1 addition & 1 deletion generate-readme-toc.js
Expand Up @@ -14,4 +14,4 @@ var tocOptions = {
}
};

Fs.writeFileSync(filename, Toc.insert(readme, tocOptions));
Fs.writeFileSync(filename, Toc.insert(readme, tocOptions));
98 changes: 69 additions & 29 deletions lib/array.js
Expand Up @@ -15,8 +15,10 @@ internals.Array = function () {

Any.call(this);
this._type = 'array';
this._inner.items = [];
this._inner.inclusions = [];
this._inner.exclusions = [];
this._inner.requireds = [];
this._flags.sparse = false;
};

Expand Down Expand Up @@ -88,6 +90,9 @@ internals.checkItems = function (items, wasArray, state, options) {
var errors = [];
var errored;

var requireds = this._inner.requireds.slice();
var inclusions = this._inner.inclusions.slice();

for (var v = 0, vl = items.length; v < vl; ++v) {
errored = false;
var item = items[v];
Expand Down Expand Up @@ -126,10 +131,29 @@ internals.checkItems = function (items, wasArray, state, options) {
continue;
}

// Requireds

for (i = 0, il = requireds.length; i < il; ++i) {
var res = requireds[i]._validate(item, localState, {});
if (!res.errors) {
items[v] = res.value;
isValid = true;
inclusions.push(requireds[i]);
requireds.splice(i, 1);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should probably decrease i and il after splicing, you're going to be out of bounds

--i;
--il;
break;
}
}

if (isValid) {
continue;
}

// Inclusions

for (i = 0, il = this._inner.inclusions.length; i < il; ++i) {
var res = this._inner.inclusions[i]._validate(item, localState, options);
for (i = 0, il = inclusions.length; i < il; ++i) {
var res = inclusions[i]._validate(item, localState, options);
if (!res.errors) {
items[v] = res.value;
isValid = true;
Expand Down Expand Up @@ -163,6 +187,32 @@ internals.checkItems = function (items, wasArray, state, options) {
}
}

if (requireds.length) {
var knownMisses = [];
var unknownMisses = 0;
for (var i = 0, il = requireds.length; i < il; ++i) {
var label = Hoek.reach(requireds[i], '_settings.language.label');
if (label) {
knownMisses.push(label);
}
else {
++unknownMisses;
}
}

if (knownMisses.length) {
if (unknownMisses) {
errors.push(Errors.create('array.includesRequiredBoth', { knownMisses: knownMisses, unknownMisses: unknownMisses }, { key: state.key, path: state.patk }, options));
}
else {
errors.push(Errors.create('array.includesRequiredKnowns', { knownMisses: knownMisses }, { key: state.key, path: state.path }, options));
}
}
else {
errors.push(Errors.create('array.includesRequiredUnknowns', { unknownMisses: unknownMisses }, { key: state.key, path: state.path }, options));
}
}

return errors.length ? errors : null;
};

Expand All @@ -171,48 +221,38 @@ internals.Array.prototype.describe = function () {

var description = Any.prototype.describe.call(this);

if (this._inner.inclusions.length) {
description.includes = [];
if (this._inner.items.length) {
description.items = [];

for (var i = 0, il = this._inner.inclusions.length; i < il; ++i) {
description.includes.push(this._inner.inclusions[i].describe());
}
}

if (this._inner.exclusions.length) {
description.excludes = [];

for (var i = 0, il = this._inner.exclusions.length; i < il; ++i) {
description.excludes.push(this._inner.exclusions[i].describe());
for (var i = 0, il = this._inner.items.length; i < il; ++i) {
description.items.push(this._inner.items[i].describe());
}
}

return description;
};


internals.Array.prototype.includes = function () {

var inclusions = Hoek.flatten(Array.prototype.slice.call(arguments)).map(function (type) {

return Cast.schema(type);
});
internals.Array.prototype.items = function () {

var obj = this.clone();
obj._inner.inclusions = obj._inner.inclusions.concat(inclusions);
return obj;
};


internals.Array.prototype.excludes = function () {
Hoek.flatten(Array.prototype.slice.call(arguments)).forEach(function (type) {

var exclusions = Hoek.flatten(Array.prototype.slice.call(arguments)).map(function (type) {
type = Cast.schema(type);
obj._inner.items.push(type);

return Cast.schema(type);
if (type._flags.presence === 'required') {
obj._inner.requireds.push(type);
}
else if (type._flags.presence === 'forbidden') {
obj._inner.exclusions.push(type.optional());
}
else {
obj._inner.inclusions.push(type);
}
});

var obj = this.clone();
obj._inner.exclusions = obj._inner.exclusions.concat(exclusions);
return obj;
};

Expand Down
3 changes: 3 additions & 0 deletions lib/language.js
Expand Up @@ -29,6 +29,9 @@ exports.errors = {
includesSingle: 'single value of "{{!key}}" does not match any of the allowed types',
includesOne: 'at position {{pos}} fails because {{reason}}',
includesOneSingle: 'single value of "{{!key}}" fails because {{reason}}',
includesRequiredUnknowns: 'does not contain {{unknownMisses}} required value(s)',
includesRequiredKnowns: 'does not contain {{knownMisses}}',
includesRequiredBoth: 'does not contain {{knownMisses}} and {{unknownMisses}} other required value(s)',
excludes: 'at position {{pos}} contains an excluded value',
excludesSingle: 'single value of "{{!key}}" contains an excluded value',
min: 'must contain at least {{limit}} items',
Expand Down
2 changes: 1 addition & 1 deletion test/any.js
Expand Up @@ -61,7 +61,7 @@ describe('any', function () {
it('validates without converting', function (done) {

var schema = Joi.object({
array: Joi.array().includes(Joi.string().min(5), Joi.number().min(3))
array: Joi.array().items(Joi.string().min(5), Joi.number().min(3))
}).strict();

Helper.validate(schema, [
Expand Down