Skip to content

Commit

Permalink
Initial
Browse files Browse the repository at this point in the history
  • Loading branch information
polytypic committed Oct 15, 2017
0 parents commit a22efec
Show file tree
Hide file tree
Showing 19 changed files with 6,579 additions and 0 deletions.
21 changes: 21 additions & 0 deletions .eslintrc
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
}
}
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.log
*~
.nyc_output
coverage
.idea
node_modules
10 changes: 10 additions & 0 deletions .npmignore
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
10 changes: 10 additions & 0 deletions .travis.yml
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
19 changes: 19 additions & 0 deletions LICENSE.md
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.
162 changes: 162 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# <a id="partial-lenses-validation"></a> [](#contents) Partial Lenses Validation &middot; [![Gitter](https://img.shields.io/gitter/room/calmm-js/chat.js.svg)](https://gitter.im/calmm-js/chat) [![GitHub stars](https://img.shields.io/github/stars/calmm-js/partial.lenses.validation.svg?style=social)](https://github.com/calmm-js/partial.lenses.validation) [![npm](https://img.shields.io/npm/dm/partial.lenses.validation.svg)](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.

[![npm version](https://badge.fury.io/js/partial.lenses.validation.svg)](http://badge.fury.io/js/partial.lenses.validation)
[![Bower version](https://badge.fury.io/bo/partial.lenses.validation.svg)](https://badge.fury.io/bo/partial.lenses.validation)
[![Build Status](https://travis-ci.org/calmm-js/partial.lenses.validation.svg?branch=master)](https://travis-ci.org/calmm-js/partial.lenses.validation)
[![Code Coverage](https://img.shields.io/codecov/c/github/calmm-js/partial.lenses.validation/master.svg)](https://codecov.io/github/calmm-js/partial.lenses.validation?branch=master)
[![](https://david-dm.org/calmm-js/partial.lenses.validation.svg)](https://david-dm.org/calmm-js/partial.lenses.validation)
[![](https://david-dm.org/calmm-js/partial.lenses.validation/dev-status.svg)](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.
17 changes: 17 additions & 0 deletions bower.json
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/*"]
}
64 changes: 64 additions & 0 deletions dist/partial.lenses.validation.cjs.js
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;
53 changes: 53 additions & 0 deletions dist/partial.lenses.validation.es.js
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';
Loading

0 comments on commit a22efec

Please sign in to comment.