Skip to content

Commit

Permalink
Add Joi.bind(). Closes #1631.
Browse files Browse the repository at this point in the history
  • Loading branch information
Marsup committed Nov 25, 2018
1 parent 80a60a0 commit ae6031d
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 4 deletions.
12 changes: 12 additions & 0 deletions API.md
Expand Up @@ -465,6 +465,18 @@ const defaultJoi = Joi.defaults((schema) => {
const schema = defaultJoi.object(); // Equivalent to a Joi.object().min(1)
```

### `bind()`

By default, some Joi methods to function properly need to rely on the Joi instance they are attached to because they use `this` internally. So `Joi.string()` works but if you extract the function from it and call `string()` it won't. `bind()` creates a new Joi instance where all the functions relying on `this` are bound to the Joi instance.

```js
const { object, string } = require('joi').bind();

const schema = object({
property: string().min(4)
});
```

### `extend(extension)`

Creates a new Joi instance customized with the extension(s) you provide included.
Expand Down
25 changes: 21 additions & 4 deletions lib/index.js
Expand Up @@ -46,6 +46,7 @@ internals.root = function () {
const root = any.clone();
Any.prototype._currentJoi = root;
root._currentJoi = root;
root._binds = new Set(['any', 'alternatives', 'alt', 'array', 'boolean', 'binary', 'date', 'func', 'number', 'object', 'string', 'symbol', 'validate', 'describe', 'compile', 'assert', 'attempt', 'lazy', 'defaults', 'extend']);

root.any = function (...args) {

Expand Down Expand Up @@ -141,14 +142,14 @@ internals.root = function () {
}

const options = count === 2 ? args[1] : undefined;
const schema = root.compile(args[0]);
const schema = this.compile(args[0]);

return schema._validateWithOptions(value, options, callback);
};

root.describe = function (...args) {

const schema = args.length ? root.compile(args[0]) : any;
const schema = args.length ? this.compile(args[0]) : any;
return schema.describe();
};

Expand All @@ -168,12 +169,12 @@ internals.root = function () {

root.assert = function (value, schema, message) {

root.attempt(value, schema, message);
this.attempt(value, schema, message);
};

root.attempt = function (value, schema, message) {

const result = root.validate(value, schema);
const result = this.validate(value, schema);
const error = result.error;
if (error) {
if (!message) {
Expand Down Expand Up @@ -259,6 +260,18 @@ internals.root = function () {
return joi;
};

root.bind = function () {

const joi = Object.create(this);

joi._binds.forEach((bind) => {

joi[bind] = joi[bind].bind(joi);
});

return joi;
};

root.extend = function (...args) {

const extensions = Hoek.flatten(args);
Expand All @@ -268,6 +281,8 @@ internals.root = function () {

const joi = Object.create(this.any());
Object.assign(joi, this);
joi._currentJoi = joi;
joi._binds = new Set(joi._binds);

for (let i = 0; i < extensions.length; ++i) {
let extension = extensions[i];
Expand Down Expand Up @@ -416,6 +431,8 @@ internals.root = function () {

return internals.callWithDefaults.call(this, instance, extArgs);
};

joi._binds.add(extension.name);
}

return joi;
Expand Down
41 changes: 41 additions & 0 deletions test/index.js
Expand Up @@ -4373,4 +4373,45 @@ describe('Joi', () => {
});

});

describe('bind()', () => {

it('binds functions', () => {

expect(() => {

const string = Joi.string;
string();
}).to.throw('Must be invoked on a Joi instance.');

const { string } = Joi.bind();
expect(() => string()).to.not.throw();

const { error } = string().validate(0);
expect(error).to.be.an.error('"value" must be a string');
});

it('binds functions on an extended joi', () => {

const customJoi = Joi.extend({
base: Joi.string(),
name: 'myType'
});

expect(() => {

const string = customJoi.string;
string();
}).to.throw('Must be invoked on a Joi instance.');

const { string, myType } = customJoi.bind();
expect(() => string()).to.not.throw();
expect(string().validate(0).error).to.be.an.error('"value" must be a string');

expect(() => myType()).to.not.throw();
expect(myType().validate(0).error).to.be.an.error('"value" must be a string');

expect(customJoi._binds.size).to.equal(Joi._binds.size + 1);
});
});
});

0 comments on commit ae6031d

Please sign in to comment.