npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

bspec

v1.0.1

Published

A JavaScript library for structuring business rules

Downloads

967

Readme

bspec logo

Build Status Coverage Status

Sauce Test Status

A JavaScript library for structuring business rules.

Overview

Structuring and validating business rules in JavaScript

Example

Consider the following scenario:

On entering a train station, passengers are required to insert a ticket into a slot beside a closed barrier. An entry barrier opens only if the ticket meets all of the following criteria:

  1. it is valid for travel from that station;
  2. it has not expired;
  3. it has not already been used for the maximum number of journeys allowed.
'use strict';

// use promise-based specifications
var Spec = require('bspec').PromiseSpec;

// hardcore the `today` date for the sake of consistent results
var TODAY = new Date(2015, 2, 1);

// Is the ticket expired?
var isTicketExpired = function isTicketExpired(ticket) {
  return Promise.resolve(TODAY > ticket.expires_at);
};

// Is the ticket has been used for the maximum number of journeys allowed?
var isMaxJourneys = function isMaxJourneys(ticket) {
  return Promise.resolve(ticket.cur_journeys >= ticket.max_journeys);
};

// Is the ticket valid for travel from `name` station?
var isValidFromStation = function isValidFromStation(name, ticket) {
  return Promise.resolve(ticket.stations.indexOf(name) !== -1);
};

// Rule implementation for the `Riva` station barrier
var barrierSpec = Spec(isValidFromStation.bind(null, 'Riva'))
                        .and(Spec(isTicketExpired).not())
                        .and(Spec(isMaxJourneys).not());

// A ticket we would like to check
var ticket = {
  stations: [ 'Riva' ],
  expires_at: new Date(2015, 2, 6),
  max_journeys: 30,
  cur_journeys: 11
};

// Verify the ticket satisfies the created specification
barrierSpec.isSatisfiedBy(ticket)
  .then(function(result) {
    console.log('Is the ticket can be used to enter the Riva station:', result);
  })
  .catch(function(err) {
    throw err;
  });

Installation

installing with npm:

$ npm install bspec --save

In browser

To use bspec in a browser, use the bspec.js file in the /dist directory of this repository, or build it manually. To build a fresh version:

$ npm install
$ npm run browser

installing with bower:

$ bower install bspec

Usage

The essential part of the library is a specification -- an object with the following properties:

  • it can be combined with other specification-objects using .and(), .or() and .not() methods to form a composite specification and express more complex rules.
  • it implements isSatisfiedBy method -- a predicate that determines whether a candidate object does or does not satisfy some criteria.

The library supports the following specifications:

  • Synchronous -- SyncSpec
  • Callback-based -- CallbackSpec
  • Promise-based -- PromiseSpec
  var Spec = require('bspec').SyncSpec;
  var Spec = require('bspec').CallbackSpec;
  var Spec = require('bspec').PromiseSpec;

To create a composable specification, make an instance of Spec and define the isSatisfiedBy method to check for some condition. isSatisfiedBy method signature depends on the type of specification (see the API section below).

There are several ways you can define the isSatisfiedBy method:

  • Write a predicate-function and wrap it in a Spec object;
  • Create an object with the isSatisfiedBy property and wrap it in a Spec object;
  • Derive a new object from Spec and implement the isSatisfiedBy function.

Writing a predicate-function and wrapping it in a Spec object

var Spec = require('bspec').SyncSpec;

function isExpired(order) {
  return (new Date() > order.date);
}

var expiredSpec = new Spec(isExpired);
console.log(expiredSpec.isSatisfiedBy({ date: new Date(2015, 1, 5) }));

Creating an object with the isSatisfiedBy property and wrapping it in a Spec

var Spec = require('bspec').SyncSpec;

var isExpired = {
  isSatisfiedBy: function(order) {
    return (new Date() > order.date);
  }
};

var expiredSpec = new Spec(isExpired);
console.log(expiredSpec.isSatisfiedBy({ date: new Date(2015, 1, 5) }));

Deriving a new object from Spec and implementing the isSatisfiedBy function

var Spec = require('bspec').SyncSpec;
var util = require('util');

function IsExpiredSpec() { }

util.inherits(IsExpiredSpec, Spec);

IsExpiredSpec.prototype.isSatisfiedBy = function isSatisfiedBy(order) {
  return (new Date() > order.date);
};

var expiredSpec = new IsExpiredSpec();
console.log(expiredSpec.isSatisfiedBy({ date: new Date(2015, 1, 5) }));

OR with ES6 classes:

var Spec = require('bspec').SyncSpec;

class IsExpiredSpec extends Spec {
  isSatisfiedBy(order) {
    return (new Date() > order.date);
  }
}

var expiredSpec = new IsExpiredSpec();
console.log(expiredSpec.isSatisfiedBy({ date: new Date(2015, 1, 5) }));

API

.and(otherSpec)

the and of a set of specifications is true if and only if all of its operands are true.

var spec = spec1.and(spec2);

.or(otherSpec)

the or of a set of specifications is true if and only if one or more of its operands is true

var spec = spec1.or(spec2);

.not()

not negates the specification

var spec = spec1.not();

.explain()

returns a string that describes a composite specification

console.log(someSpec.explain());
// ((ValidOrderSpec AND (NOT OverDueOrderSpec)) AND (NOT OrderProcessed))

NOTE: meaningful names will be printed only for specifications derived from a Spec object.

.isSatisfiedBy(...)

checks whether some candidate object satisfies the specification. isSatisfiedBy method signature depends on the specification type:

SyncSpec (synchronous specification)

  // signature:
  function isSatisfiedBy(candidate: any): boolean;
  
  // usage:
  var result = spec.isSatisfiedBy(obj);
  
  // `result` true|false value
  // should throw an exception in case of an error

CallbackSpec (callback-based specification)

  // signature:
  function isSatisfiedBy(candidate: any, cb: (err: Error, result: boolean): void): void;
  
  // usage:
  spec.isSatisfiedBy(obj, function(err, result) {
    // `err` contains an error if any
    // `result` true|false value
  });

PromiseSpec (promise-based specification)

  // signature:
  function isSatisfiedBy(candidate: any): Promise;

  // usage:
  spec.isSatisfiedBy(obj)
    .then(function(result) {
      // `result` true|false value
    }).catch(function(err) {
      // `err` contains an error if any
    });

NOTE: To use promise-based specifications you need ES6 Promise to be implemented in your environment, e.g. io.js, a modern browser or a polyfill like es6-promise.

For the details of usage, take a look at the examples section in the project.

Tests

To run the tests for bspec:

$ npm test

Contact

[Grigoriy Chudnov] (mailto:[email protected])

License

Distributed under the The MIT License (MIT).