Skip to content

Commit

Permalink
add test for typescript
Browse files Browse the repository at this point in the history
  • Loading branch information
frankthelen committed Jan 2, 2021
1 parent 9ab684c commit dc0d40f
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 8 deletions.
48 changes: 42 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ const rools = new Rools();
await rools.register([ruleMoodGreat, ruleGoWalking]);
await rools.evaluate(facts);
```

These are the resulting facts:

```javascript
{ user: { name: 'frank', stars: 347, mood: 'great' },
weather: { temperature: 20, windy: true, rainy: false },
Expand Down Expand Up @@ -105,10 +107,11 @@ or through getters and setters if applicable, e.g., `facts.user.getSalery()`.
### Conflict resolution

If there is more than one rule ready to fire, i.e., the conflict set is greater 1, the following conflict resolution strategies are applied (by default, in this order):
* Refraction -- Each rule will fire only once, at most, during any one match-resolve-act cycle.
* Priority -- Rules with higher priority will fire first. Set the rule's property `priority` to an integer value. Default priority is `0`. Negative values are supported.
* Specificity -- Rules which are more specific will fire first. For example, there is rule R1 with premises P1 and P2, and rule R2 with premises P1, P2 and P3. R2 is more specific than R1 and will fire first. R2 is more specific than R1 because it has *all* premises of R1 and additional ones.
* Order of rules -- The rules that were registered first will fire first.

* Refraction -- Each rule will fire only once, at most, during any one match-resolve-act cycle.
* Priority -- Rules with higher priority will fire first. Set the rule's property `priority` to an integer value. Default priority is `0`. Negative values are supported.
* Specificity -- Rules which are more specific will fire first. For example, there is rule R1 with premises P1 and P2, and rule R2 with premises P1, P2 and P3. R2 is more specific than R1 and will fire first. R2 is more specific than R1 because it has *all* premises of R1 and additional ones.
* Order of rules -- The rules that were registered first will fire first.

### Final rules

Expand All @@ -122,6 +125,7 @@ While premises (`when`) are always working synchronously on the facts,
actions (`then`) can be synchronous or asynchronous.

Example: asynchronous action using async/await

```javascript
const rule = new Rule({
name: 'check availability',
Expand All @@ -133,6 +137,7 @@ const rule = new Rule({
```

Example: asynchronous action using promises

```javascript
const rule = new Rule({
name: 'check availability',
Expand All @@ -152,6 +157,7 @@ The extended rule simply inherits all the premises from its parents (and their p
Use the rule's `extend` property to set its parents.

Example: extended rule

```javascript
const baseRule = new Rule({
name: 'user lives in Germany',
Expand Down Expand Up @@ -180,6 +186,7 @@ However, if that solves your needs, you can consecutively run different sets of
Rules in different instances of Rools are perfectly isolated and can, of course, run against the same facts.

Example: evaluate different sets of rules on the same facts

```javascript
const facts = {...};
const rools1 = new Rools();
Expand Down Expand Up @@ -207,6 +214,7 @@ You are free to use references or just to repeat the same premise.
Both options are working fine.

Example 1: by reference

```javascript
const isApplicable = (facts) => facts.user.salery >= 2000;
const rule1 = new Rule({
Expand All @@ -226,6 +234,7 @@ const rule2 = new Rule({
```

Example 2: repeat premise

```javascript
const rule1 = new Rule({
when: [
Expand Down Expand Up @@ -312,6 +321,7 @@ Calling `new Rools()` creates a new Rools instance, i.e., a new rule engine.
You usually do this once for a given set of rules.

Example:

```javascript
constRools } = require('rools');
const rools = new Rools();
Expand Down Expand Up @@ -344,6 +354,7 @@ New rules will become effective immediately.
If this promise is rejected, the affected Rools instance is inconsistent and should no longer be used.

Example:

```javascript
constRools, Rule } = require('rools');
const ruleMoodGreat = new Rule({
Expand Down Expand Up @@ -372,6 +383,7 @@ await rools.register([ruleMoodGreat, ruleGoWalking]);

Facts are plain JavaScript or JSON objects or objects from ES6 classes with getters and setters.
For example:

```javascript
const user = {
name: 'frank',
Expand All @@ -395,10 +407,12 @@ If a premise (`when`) fails, `evaluate()` will still *not* fail (for robustness
If an action (`then`) fails, `evaluate()` will reject its promise.

If there is more than one rule ready to fire, Rools applies a *conflict resolution strategy* to decide which rule/action to fire first. The default conflict resolution strategy is 'ps'.
* 'ps' -- (1) priority, (2) specificity, (3) order of registration
* 'sp' -- (1) specificity, (2) priority, (3) order of registration

* 'ps' -- (1) priority, (2) specificity, (3) order of registration
* 'sp' -- (1) specificity, (2) priority, (3) order of registration

If you don't like the default 'ps', you can change the conflict resolution strategy like this:

```javascript
await rools.evaluate(facts, { strategy: 'sp' });
```
Expand Down Expand Up @@ -432,6 +446,28 @@ const rools = new Rools({
The error log reports failed actions or premises.
The debug log reports the entire evaluation process for debugging purposes.

## TypeScript

This package provides types for TypeScript.

```typescript
import { Rools, Rule } from "rools";

// ...
```

For this module to work, your TypeScript compiler options must include:

```json
{
"compilerOptions": {
"target": "ES2015", // or later
"moduleResolution": "node",
"esModuleInterop": true
}
}
```

## Migration

### Version 1.x.x to Version 2.x.x
Expand Down
86 changes: 86 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 9 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,19 @@
],
"scripts": {
"lint": "eslint . --ignore-path ./.eslintignore",
"test": "NODE_ENV=test nyc --reporter=lcov --reporter=text-summary mocha --exit --recursive test",
"test": "npm run test:unit && npm run test:typescript",
"test:unit": "nyc --reporter=lcov --reporter=text-summary mocha --exit --recursive test/**/*.spec.js",
"test:typescript": "mocha -r ts-node/register test/typescript/**/*.spec.ts",
"coveralls": "nyc report --reporter=lcovonly && cat ./coverage/lcov.info | coveralls",
"preversion": "npm run lint && npm test"
},
"engines": {
"node": ">=10.x.x"
},
"devDependencies": {
"@types/chai": "^4.2.14",
"@types/mocha": "^8.2.0",
"@types/node": "^14.14.19",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"coveralls": "^3.1.0",
Expand All @@ -39,7 +44,9 @@
"mocha": "^8.2.1",
"nyc": "^15.1.0",
"sinon": "^9.2.2",
"sinon-chai": "^3.5.0"
"sinon-chai": "^3.5.0",
"ts-node": "^9.1.1",
"typescript": "^4.1.3"
},
"dependencies": {
"arrify": "^2.0.1",
Expand Down
25 changes: 25 additions & 0 deletions test/typescript/typescript.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'mocha';
import { expect } from 'chai';
import { Rools, Rule } from "../..";

describe('Integration TypeScript', () => {
it('should work', async () => {
const facts: any = {
foo: false,
bar: true,
};
const rools = new Rools();
const rule = new Rule({
name: 'rule',
when: (facts: any) => {
return true
},
then: (facts: any) => {
facts.foo = true;
},
});
await rools.register([rule]);
await rools.evaluate(facts);
expect(facts.foo).to.be.equal(true);
});
});
7 changes: 7 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"target": "ES2015", /* 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
}
}

0 comments on commit dc0d40f

Please sign in to comment.