-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit a22efec
Showing
19 changed files
with
6,579 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"parser": "babel-eslint", | ||
"extends": "eslint:recommended", | ||
"rules": { | ||
"arrow-parens": [2, "as-needed"], | ||
"no-console": 0, | ||
"no-const-assign": 2, | ||
"no-sparse-arrays": 0, | ||
"no-unneeded-ternary": 2, | ||
"no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], | ||
"no-var": 2, | ||
"prefer-arrow-callback": 2, | ||
"semi": [2, "never"] | ||
}, | ||
"env": { | ||
"browser": true, | ||
"es6": true, | ||
"mocha": true, | ||
"node": true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
*.log | ||
*~ | ||
.nyc_output | ||
coverage | ||
.idea | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
*~ | ||
.babelrc | ||
.eslintrc | ||
.nyc_output | ||
.travis.yml | ||
bench | ||
bower.json | ||
docs | ||
scripts | ||
test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
language: node_js | ||
notifications: | ||
email: false | ||
before_install: npm install npm@latest -g | ||
node_js: | ||
- "8" | ||
- "6" | ||
- "4" | ||
script: | ||
- npm run report-coverage |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
Copyright © 2017 Vesa Karvonen | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
# <a id="partial-lenses-validation"></a> [≡](#contents) Partial Lenses Validation · [](https://gitter.im/calmm-js/chat) [](https://github.com/calmm-js/partial.lenses.validation) [](https://www.npmjs.com/package/partial.lenses.validation) | ||
|
||
Validation [transform](https://github.com/calmm-js/partial.lenses/#transforms) | ||
[combinators](https://wiki.haskell.org/Combinator) for [Partial | ||
Lenses](https://github.com/calmm-js/partial.lenses/). The main idea is to | ||
produce validation errors in the same shape as the data being validated. This | ||
way validation errors can be mechanically associated with the corresponding | ||
elements of the validated data structure. | ||
|
||
[](http://badge.fury.io/js/partial.lenses.validation) | ||
[](https://badge.fury.io/bo/partial.lenses.validation) | ||
[](https://travis-ci.org/calmm-js/partial.lenses.validation) | ||
[](https://codecov.io/github/calmm-js/partial.lenses.validation?branch=master) | ||
[](https://david-dm.org/calmm-js/partial.lenses.validation) | ||
[](https://david-dm.org/calmm-js/partial.lenses.validation?type=dev) | ||
|
||
## <a id="contents"></a> [≡](#contents) Contents | ||
|
||
* [Reference](#reference) | ||
* [Operations on rules](#operations-on-rules) | ||
* [`V.validate(rules, data) ~> maybeErrors`](#V-validate) <small><sup>v0.1.0</sup></small> | ||
* [Primitive rules](#primitive-rules) | ||
* [`V.accept ~> rules`](#V-accept) <small><sup>v0.1.0</sup></small> | ||
* [`V.reject(error) ~> rules`](#V-reject) <small><sup>v0.1.0</sup></small> | ||
* [Rules on an element](#rules-on-an-element) | ||
* [`V.unless(predicate, error, ...) ~> rules`](#V-rules) <small><sup>v0.1.0</sup></small> | ||
* [Rules on objects](#rules-on-objects) | ||
* [`V.object([...propNames], {prop: rules, ...}) ~> rules`](#V-object) <small><sup>v0.1.0</sup></small> | ||
* [Rules on arrays](#rules-on-arrays) | ||
* [`V.arrayIx(rules) ~> rules`](#V-arrayIx) <small><sup>v0.1.0</sup></small> | ||
* [`V.arrayId(rules) ~> rules`](#V-arrayId) <small><sup>v0.1.0</sup></small> | ||
* [Conditional rules](#conditional-rules) | ||
* [`V.cases(predicate, rules, ...) ~> rules`](#V-cases) <small><sup>v0.1.0</sup></small> | ||
* [`V.choose(maybeData -> rules) ~> rules`](#V-choose) <small><sup>v0.1.0</sup></small> | ||
* [Known caveats](#known-caveats) | ||
* [Related work](#related-work) | ||
|
||
## <a id="reference"></a> [≡](#contents) Reference | ||
|
||
The [combinators](https://wiki.haskell.org/Combinator) provided by this library | ||
are available as named imports. Typically one just imports the library as: | ||
|
||
```js | ||
import * as V from "partial.lenses.validation" | ||
``` | ||
|
||
### <a id="operations-on-rules"></a> [≡](#contents) Operations on rules | ||
##### <a id="V-validate"></a> [≡](#contents) [`V.validate(rules, data) ~> maybeErrors`](#V-validate) <small><sup>v0.1.0</sup></small> | ||
|
||
`V.validate` runs the given validation rules on the given data structure. Given | ||
properly defined rules, the result is an optional object structure in the shape | ||
of the data structure containing the validation errors. In case there are no | ||
validation errors, the result is `undefined`. | ||
|
||
For example: | ||
|
||
```js | ||
V.validate(V.reject('error'), 'data') | ||
// 'error' | ||
V.validate(V.accept, 'data') | ||
// undefined | ||
``` | ||
|
||
`V.validate` is actually a synonym for | ||
[`L.transform`](https://github.com/calmm-js/partial.lenses/#L-transform) and | ||
"rules" are just Partial Lenses | ||
[transforms](https://github.com/calmm-js/partial.lenses#transforms). | ||
|
||
### <a id="primitive-rules"></a> [≡](#contents) Primitive rules | ||
##### <a id="V-accept"></a> [≡](#contents) [`V.accept ~> rules`](#V-accept) <small><sup>v0.1.0</sup></small> | ||
|
||
`V.accept` accepts the current focus by simply removing it. | ||
|
||
##### <a id="V-reject"></a> [≡](#contents) [`V.reject(error) ~> rules`](#V-reject) <small><sup>v0.1.0</sup></small> | ||
|
||
`V.reject` overwrite the focus with the given error. | ||
|
||
### <a id="rules-on-an-element"></a> [≡](#contents) Rules on an element | ||
##### <a id="V-unless"></a> [≡](#contents) [`V.unless(predicate, error, ...) ~> rules`](#V-rules) <small><sup>v0.1.0</sup></small> | ||
|
||
`V.unless` is given a list of predicate-error -pairs as arguments. The | ||
predicates are called from first to last with the focus. In case a predicate | ||
fails, the focus is overwritten with the corresponding error. If all predicates | ||
pass, the focus is removed. | ||
|
||
For example: | ||
|
||
```js | ||
V.validate(V.unless(R.contains(1), 'does not contain one', | ||
R.contains(2), 'does not contain two'), | ||
[1]) | ||
// 'does not contain two' | ||
``` | ||
|
||
### <a id="rules-on-objects"></a> [≡](#contents) Rules on objects | ||
##### <a id="V-object"></a> [≡](#contents) [`V.object([...propNames], {prop: rules, ...}) ~> rules`](#V-object) <small><sup>v0.1.0</sup></small> | ||
|
||
`V.object` is given a list or property names to preserve in case of errors and a | ||
template object of rules with which to validate the corresponding fields. | ||
|
||
For example: | ||
|
||
```js | ||
V.validate(V.object(['id'], {a: V.accept, b: V.reject('error')}), | ||
{id: 101, a: 1, b: 2}) | ||
// { id: 101, b: 'error' } | ||
``` | ||
|
||
### <a id="rules-on-arrays"></a> [≡](#contents) Rules on arrays | ||
##### <a id="V-arrayIx"></a> [≡](#contents) [`V.arrayIx(rules) ~> rules`](#V-arrayIx) <small><sup>v0.1.0</sup></small> | ||
|
||
`V.arrayIx` is for validating an array of things that are addressed by their | ||
index and have no identities. The result is an array of the same length as the | ||
input with accepted elements having value `null`. In case all elements are | ||
accepted, the array is removed. | ||
|
||
```js | ||
V.validate(V.arrayIx(V.unless(R.equals('a'), 'error')), | ||
['a', 'b']) | ||
// [ null, 'error' ] | ||
``` | ||
|
||
##### <a id="V-arrayId"></a> [≡](#contents) [`V.arrayId(rules) ~> rules`](#V-arrayId) <small><sup>v0.1.0</sup></small> | ||
|
||
`V.arrayId` is for validating an array of things that are addressed by and have | ||
unique identities. The result is an array containing only the rejected | ||
elements. In case all elements are accepted, the array is removed. | ||
|
||
```js | ||
V.validate(V.arrayId(V.object(['id'], {x: V.unless(R.equals(1), 'error')})), | ||
[{id: 1, x: 2}, {id: 2, x: 1}]) | ||
// [ { id: 1, x: 'error' } ] | ||
``` | ||
|
||
### <a id="conditional-rules"></a> [≡](#contents) Conditional rules | ||
##### <a id="V-cases"></a> [≡](#contents) [`V.cases(predicate, rules, ...) ~> rules`](#V-cases) <small><sup>v0.1.0</sup></small> | ||
|
||
`V.cases` is given a list of predicate-rule -pairs as arguments. The predicates | ||
are called from first to last with the focus. In case a predicate passes, the | ||
corresponding rule is used on the focus and the remaining predicates are skipped | ||
and rules ignored. In case all predicates fail, the focus is removed. | ||
|
||
##### <a id="V-choose"></a> [≡](#contents) [`V.choose(maybeData -> rules) ~> rules`](#V-choose) <small><sup>v0.1.0</sup></small> | ||
|
||
`V.choose` is given a function that gets the current focus and then must return | ||
rules to be used on the focus. This allows rules to depend on the data and | ||
allows rules that examine multiple parts of the data. | ||
|
||
## <a id="known-caveats"></a> [≡](#contents) Known caveats | ||
|
||
The implementation technique does not lend itself to an incremental | ||
implementation. Every time [`validate`](V-validate) is run, everything is | ||
recomputed. In an interactive settings this can become a performance issue. | ||
|
||
It would actually likely be possible to use optics for asynchronous validation, | ||
but this implementation does not directly support such a thing and validation is | ||
entirely synchronous. | ||
|
||
## <a id="related-work"></a> [≡](#contents) Related work | ||
|
||
This library primarily exists as a result of Stefan Rimaila's work on | ||
[validation](https://github.com/stuf/validation) using lenses. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"name": "partial.lenses.validation", | ||
"description": "Validation transform combinators for Partial Lenses", | ||
"main": "dist/partial.lenses.validation.js", | ||
"license": "MIT", | ||
"keywords": [ | ||
"structural", | ||
"json", | ||
"validation" | ||
], | ||
"homepage": "https://github.com/calmm-js/partial.lenses.validation", | ||
"dependencies": { | ||
"infestines": "^0.4.5", | ||
"partial.lenses": "^13.0.0" | ||
}, | ||
"ignore": ["*", "!*.md", "!dist/*"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
'use strict'; | ||
|
||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
|
||
var L = require('partial.lenses'); | ||
var I = require('infestines'); | ||
|
||
var object = /*#__PURE__*/I.curry(function (propsToKeep, template) { | ||
var keys$$1 = I.keys(template); | ||
var keep = propsToKeep.length ? propsToKeep.concat(keys$$1) : keys$$1; | ||
return [L.removable.apply(null, keys$$1), L.rewrite(L.get(L.props.apply(null, keep))), L.branch(template)]; | ||
}); | ||
|
||
var isNull = function isNull(x) { | ||
return x === null; | ||
}; | ||
var removeIfAllNull = function removeIfAllNull(xs) { | ||
return L.all(isNull, L.elems, xs) ? undefined : xs; | ||
}; | ||
|
||
var arrayIx = function arrayIx(r) { | ||
return [L.iso(I.id, removeIfAllNull), L.elems, L.required(null), r]; | ||
}; | ||
|
||
var arrayId = function arrayId(r) { | ||
return [L.defaults([]), L.elems, r]; | ||
}; | ||
|
||
var pargs = function pargs(name, fn) { | ||
return (/*#__PURE__*/(process.env.NODE_ENV === 'production' ? I.id : function (fn) { | ||
return function () { | ||
if (arguments.length & 1) throw Error('partial.lenses.validation: `' + name + '` must be given an even number of arguments.'); | ||
return fn.apply(null, arguments); | ||
}; | ||
})(function () { | ||
var r = accept, | ||
n = arguments.length; | ||
while (n) { | ||
n -= 2; | ||
r = fn(arguments[n], arguments[n + 1], r); | ||
} | ||
return r; | ||
}) | ||
); | ||
}; | ||
|
||
var cases = /*#__PURE__*/pargs('cases', L.iftes); | ||
var unless = /*#__PURE__*/pargs('unless', function (c, a, r) { | ||
return L.iftes(c, r, reject(a)); | ||
}); | ||
|
||
var accept = L.removeOp; | ||
var reject = L.setOp; | ||
var validate = L.transform; | ||
|
||
exports.object = object; | ||
exports.arrayIx = arrayIx; | ||
exports.arrayId = arrayId; | ||
exports.cases = cases; | ||
exports.unless = unless; | ||
exports.accept = accept; | ||
exports.reject = reject; | ||
exports.validate = validate; | ||
exports.choose = L.choose; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { all, branch, defaults, elems, get, iftes, iso, props, removable, removeOp, required, rewrite, setOp, transform } from 'partial.lenses'; | ||
import { curry, id, keys } from 'infestines'; | ||
|
||
var object = /*#__PURE__*/curry(function (propsToKeep, template) { | ||
var keys$$1 = keys(template); | ||
var keep = propsToKeep.length ? propsToKeep.concat(keys$$1) : keys$$1; | ||
return [removable.apply(null, keys$$1), rewrite(get(props.apply(null, keep))), branch(template)]; | ||
}); | ||
|
||
var isNull = function isNull(x) { | ||
return x === null; | ||
}; | ||
var removeIfAllNull = function removeIfAllNull(xs) { | ||
return all(isNull, elems, xs) ? undefined : xs; | ||
}; | ||
|
||
var arrayIx = function arrayIx(r) { | ||
return [iso(id, removeIfAllNull), elems, required(null), r]; | ||
}; | ||
|
||
var arrayId = function arrayId(r) { | ||
return [defaults([]), elems, r]; | ||
}; | ||
|
||
var pargs = function pargs(name, fn) { | ||
return (/*#__PURE__*/(process.env.NODE_ENV === 'production' ? id : function (fn) { | ||
return function () { | ||
if (arguments.length & 1) throw Error('partial.lenses.validation: `' + name + '` must be given an even number of arguments.'); | ||
return fn.apply(null, arguments); | ||
}; | ||
})(function () { | ||
var r = accept, | ||
n = arguments.length; | ||
while (n) { | ||
n -= 2; | ||
r = fn(arguments[n], arguments[n + 1], r); | ||
} | ||
return r; | ||
}) | ||
); | ||
}; | ||
|
||
var cases = /*#__PURE__*/pargs('cases', iftes); | ||
var unless = /*#__PURE__*/pargs('unless', function (c, a, r) { | ||
return iftes(c, r, reject(a)); | ||
}); | ||
|
||
var accept = removeOp; | ||
var reject = setOp; | ||
var validate = transform; | ||
|
||
export { object, arrayIx, arrayId, cases, unless, accept, reject, validate }; | ||
export { choose } from 'partial.lenses'; |
Oops, something went wrong.