All Projects → thiagodp → spec-pattern

thiagodp / spec-pattern

Licence: MIT license
Specification design pattern for JavaScript and TypeScript with bonus classes

Programming Languages

typescript
32286 projects

Projects that are alternatives of or similar to spec-pattern

framework
Lightweight, open source and magic-free framework for testing solidity smart contracts.
Stars: ✭ 36 (-16.28%)
Mutual labels:  spec, specification
ntast
Notion Abstract Syntax Tree specification.
Stars: ✭ 101 (+134.88%)
Mutual labels:  spec, specification
es-abstract
ECMAScript spec abstract operations.
Stars: ✭ 86 (+100%)
Mutual labels:  spec, specification
LinqSpecs
A toolset for use the specification pattern in LINQ queries.
Stars: ✭ 161 (+274.42%)
Mutual labels:  specification, specification-pattern
Patterns Cheatsheet
Software design patterns cheatsheet.
Stars: ✭ 47 (+9.3%)
Mutual labels:  design-pattern, pattern
falcon-apispec
apispec plugin that generates OpenAPI specification (aka Swagger Docs) for Falcon web applications.
Stars: ✭ 44 (+2.33%)
Mutual labels:  spec, specification
eggplant
A behaviour driven development (BDD) library for Clojure. Simplicity is key.
Stars: ✭ 16 (-62.79%)
Mutual labels:  spec, specification
Proposals
Tracking ECMAScript Proposals
Stars: ✭ 14,444 (+33490.7%)
Mutual labels:  spec, specification
bash-glob
Bash-powered globbing for node.js. Alternative to node-glob. Does not work on Windows 9 and lower.
Stars: ✭ 13 (-69.77%)
Mutual labels:  pattern, matcher
Specs
The Filecoin protocol specification
Stars: ✭ 249 (+479.07%)
Mutual labels:  spec, specification
elastic-composer
Client-side Elasticsearch query generator and executor. Filter fields, find search suggestions, and paginate query results for your indicies using a simple, reactive, and high-level API
Stars: ✭ 14 (-67.44%)
Mutual labels:  builder, filter
Pydesignpattern
Design Pattern that described by Python, This is the source code for the book of Everybody Know Design Patterns.
Stars: ✭ 174 (+304.65%)
Mutual labels:  design-pattern, filter
dotnet-design-patterns-samples
The samples of .NET design patterns
Stars: ✭ 25 (-41.86%)
Mutual labels:  builder, design-pattern
f2e-spec
Alibaba Front-end Coding Guidelines and Relevant Tools
Stars: ✭ 548 (+1174.42%)
Mutual labels:  specification
esa-httpclient
An asynchronous event-driven HTTP client based on netty.
Stars: ✭ 82 (+90.7%)
Mutual labels:  filter
porn-domains
A collection of domains used for explicit adult content like porn websites.
Stars: ✭ 97 (+125.58%)
Mutual labels:  filter
sql-builder
A simple SQL builder for generate SQL for non-ActiveRecord supports databases
Stars: ✭ 34 (-20.93%)
Mutual labels:  builder
oasis-sdk
Official SDK for the Oasis Network.
Stars: ✭ 57 (+32.56%)
Mutual labels:  builder
django-admin-search
Modal filter for django admin
Stars: ✭ 60 (+39.53%)
Mutual labels:  filter
MARCspec
📄 MARCspec - A common MARC record path language
Stars: ✭ 21 (-51.16%)
Mutual labels:  specification

npm (tag) License npm

spec-pattern

Implementation of the Specification Pattern for JavaScript and TypeScript.

Build complex filters and rules easily.

Installation

$ npm i spec-pattern

Usage

Without syntax sugar

A simple Between rule

import { Between } from 'spec-pattern';

const rating = new Between( 1, 5 );

console.log( rating.isSatisfiedBy( 3 ) ); // true
console.log( rating.isSatisfiedBy( 0 ) ); // false

A little more complex Between rule

import { Between } from 'spec-pattern';

const desiredAgesToAnswerSurvey = new Between( 16, 21 )
   .or( new Between( 65, 120 ) );

console.log( desiredAgesToAnswerSurvey.isSatisfiedBy( 18 ) ); // true
console.log( desiredAgesToAnswerSurvey.isSatisfiedBy( 70 ) ); // true
console.log( desiredAgesToAnswerSurvey.isSatisfiedBy( 5 ) ); // false

Composing rules

import { Between, In, GreaterThan } from 'spec-pattern';

const someCrazyRule = new Between( 1, 3 )
   .or( new Between( 6, 9 ) )
   .or( new In( [ 11, 25, 31 ] ) )
   .or( new GreaterThan( 50 ) );

console.log( someCrazyRule.isSatisfiedBy( 2 ) ); // true
console.log( someCrazyRule.isSatisfiedBy( 7 ) ); // true
console.log( someCrazyRule.isSatisfiedBy( 5 ) ); // false
console.log( someCrazyRule.isSatisfiedBy( 11 ) ); // true
console.log( someCrazyRule.isSatisfiedBy( 50 ) ); // false
console.log( someCrazyRule.isSatisfiedBy( 51 ) ); // true

// Printable !
console.log( someCrazyRule.toString() );
// (((between (1, 3) or between (6, 9)) or in [11, 25, 31]) or greater than 50)

Not only numbers

import { StartsWith, Contains } from 'spec-pattern';

const helloWithoutWorld = new StartsWith( 'Hello' )
    .andNot( new Contains( 'world' ) );

console.log( helloWithoutWorld.isSatisfiedBy( 'Hello Bob' ) ); // true
console.log( helloWithoutWorld.isSatisfiedBy( 'Hello world' ) ); // false
import { LengthBetween, EqualTo } from 'spec-pattern';

const crazyText = new LengthBetween( 2, 5 )
    .andNot( new EqualTo( 'Hello' ) );

console.log( crazyText.isSatisfiedBy( '' ) ); // false
console.log( crazyText.isSatisfiedBy( 'Hi' ) ); // true
console.log( crazyText.isSatisfiedBy( 'Hello' ) ); // false
console.log( crazyText.isSatisfiedBy( 'Howdy' ) ); // true
console.log( crazyText.isSatisfiedBy( 'Hello world' ) ); // false

With syntax sugar

A simple Between rule

import { between } from 'spec-pattern';

const rating = between( 1, 5 );

console.log( rating.isSatisfiedBy( 3 ) ); // true
console.log( rating.isSatisfiedBy( 0 ) ); // false

A little more complex Between rule

import { between } from 'spec-pattern';

const desiredAgesToAnswerSurvey = between( 16, 21 )
   .or( between( 65, 120 ) );

console.log( desiredAgesToAnswerSurvey.isSatisfiedBy( 18 ) ); // true
console.log( desiredAgesToAnswerSurvey.isSatisfiedBy( 70 ) ); // true
console.log( desiredAgesToAnswerSurvey.isSatisfiedBy( 5 ) ); // false

Composing rules

import { between, isIn, greaterThan } from 'spec-pattern';

const someCrazyRule = between( 1, 3 )
   .or( between( 6, 9 ) )
   .or( isIn( [ 11, 25, 31 ] ) )
   .or( greaterThan( 50 ) );

console.log( someCrazyRule.isSatisfiedBy( 2 ) ); // true
console.log( someCrazyRule.isSatisfiedBy( 7 ) ); // true
console.log( someCrazyRule.isSatisfiedBy( 5 ) ); // false
console.log( someCrazyRule.isSatisfiedBy( 11 ) ); // true
console.log( someCrazyRule.isSatisfiedBy( 50 ) ); // false
console.log( someCrazyRule.isSatisfiedBy( 51 ) ); // true

// Printable !
console.log( someCrazyRule.toString() );
// (((between (1, 3) or between (6, 9)) or in [11, 25, 31]) or greater than 50)

Not only numbers

import { startsWith, contains } from 'spec-pattern';

const helloWithoutWorld = startsWith( 'Hello' )
    .andNot( contains( 'world' ) );

console.log( helloWithoutWorld.isSatisfiedBy( 'Hello Bob' ) ); // true
console.log( helloWithoutWorld.isSatisfiedBy( 'Hello world' ) ); // false
import { lengthBetween, equalTo } from 'spec-pattern';

const crazyText = lengthBetween( 2, 5 )
    .andNot( equalTo( 'Hello' ) );

console.log( crazyText.isSatisfiedBy( '' ) ); // false
console.log( crazyText.isSatisfiedBy( 'Hi' ) ); // true
console.log( crazyText.isSatisfiedBy( 'Hello' ) ); // false
console.log( crazyText.isSatisfiedBy( 'Howdy' ) ); // true
console.log( crazyText.isSatisfiedBy( 'Hello world' ) ); // false

Available sugar

There is a corresponding sugar function for every available class. Sugar functions are always named in camelCase. For instance, sameValueAs() for the class SameValueAs. The only exception is the class In. Since in is a reserved word in JavaScript and thus cannot be a function name, the corresponding sugar is isIn.

Available classes

  • SameValueAs( value: any ): equality of values, not of types, not of instances
  • StrictSameValueAs( value: any ): equality of values and types, not of instances
  • EqualTo( value: any ): equality of values or instances, with ==
  • StrictEqualTo( value: any ): equality of values and types or of instances, with ===
  • SameTypeAs( value: any ): equality of types
  • GreaterThan( value: any )
  • GreaterThanOrEqualTo( value: any )
  • LessThan( value: any )
  • LessThanOrEqualTo( value: any )
  • Between( min: any, max: any )
  • In( values: array ): inside an array
  • StartsWith( value: string, ignoreCase: boolean = false ): string starts with
  • EndsWith( value: string, ignoreCase: boolean = false ): string ends with
  • Contains( value: string, ignoreCase: boolean = false ): string contains
  • LengthBetween( min: any, max: any ): string length between two values
  • Empty(): string is empty or array is empty
  • Matches( regex: RegExp ): matches a regular expression
  • Any( ...specs: Spec ): composite that takes in multiple Specs and performs an or
  • All( ...specs: Spec ): composite that takes in multiple Specs and performs an and

All these classes extend the abstract class Composite, which in turn implements the interface Spec:

export interface Spec< C, T extends C | unknown > {

    isSatisfiedBy( candidate: C | T ): boolean;

    and( other: Spec< C, T > ): Spec< C, T >;

    andNot( other: Spec< C, T > ): Spec< C, T >;

    or( other: Spec< C, T > ): Spec< C, T >;

    orNot( other: Spec< C, T > ): Spec< C, T >;

    xor( other: Spec< C, T > ): Spec< C, T >;

    xorNot( other: Spec< C, T > ): Spec< C, T >;

    not(): Spec< C, T >;

}

Creating your own class

Create your own class by extending the 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< C, T extends C | unknown > extends Composite< C, T > {

    constructor( private _value: T ) {
        super();
    }

    isSatisfiedBy( candidate: C | 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 ); // super()

    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

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].