Skip to content

Commit

Permalink
Adds from to optional and Blueprint.required
Browse files Browse the repository at this point in the history
* Adds `required` to blueprint, which can be used with `from` to map the
input properties to different output properties. This is intendeded to
be used in conjunction with immutable.
* Adds the same `from` behavior to `optional`
  • Loading branch information
losandes committed Jun 25, 2019
1 parent 0701b4c commit 4ce1489
Show file tree
Hide file tree
Showing 8 changed files with 414 additions and 36 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,27 @@ const optionalValues = blueprint('optionalValues', {
})
```
### Unpacking
The `optional`, and `required` functions can be used to map the input values to different output properties, by using `from`. With `optional`, `from` can be chained with `withDefault`. This is particularly useful when accepting input with a different model than the intended output.
```JavaScript
const { blueprint, gt, optional } = require('@polyn/blueprint')

const unpackedValues = blueprint('UnpackedValues', {
requiredString: required('string')
.from(({ input }) => input.something.string),
requiredGt: required(gt(10))
.from(({ input }) => input.something.twelve),
requiredExp: required(/^book|magazine$/)
.from(({ input }) => input.something.type),
stringFrom: optional('string')
.from(({ input }) => input.something.string),
stringOrDefault: optional('string')
.from(({ input }) => input.something.string)
.withDefault('foo')
})
```
## Custom Validators
Blueprint supports multiple ways for defining your own validators.
Expand Down
110 changes: 94 additions & 16 deletions dist/blueprint.js
Original file line number Diff line number Diff line change
Expand Up @@ -478,16 +478,8 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons

return _objectSpread({}, validators[name]);
};
/**
* Fluent interface to support optional function based validators
* (i.e. like gt, lt, range, custom), and to use default values when
* the value presented is null, or undefined.
* @param {any} comparator - the name of the validator, or a function that performs validation
*/


var optional = function optional(comparator) {
var options = {};
var comparatorToValidator = function comparatorToValidator(comparator) {
var validator;

if (is.function(comparator)) {
Expand All @@ -498,14 +490,29 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons
validator = validators[comparator];
}

return validator;
};
/**
* Fluent interface to support optional function based validators
* (i.e. like gt, lt, range, custom), and to use default values when
* the value presented is null, or undefined.
* @param {any} comparator - the name of the validator, or a function that performs validation
*/


var optional = function optional(comparator) {
var defaultVal;
var from;
var validator = comparatorToValidator(comparator);

var valueOrDefaultValue = function valueOrDefaultValue(value) {
if (is.function(options.defaultValue)) {
if (is.function(defaultVal)) {
return {
value: options.defaultValue()
value: defaultVal()
};
} else if (is.defined(options.defaultValue)) {
} else if (is.defined(defaultVal)) {
return {
value: options.defaultValue
value: defaultVal
};
} else {
return {
Expand All @@ -514,18 +521,88 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons
}
};

var output = function output(context) {
var value = context.value;
var output = function output(ctx) {
var context;

if (from) {
context = _objectSpread({}, ctx, {
value: from(ctx)
});
} else {
context = ctx;
}

var _context = context,
value = _context.value;

if (is.nullOrUndefined(value)) {
return valueOrDefaultValue(value);
} else {
return validator(context);
}
};
/**
* A value factory for producing a value, given the constructor context
* @param {function} callback - a callback function that accepts IValidationContext and produces a value
*/


output.from = function (callback) {
if (is.function(callback)) {
from = callback;
}

return output;
};
/**
* Sets a default value to be used when a value is not given for this property
* @param {any} defaultValue - the value to use when this property is null or undefined
*/


output.withDefault = function (defaultValue) {
options.defaultValue = defaultValue;
defaultVal = defaultValue;
return output;
};

return output;
};
/**
* Fluent interface to support optional function based validators
* (i.e. like gt, lt, range, custom), and to use default values when
* the value presented is null, or undefined.
* @param {any} comparator - the name of the validator, or a function that performs validation
*/


var required = function required(comparator) {
var from;
var validator = comparatorToValidator(comparator);

var output = function output(ctx) {
var context;

if (from) {
context = _objectSpread({}, ctx, {
value: from(ctx)
});
} else {
context = ctx;
}

return validator(context);
};
/**
* A value factory for producing a value, given the constructor context
* @param {function} callback - a callback function that accepts IValidationContext and produces a value
*/


output.from = function (callback) {
if (is.function(callback)) {
from = callback;
}

return output;
};

Expand All @@ -539,6 +616,7 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons
registerBlueprint: registerBlueprint,
registerExpression: registerExpression,
optional: optional,
required: required,
// below are undocumented / subject to breaking changes
registerInstanceOfType: registerInstanceOfType,
registerArrayOfType: registerArrayOfType,
Expand Down
18 changes: 16 additions & 2 deletions dist/blueprint.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 42 additions & 3 deletions examples-typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
registerBlueprint,
registerExpression,
optional,
required,
gt, gte, lt, lte, range,
is
} from '.'
Expand Down Expand Up @@ -115,10 +116,19 @@ import {
maybeEnum: optional(/^book|magazine$/),
enumOrDefault: optional(/^book|magazine|product$/).withDefault('product'),
maybeCustom: optional(({ value }) => typeof value === 'string')
.withDefault('custom')
.withDefault('custom'),
fromContext: optional('string')
.from(({ input }: IValidationContext) => input.context.string),
fromContextWithDefault: (optional('string')
.from(({ input }: IValidationContext) => input.context.string2) as optional)
.withDefault('default!')
})

const result = optionalValues.validate({});
const result = optionalValues.validate({
context: {
string: 'foo'
}
});

expect(result.err).to.be.null
expect(result.value).to.deep.equal({
Expand All @@ -127,7 +137,36 @@ import {
gt20OrDefault: 42,
maybeEnum: undefined,
enumOrDefault: 'product',
maybeCustom: 'custom'
maybeCustom: 'custom',
fromContext: 'foo',
fromContextWithDefault: 'default!'
})
})()

;(() => {
// Exploring `required`
const optionalValues = blueprint('optionalValues', {
requiredString: required('string')
.from(({ input }: IValidationContext) => input.context.string),
requiredGt: required(gt(10))
.from(({ input }: IValidationContext) => input.context.twelve),
requiredExp: required(/^book|magazine$/)
.from(({ input }: IValidationContext) => input.context.type)
})

const result = optionalValues.validate({
context: {
string: 'foo',
twelve: 12,
type: 'book'
}
});

expect(result.err).to.be.null
expect(result.value).to.deep.equal({
requiredString: 'foo',
requiredGt: 12,
requiredExp: 'book'
})
})()

Expand Down
10 changes: 10 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,10 +250,20 @@ declare namespace optional {
*/
interface optional {
(comparator: string | Validator | RegExp): (context: IValidationContext) => IValueOrError;
from (callback: (context: IValidationContext) => any): optional | ((context: IValidationContext) => IValueOrError);
withDefault (value: any): (context: IValidationContext) => IValueOrError;
}

/**
* Support for unpacking constructor input
*/
interface required {
(comparator: string | Validator | RegExp): (context: IValidationContext) => IValueOrError;
from (callback: (context: IValidationContext) => any): (context: IValidationContext) => IValueOrError;
}

declare function optional (comparator: string | Validator | RegExp): optional;
declare function required (comparator: string | Validator | RegExp): required;


// is ==========================================================================
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@polyn/blueprint",
"version": "2.3.0",
"version": "2.4.0",
"description": "An easy to use, flexible, and powerful validation library for nodejs and browsers",
"main": "index.js",
"types": "index.d.ts",
Expand Down
Loading

0 comments on commit 4ce1489

Please sign in to comment.