Skip to content

Commit

Permalink
Merge pull request #20 from nm/release/2.1.0
Browse files Browse the repository at this point in the history
Release/2.1.0
  • Loading branch information
eisenivan authored and GitHub Enterprise committed Jun 15, 2018
2 parents 15e15b5 + b29f84f commit 2244a7a
Show file tree
Hide file tree
Showing 15 changed files with 434 additions and 22 deletions.
54 changes: 47 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ Both `left` and `right` support lookup values. Please visit [the Lodash.get docs

Regent's built in predicates are:

`dateAfterInclusive`, `dateBeforeInclusive`, `deepEquals`, `empty`, `equals`, `greaterThan`, `includes`, `lessThan`, `regex`, `typeOf`
`dateAfterInclusive`, `dateBeforeInclusive`, `deepEquals`, `empty`, `equals`, `greaterThan`, `greaterThanOrEquals`, `includes`, `lessThan`, `lessThanOrEquals`, `regex`, `typeOf`

You can learn more about predicates in the [Predicates](#predicates) section of the docs.

Expand Down Expand Up @@ -507,27 +507,27 @@ Other notable use cases of a custom predicate could include custom date formatti
`explain` was built to help a developer visualize their logic. Because we are defining small rules and composing them together, a rule abstracts away the actual logic check. Running the rule through explain returns the logic in a human readable form. Check out this example.

```javascript
const IS_RAINING = { left: '@precipitation', fn: 'includes', right: 'snow' };
const IS_RAINING = { left: '@precipitation', fn: 'includes', right: 'rain' };
const IS_SNOWING = { left: '@precipitation', fn: 'includes', right: 'snow' };
const PRECIPITATION = and(IS_RAINING, IS_SNOWING);
const PRECIPITATION = or(IS_RAINING, IS_SNOWING);

explain(PRECIPITATION)
// => ​​​​​((@precipitation includes "snow") and (@precipitation includes "snow"))​​​​​
// => ​​​​​((@precipitation includes "rain") or (@precipitation includes "snow"))​​​​​
```

`explain` also accepts an optional data object as a second argument. When provided explain will show the actual values of the lookup keys in the explanation.

```javascript
const IS_RAINING = { left: '@precipitation', fn: 'includes', right: 'snow' };
const IS_RAINING = { left: '@precipitation', fn: 'includes', right: 'rain' };
const IS_SNOWING = { left: '@precipitation', fn: 'includes', right: 'snow' };
const PRECIPITATION = and(IS_RAINING, IS_SNOWING);
const PRECIPITATION = or(IS_RAINING, IS_SNOWING);

const data = {
precipitation: ['sleet', 'hail']
};

explain(PRECIPITATION, data)
// => ​​​​​((@precipitation->["sleet","hail"] includes "snow") and (@precipitation->["sleet","hail"] includes "snow"))
// => ​​​​​((@precipitation->["sleet","hail"] includes "rain") or (@precipitation->["sleet","hail"] includes "snow"))
```

# API Reference
Expand Down Expand Up @@ -650,6 +650,26 @@ const data = {
{ left: '@highTemp', fn: 'greaterThan', right: '@currentTemp' } // true
```

### greaterThanOrEquals

Uses the [greater than or equal operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators) and returns true if `left` is greater than or equal to `right`.

```javascript
const data = {
currentTemp: 75,
highTemp: 72
}

{ left: '@highTemp', fn: 'greaterThanOrEquals', right: '@currentTemp' } // true

const data = {
currentTemp: 72,
highTemp: 72
}

{ left: '@highTemp', fn: 'greaterThanOrEquals', right: '@currentTemp' } // true
```

### includes

Uses [lodash.includes](https://lodash.com/docs/4.17.5#includes) to check if `right` is in `left`.
Expand All @@ -673,6 +693,26 @@ const data = {
{ left: '@currentTemp', fn: 'lessThan', right: '@highTemp' } // true
```

### lessThanOrEquals

Uses the [less than or equal operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators) and returns true if `left` is less than or equal to `right`.

```javascript
const data = {
currentTemp: 60,
highTemp: 68
}

{ left: '@currentTemp', fn: 'lessThanOrEquals', right: '@highTemp' } // true

const data = {
currentTemp: 68,
highTemp: 68
}

{ left: '@currentTemp', fn: 'lessThanOrEquals', right: '@highTemp' } // true
```

### regex

Tests `left` against the regex in `right`. Uses [RegExp.prototype.test()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test).
Expand Down
6 changes: 2 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "regent",
"version": "2.0.3",
"version": "2.1.0",
"description": "Javascript logic framework",
"main": "lib/regent.js",
"scripts": {
Expand Down Expand Up @@ -64,9 +64,7 @@
},
"nyc": {
"exclude": [
"**/*styles.js",
"**/*spec.js",
"src/polyfills/*"
"**/*spec.js"
]
}
}
4 changes: 4 additions & 0 deletions src/fn.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import deepEquals from './functions/deep-equals';
import empty from './functions/empty';
import equals from './functions/equals';
import greaterThan from './functions/greater-than';
import greaterThanOrEquals from './functions/greater-than-equals';
import includes from './functions/includes';
import lessThan from './functions/less-than';
import lessThanOrEquals from './functions/less-than-equals';
import regex from './functions/regex';
import typeOf from './functions/type-of';

Expand All @@ -16,8 +18,10 @@ const fn = {
empty,
equals,
greaterThan,
greaterThanOrEquals,
includes,
lessThan,
lessThanOrEquals,
regex,
typeOf,
};
Expand Down
1 change: 1 addition & 0 deletions src/functions/greater-than-equals.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default (left, right) => left >= right;
100 changes: 100 additions & 0 deletions src/functions/greater-than-equals.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import test from 'tape';
import greaterThanOrEquals from './greater-than-equals';

test('greaterThanOrEquals should be a function', (assert) => {
assert.equal(typeof greaterThanOrEquals, 'function');
assert.end();
});

test('greaterThan should return true if the value given is greater than the param', (assert) => {
let actual = greaterThanOrEquals(1, 0);
let expected = true;
assert.equal(actual, expected);

actual = greaterThanOrEquals(4, 3);
expected = true;
assert.equal(actual, expected);

actual = greaterThanOrEquals(4.2, 4.1);
expected = true;
assert.equal(actual, expected);

actual = greaterThanOrEquals(4, 4);
expected = true;
assert.equal(actual, expected);

actual = greaterThanOrEquals(4.2, 4.2);
expected = true;
assert.equal(actual, expected);

assert.end();
});

test('greaterThan should return true if the value given is equal to the param', (assert) => {
let actual = greaterThanOrEquals(1, 1);
let expected = true;
assert.equal(actual, expected);

actual = greaterThanOrEquals(4, 4);
expected = true;
assert.equal(actual, expected);

actual = greaterThanOrEquals(4.2, 4.2);
expected = true;
assert.equal(actual, expected);

assert.end();
});

test('greaterThanOrEquals should return false if the value given is less than the param', (assert) => {
let actual = greaterThanOrEquals(1, 2);
let expected = false;
assert.equal(actual, expected);

actual = greaterThanOrEquals(0, 10);
expected = false;
assert.equal(actual, expected);

actual = greaterThanOrEquals(null, 4);
expected = false;
assert.equal(actual, expected);

assert.end();
});

test('greaterThanOrEquals should return false if the key is undefined', (assert) => {
const actual = greaterThanOrEquals(undefined, 0);
const expected = false;
assert.equal(actual, expected);
assert.end();
});

test('greaterThanOrEquals should return false if the key is a string that parses to NaN', (assert) => {
const redProps = [
'cool',
'some string',
'undefined',
'null',
'false',
'true',
];
redProps.forEach((val) => {
const actual = greaterThanOrEquals(val, 0);
const expected = false;
assert.equal(actual, expected, `should return false when passed ${val}`);
});
assert.end();
});

test('greaterThanOrEquals should compare strings based on standard lexicographical ordering, using Unicode values', (assert) => {
assert.equal(greaterThanOrEquals('a', 'b'), false, 'case 1');
assert.equal(greaterThanOrEquals('aaaa', 'abaa'), false, 'case 2');
assert.equal(greaterThanOrEquals('bb', 'a'), true, 'case 3');
assert.equal(greaterThanOrEquals('baa', 'abb'), true, 'case 4');
assert.equal(greaterThanOrEquals('1', 2), false, 'case 5');
assert.equal(greaterThanOrEquals('2', 1), true, 'case 6');
assert.equal(greaterThanOrEquals('2', '4'), false, 'case 7');
assert.equal(greaterThanOrEquals('2', '2'), true, 'case 8');
assert.equal(greaterThanOrEquals(4, 4), true, 'case 9');
assert.end();
});
4 changes: 4 additions & 0 deletions src/functions/greater-than.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ test('greaterThan should return false if the value given is less than or equal t
expected = false;
assert.equal(actual, expected);

actual = greaterThan(4, 4);
expected = false;
assert.equal(actual, expected);

assert.end();
});

Expand Down
1 change: 1 addition & 0 deletions src/functions/less-than-equals.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default (left, right) => left <= right;
58 changes: 58 additions & 0 deletions src/functions/less-than-equals.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import test from 'tape';
import lessThanOrEquals from './less-than-equals';

test('lessThanOrEquals should be a function', (assert) => {
assert.equal(typeof lessThanOrEquals, 'function');
assert.end();
});

test('lessThanOrEquals should return true if "left" is less than "right"', (assert) => {
const actual = lessThanOrEquals(4, 5);
const expected = true;
assert.equal(actual, expected);
assert.end();
});

test('lessThanOrEquals should return true if "left" equals "right"', (assert) => {
const actual = lessThanOrEquals(5, 5);
const expected = true;
assert.equal(actual, expected);
assert.end();
});

test('lessThanOrEquals should return false if "left" is greater than "right', (assert) => {
const actual = lessThanOrEquals(255, 254);
const expected = false;
assert.equal(actual, expected);
assert.end();
});

test('lessThanOrEquals should return false if the key is a string that parses to NaN', (assert) => {
const redProps = [
'cool',
'some string',
'undefined',
'null',
'false',
'true',
];
redProps.forEach((val) => {
const actual = lessThanOrEquals(val, 0);
const expected = false;
assert.equal(actual, expected, `should return false when passed ${val}`);
});
assert.end();
});

test('lessThanOrEquals should compare strings based on standard lexicographical ordering, using Unicode values', (assert) => {
assert.equal(lessThanOrEquals('a', 'b'), true, 'case 1');
assert.equal(lessThanOrEquals('aaaa', 'abaa'), true, 'case 2');
assert.equal(lessThanOrEquals('bb', 'a'), false, 'case 3');
assert.equal(lessThanOrEquals('baa', 'abb'), false, 'case 4');
assert.equal(lessThanOrEquals('1', 2), true, 'case 5');
assert.equal(lessThanOrEquals('2', 1), false, 'case 6');
assert.equal(lessThanOrEquals('2', '4'), true, 'case 7');
assert.equal(lessThanOrEquals('4', '4'), true, 'case 8');
assert.equal(lessThanOrEquals(4, '4'), true, 'case 9');
assert.end();
});
24 changes: 24 additions & 0 deletions src/functions/less-than.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,30 @@ test('lessThan should return false if "left" is equal to "right"', (assert) => {
assert.end();
});

test('lessThan should return false if the key is undefined', (assert) => {
const actual = lessThan(undefined, 0);
const expected = false;
assert.equal(actual, expected);
assert.end();
});

test('lessThan should return false if the key is a string that parses to NaN', (assert) => {
const redProps = [
'cool',
'some string',
'undefined',
'null',
'false',
'true',
];
redProps.forEach((val) => {
const actual = lessThan(val, 0);
const expected = false;
assert.equal(actual, expected, `should return false when passed ${val}`);
});
assert.end();
});

test('lessThan should compare strings based on standard lexicographical ordering, using Unicode values', (assert) => {
assert.equal(lessThan('a', 'b'), true, 'case 1');
assert.equal(lessThan('aaaa', 'abaa'), true, 'case 2');
Expand Down
11 changes: 9 additions & 2 deletions src/impl.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import test from 'tape';
import { evaluate, and, or, explain, not, filter, find } from './index';
import { evaluate, and, or, xor, explain, not, filter, find } from './index';

// An example of using Regent without custom predicates
test('Implement Regent without init()', (assert) => {
Expand All @@ -18,8 +18,15 @@ test('Implement Regent without init()', (assert) => {
assert.equal(explain(NO_PRECIPITATION), '(NOT (@precipitation includes "rain") and NOT (@precipitation includes "snow"))', 'Explain should work for NOT rules');

const SHOULD_WEAR_COAT = or(IS_RAINING, IS_SNOWING, IS_COLD);

assert.true(evaluate(SHOULD_WEAR_COAT, data), 'Should Wear Coat should return true');

const WET_BUT_NOT_TOO_WET = xor(IS_RAINING, IS_SNOWING);
assert.true(evaluate(WET_BUT_NOT_TOO_WET, data), 'Should return true');

assert.throws(() => xor(), 'XOR with 0 arguments should throw.');
assert.throws(() => xor(IS_RAINING), 'XOR with 1 argument should throw.');
assert.throws(() => xor(IS_RAINING, IS_SNOWING, IS_COLD), 'XOR with more than 2 arguments should throw.');

const explanation = explain(SHOULD_WEAR_COAT, data);
assert.equal(explanation, '((@precipitation->["rain"] includes "rain") or (@precipitation->["rain"] includes "snow") or (@temperature->78 lessThan 75))', 'Regent explain is not working properly');

Expand Down
Loading

0 comments on commit 2244a7a

Please sign in to comment.