spec-pattern
Implementation of the Specification Pattern for JavaScript and TypeScript.
Useful for building complex filters or rules in a easy way.
No external dependencies. Fully tested. Adopts semantic versioning. Forks are welcome!
Install
$ npm install spec-pattern --save
Examples
A simple Between rule
import { Between } from 'spec-pattern';
let rules = new Between( 1, 3 );
console.log( rules.isSatisfiedBy( 2 ) );
A little more complex Between rule
import { Between } from 'spec-pattern';
let rules = new Between( 1, 3 )
.or( new Between( 6, 9 ) );
console.log( rules.isSatisfiedBy( 2 ) );
console.log( rules.isSatisfiedBy( 7 ) );
console.log( rules.isSatisfiedBy( 5 ) );
Composing rules
import { Between, In, GreaterThan } from 'spec-pattern';
let rules = new Between( 1, 3 )
.or( new Between( 6, 9 ) )
.or( new In( [ 11, 25, 31 ] )
.or( new GreaterThan( 50 ) );
console.log( rules.isSatisfiedBy( 2 ) );
console.log( rules.isSatisfiedBy( 7 ) );
console.log( rules.isSatisfiedBy( 5 ) );
console.log( rules.isSatisfiedBy( 11 ) );
console.log( rules.isSatisfiedBy( 50 ) );
console.log( rules.isSatisfiedBy( 51 ) );
Not only numbers
import { StartsWith, Contains } from 'spec-pattern';
let rules = new StartsWith( 'Hello' )
.andNot( new Contains( 'world' ) );
console.log( rules.isSatisfiedBy( 'Hello Bob' ) );
console.log( rules.isSatisfiedBy( 'Hello world' ) );
import { LengthBetween, EqualTo } from 'spec-pattern';
let rules = new LengthBetween( 2, 5 )
.andNot( new EqualTo( 'Hello' ) );
console.log( rules.isSatisfiedBy( '' ) );
console.log( rules.isSatisfiedBy( 'Hi' ) );
console.log( rules.isSatisfiedBy( 'Hello' ) );
console.log( rules.isSatisfiedBy( 'Howdy' ) );
console.log( rules.isSatisfiedBy( 'Hello world' ) );
Available classes
EqualTo( value: any )
GreaterThan( value: any )
GreaterThanOrEqualTo( value: any )
LessThan( value: any )
LessThanOrEqualTo( value: any )
Between( min: any, max: any )
In( values: array )
StartsWith( value: string, ignoreCase: boolean = false )
EndsWith( value: string, ignoreCase: boolean = false )
Contains( value: string, ignoreCase: boolean = false )
LengthBetween( min: any, max: any )
Matches( regex: RegExp )
All these classes extend the abstract class Composite
, which in turn implements the interface Spec
:
interface Spec< T > {
isSatisfiedBy( candidate: T ): boolean;
and( other: Spec< T > ): Spec< T >;
andNot( other: Spec< T > ): Spec< T >;
or( other: Spec< T > ): Spec< T >;
orNot( other: Spec< T > ): Spec< T >;
not(): Spec< T >;
}
Creating your own class
Creating your own class is very easy. Just extends abstract class Composite_
, like in the following example. Of course, you can also extend one of the aforementioned classes or implement the interface Spec_
(but why reinventing the wheel, right?).
Let's create a class DifferentFrom_
...
...in TypeScript:
import { Composite } from 'spec-pattern';
export class DifferentFrom< T > extends Composite< T > {
constructor( private _value: T ) {
super();
}
isSatisfiedBy( candidate: T ): boolean {
return this._value != candidate;
}
toString(): string {
return 'different from ' + this._value;
}
}
...or in JavaScript 6+:
import { Composite } from 'spec-pattern';
class DifferentFrom extends Composite {
constructor( value ) {
this._value = value;
}
isSatisfiedBy( candidate ) {
return this._value != candidate;
}
toString() {
return 'different from ' + this._value;
}
}
...or in JavaScript 5+:
var Composite = require( 'spec-pattern' ).Composite;
function DifferentFrom( value ) {
Composite.call( this );
this._value = value;
this.isSatisfiedBy = function ( candidate ) {
return this._value != candidate;
};
this.toString = function() {
return 'different from ' + this._value;
};
}
DifferentFrom.prototype = Object.create( Composite.prototype );
DifferentFrom.prototype.constructor = DifferentFrom;
That's it! Just three methods: constructor
, isSatisfiedBy
, and toString()
.
License
MIT © Thiago Delgado Pinto