0.0.7 • Published 8 years ago

criteria-pattern v0.0.7

Weekly downloads
3
License
Apache-2.0
Repository
github
Last release
8 years ago

criteria-pattern

An async friendly version of a Criteria pattern.

Background

Originally this library was created for validation and transforming objects, however after coming into contact with the specification and criteria pattern; this library is still mostly intended for validation and transforming objects however it has be refactored to allowing mimicing those patterns so it can be used for buisiness logic.

Installation

 npm install --save criteria-pattern

There is also a criteria-pattern-basics package you can install for some basic useful criterion:

 npm install --save criteria-pattern-basics

Usage

There are four main classes in this library:

  • Criterion
  • Failure
  • Criteria
  • BulkFailure

Criterion

The Criterion class represents a single operation that will be performed on the data passed to its satisfy(value) method. Extend this class to implement your own custom check, filter or business logic:

import {Criterion, Failure} from 'criteria-pattern';

class CanUserWithdraw extends Criterion {

  satisfy(user) {

    return (user.balance > 0) ? user : new Failure('Insufficient funds!', null);

  }

}

someProcessForLoadingUsers.
get().
then(user=>{

  var check = new CanUserWithdraw();
  var result = check.satisfy(user);

  if(result instanceof Failure)
    return reply(result.error);

    withdrawFunds(result);

});

In the above example, returning an instance of Failure is not absolutely necessary but is highly recommended. The Criteria class and the boolean logic methods of Criterion depend on instances of Failure to recognise a failure. If the value returned is not an instance of Failure and is not a Promise then the Criterion is considered satisfied and the value returned as the result.

When consuming the result of Criterion#satisfy remember the result can be a Failure, Promise or anything else your Criterion return.

It is best to guard against unforseen race conditions by wrapping the result in a Promise#resolve call and continuing execution from there.

For this reason Criterion#satisfy should never return an Error unless the intention is to reject the promise chain.

Failure

The Failure class accepts two arguments to its constructor: an error message (string) and a context (object) for the error message. The error message can contain template variables example: This is an error message for {you}.. Calling Failure#toString will attempt to replace {you} with the key you from the context.

This is used internally to allow for more flexible error messages.

Criteria

The Criteria class is a actually a sub-class of Criterion however it works by applying an entire schema (map/object/whatever) of Criterion to an object:

import {basics} from 'criteria-pattern-common';
import {Criteria, BulkFailure} from 'criteria-pattern';

class NewUserCriteria extends Criteria {

  constructor() {

   super({
 
    name: basics.required().and(basics.string()).and(new NameCriterion()),
    email: basics.required().and(basics.string()).and(new EmailCriterion()),
    password: basics.required().and(basics.range(8, 255))
 
   });

  }

}

var check = new NewUserCriteria();

check.satisfy(req.body).
then(result=> {

  if(result instanceof BulkFailure)
    return res.send(409, result.errros);

    return createUserSomeHow(result.value);

});

Criterion#satisfy always returns a Promise, this Promise resolves with the result of applying the Criteria or a BulkFailure when one of the Criterion fails.

BulkFailure

A child class of Failure with an additional property errors.

Error Messages

When using the Criteria class, you can pass a map of error message templates. as the second parameter to its constructor. When a Failure occurs, the Criteria will attempt to resolve and expand the relavant error template.

Resolution of an error message works by using either the concatenation of key, '.' and the error message or the error message or finally the error message itself as the property to copy from the error message map.

Example error message map:

 const messages = {

  'name.not': 'This is the error message used when the NotCriterion fails for the name key', 
  'not': 'This will be used for any failing NotCriterion except for the name key of course',
  'email.required': 'This is used when the {email} property is not supplied',
  'required': 'This is used for failing required keys',
 }

Chaining

Like the Criteria and Specification pattern, Criterion can be chained for boolean logic effect. Currently the following methods are supported:

  • Criterion#and({Criterion})
  • Criterion#or({Criterion})
  • Criterion#Not()

Each one returns a Crtierion sub class. Keep in mind that in order to support async operations, their satisfy({value}) methods all return Promises.

Example:

var chain = (new Validate()).
            and(new AlreadySaved().Not())
            and(new CheckLockStatus()).
            or(new RequestQ()).
            and(new Save());

chain.satisfy().
then(result=> {

  if(result instanceof Failure)
  return res.send(409, result.errors);

  res.send(201);

}).
catch(e=>handleError(e));

License

Apache-2.0 © Quenk Technologies Limited

0.0.7

8 years ago

0.0.6

8 years ago

0.0.5

8 years ago

0.0.4

8 years ago

0.0.3

8 years ago

0.0.2

8 years ago