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
Add support for truthy/falsy boolean values #998
Changes from 1 commit
da5de23
e7ee3e5
2eaeb4f
fc457b7
566cdc1
4c81267
5649c87
752cf85
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,6 +52,8 @@ | |
- [`array.length(limit)`](#arraylengthlimit) | ||
- [`array.unique([comparator])`](#arrayuniquecomparator) | ||
- [`boolean`](#boolean) | ||
- [`boolean.truthy(value)`](#booleantruthyvalue) | ||
- [`boolean.falsy(value)`](#booleanfalsyvalue) | ||
- [`binary`](#binary) | ||
- [`binary.encoding(encoding)`](#binaryencodingencoding) | ||
- [`binary.min(limit)`](#binaryminlimit) | ||
|
@@ -808,6 +810,24 @@ const boolean = Joi.boolean(); | |
boolean.validate(true, (err, value) => { }); | ||
``` | ||
|
||
#### `boolean.truthy(value)` | ||
|
||
Allows for additional strings to be considered valid boolean values in addition to default 'truthy' strings: ['true', 'yes', '1', 'on']. Accepts a string or an array of strings. | ||
|
||
```js | ||
const boolean = Joi.boolean().truthy('Y'); | ||
boolean.validate('Y', (err, value) => { }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure thing. On it, thanks. |
||
``` | ||
|
||
#### `boolean.falsy(value)` | ||
|
||
Allows for additional strings to be considered valid boolean values in addition to default 'falsy' strings: ['false', 'no', '0', 'off']. Accepts a string or an array of strings. | ||
|
||
```js | ||
const boolean = Joi.boolean().falsy('N'); | ||
boolean.validate('N', (err, value) => { }); | ||
``` | ||
|
||
### `binary` | ||
|
||
Generates a schema object that matches a Buffer data type (as well as the strings which will be converted to Buffers). | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
// Load modules | ||
|
||
const Any = require('./any'); | ||
const Hoek = require('hoek'); | ||
|
||
|
||
// Declare internals | ||
|
@@ -15,6 +16,18 @@ internals.Boolean = class extends Any { | |
|
||
super(); | ||
this._type = 'boolean'; | ||
this._inner._truthySet = [ | ||
'true', | ||
'yes', | ||
'on', | ||
'1' | ||
]; | ||
this._inner._falsySet = [ | ||
'false', | ||
'no', | ||
'off', | ||
'0' | ||
]; | ||
} | ||
|
||
_base(value, state, options) { | ||
|
@@ -27,8 +40,8 @@ internals.Boolean = class extends Any { | |
options.convert) { | ||
|
||
const lower = value.toLowerCase(); | ||
result.value = (lower === 'true' || lower === 'yes' || lower === 'on' || lower === '1' ? true | ||
: (lower === 'false' || lower === 'no' || lower === 'off' || lower === '0' ? false : value)); | ||
result.value = (this._inner._truthySet.indexOf(lower) !== -1 ? true | ||
: (this._inner._falsySet.indexOf(lower) !== -1 ? false : value)); | ||
} | ||
|
||
if (typeof value === 'number' && | ||
|
@@ -41,6 +54,50 @@ internals.Boolean = class extends Any { | |
result.errors = (typeof result.value === 'boolean') ? null : this.createError('boolean.base', null, state, options); | ||
return result; | ||
} | ||
|
||
truthy(truthyValue) { | ||
|
||
let truthySet = []; | ||
|
||
if (Array.isArray(truthyValue)) { | ||
truthySet = truthyValue.map((value) => { | ||
|
||
Hoek.assert(typeof value === 'string', 'truthy value must be a string'); | ||
|
||
return value.toLowerCase(); | ||
}); | ||
} | ||
else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not just cast the value to an array if it isn't one currently? That way you don't have the code repeated. So before line 62 something like: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good call. I'll do that along with the API.md update above. Thanks There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is actually inconsistent with how we do it everywhere else, see https://github.com/hapijs/joi/blob/master/lib/any.js#L235. |
||
Hoek.assert(typeof truthyValue === 'string', 'truthy value must be a string'); | ||
truthySet.push(truthyValue.toLowerCase()); | ||
} | ||
|
||
const obj = this.clone(); | ||
obj._inner._truthySet = truthySet.concat(obj._truthySet); | ||
return obj; | ||
} | ||
|
||
falsy(falsyValue) { | ||
|
||
let falsySet = []; | ||
|
||
if (Array.isArray(falsyValue)) { | ||
falsySet = falsyValue.map((value) => { | ||
|
||
Hoek.assert(typeof value === 'string', 'falsy value must be a string'); | ||
|
||
return value.toLowerCase(); | ||
}); | ||
} | ||
else { | ||
Hoek.assert(typeof falsyValue === 'string', 'falsy value must be a string'); | ||
falsySet.push(falsyValue.toLowerCase()); | ||
} | ||
|
||
const obj = this.clone(); | ||
obj._inner._falsySet = falsySet.concat(obj._falsySet); | ||
return obj; | ||
} | ||
}; | ||
|
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I don't think this should be
additional
as I want to be able to be explicit about it. Instead I think this should override the validtruthy
values instead of just add new ones.The same goes for the
falsey
one below.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I get that. It is currently (in this PR) implemented as additive, so that I didn't nuke the strings that are already type-coerced by the "vanilla" boolean schema type. Here's where the "additive" piece is in the code: https://github.com/hapijs/joi/pull/998/files#diff-3b86748616af947ebd7969eedec0062aR76
I'm totally down for overriding the default type-coerced strings. I was just trying to play nice with what was already in place. Just let me know what would be preferred and I will update accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I figured that was the case @WesTyler. It's definitely just my personal preference here not sure that one method is more correct than the other.
Not sure of the opinion that @Marsup has on this, but I definitely defer to him. Just wanted to state my preference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like I said in the issue, I don't mind it being a breaking change, so default true/false and additive calls to stay consistent with valid/invalid makes the most sense to me.