A framework to create efficient and extensible validations
- Flexible
- Deterministic
- Extendable
- Persistable
- Portable
One problem with validation plugins, frameworks, etc. is that they are usually opinionated in some way -- they have an inflexible API, require certain dependencies, etc. This hampers reusability and introduces additional complexity as an application's validation requirements evolve over time. Further, if we wanted to conditionally validate certain attributes (i.e. allow an end user to 'configure' the validation), we would typically have to hard code this functionality into each validation.
The goal of this module is to solve these problems without getting in the way. By prioritizing validation composition over all else, this framework is ideal for creating complex validations that are both portable and maintainable.
const FenceBuilder = require('./lib');
const FB = new FenceBuilder();
FB.register(function (val1, val2, 'strictEqual') {
return val1 === val2;
});
const validation = FB.fork().strictEqual('a').build();
console.log(`validation of "a": ${validation.run('a')}`);
console.log(`validation of "b": ${validation.run('b')}`);
A Fence begins with an instance of FenceBuilder
. The general lifecycle of a FenceBuilder
instance is: instantiation, registration, composition, and building.
An instance can be created by creating a new FenceBuilder
:
// new FenceBuilder
const FB = new FenceBuilder();
An instance can also be created from existing instances of FenceBuilder
via the fork()
method:
// new FenceBuilder
const FB = new FenceBuilder();
// someFence 'extends' FB
const someFence = FB.fork();
// anotherFence 'extends' someFence
const anotherFence = someFence.fork();
When an instance is fork()
'd, its prototype is copied by reference and used to instantiate a new instance of FenceBuilder
that is then returned. The result of callingfork()
is a 'child' instance which can be acted on without affecting its parent.
A fresh instance of FenceBuilder
won't be able to do much -- we must 'register' functions so that they can be used during the composition phase. Registering a function will add an instance method to an instance of FenceBuilder
. Doing so makes the registered function available for use during composition. To register a function, provide register()
with a name
and a function reference or declaration.
// new FenceBuilder
const FB = new FenceBuilder();
FB.register(function(val1, val2, 'strictEqual') {
return val1 === val2;
});
The name
attribute is used to promote determinism in cases where function references can not be passed around. Consider the case where we want to validate something on the server and the client -- the server may have a required
function available from utils
and the client may have a required
function available from clientUitls
. If two named functions are functionally equivalent (produce the exact same output for every possible input) then we can say overall the validation is deterministic regardless of its execution context.
When registering a function, we accept a name
and a function reference. When these instance methods are called, they create an Invokable
which represents a function and optionally some arguments.
See example
folder or the runkit example.
If you don't use a package manager, you can access fence.js
via unpkg (CDN), download the source, or point your package manager to the url.
fence.js
is compiled as a collection of CommonJS modules & [ES2015 modules](http://www.2ality.com/2014/0
-9/es6-modules-final.html) for bundlers that support the jsnext:main
or module
field in package.json (Rollup, Webpack 2)
The fence.js
package includes precompiled production and development UMD builds in the dist
folder. They can be used directly without a bundler and are thus compatible with many popular JavaScript module loaders and environments. You can drop a UMD build as a <script>
tag on your page. The UMD builds make fence.js
available as a window.fence.js
global variable.
The code is available under the MIT license.
We are open to contributions, see CONTRIBUTING.md for more info.
This module was created using generator-module-boilerplate.