Skip to content

Commit

Permalink
# This is a combination of 2 commits.
Browse files Browse the repository at this point in the history
# The first commit's message is:

Implement no-loop rule attribute

Fixes issue with using grave marks for dsl strings in test file: noloop.test.js

Implements extends functionality in the dsl

These files address noolsjs#162 , the extends keyword, which allows you to
reference other types in either external or other defines as a base
type.

Revert "Implements extends functionality in the dsl"

This reverts commit 1259c3d.

removed gitattributs/gitignore from branch

# This is the 2nd commit message:

Fixes issue with using grave marks for dsl strings in test file: noloop.test.js
  • Loading branch information
markbjerke committed Jan 26, 2016
1 parent 61d427f commit a2429b3
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 5 deletions.
4 changes: 0 additions & 4 deletions .gitignore

This file was deleted.

10 changes: 9 additions & 1 deletion lib/agenda.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ module.exports = declare(EventEmitter, {

register: function (node) {
var agendaGroup = node.rule.agendaGroup;
this.rules[node.name] = {tree: new AVLTree({compare: this.comparator}), factTable: new FactHash()};
this.rules[node.name] = { tree: new AVLTree({ compare: this.comparator }), factTable: new FactHash(), noLoop: {} };
if (agendaGroup) {
this.addAgendaGroup(agendaGroup);
}
Expand Down Expand Up @@ -162,6 +162,14 @@ module.exports = declare(EventEmitter, {

insert: function (node, insert) {
var rule = this.rules[node.name], nodeRule = node.rule, agendaGroup = nodeRule.agendaGroup;
if (nodeRule.noLoop) {
if (rule.noLoop[insert.hashCode]) {
return;
}
else {
rule.noLoop[insert.hashCode] = true;
}
}
rule.tree.insert(insert);
this.getAgendaGroup(agendaGroup).insert(insert);
if (agendaGroup) {
Expand Down
18 changes: 18 additions & 0 deletions lib/parser/nools/tokens.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,20 @@ var ruleTokens = {
};
})(),

noLoop: (function () {
var noLoopRegexp = /^(noLoop|no-loop)\s*:\s*(-?true|false)\s*[,;]?/;
return function (src, context) {
if (noLoopRegexp.test(src)) {
var parts = src.match(noLoopRegexp);
//
context.options.noLoop = Boolean(parts[2])
return src.replace(parts[0], "");
} else {
throw new Error("invalid format");
}
};
})(),

"agenda-group": function () {
return this.agendaGroup.apply(this, arguments);
},
Expand All @@ -102,6 +116,10 @@ var ruleTokens = {
return this.autoFocus.apply(this, arguments);
},

"no-loop": function () {
return this.noLoop.apply(this, arguments);
},

priority: function () {
return this.salience.apply(this, arguments);
},
Expand Down
1 change: 1 addition & 0 deletions lib/rule.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ var Rule = declare({
this.name = name;
this.pattern = pattern;
this.cb = cb;
this.noLoop = options.noLoop;
if (options.agendaGroup) {
this.agendaGroup = options.agendaGroup;
this.autoFocus = extd.isBoolean(options.autoFocus) ? options.autoFocus : false;
Expand Down
28 changes: 28 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Or [download the source](https://raw.github.com/C2FO/nools/master/nools.js) ([mi
* [Structure](#rule-structure)
* [Salience](#rule-salience)
* [Scope](#rule-scope)
* [no-loop](#rule-no-loop)
* [Constraints](#constraints)
* [Not](#not-constraint)
* [Or](#or-constraint)
Expand Down Expand Up @@ -1001,6 +1002,33 @@ flow1
});
```

<a name="rule-no-loop"></a>
### No-Loop

When a rule's action modifies a fact it may cause the rule to activate again, causing an infinite loop. Setting no-loop to true will skip the creation of another Activation for the rule with the current set of facts.

```javascript
this.rule("Hello", {noLoop: true}, [Message, "m", "m.text like /hello/"], function (facts) {
var m = facts.m;
m.text = 'hello world';
this.modify(m)
});

```
Or using the DSL

```javascript
rule Hello {
no-loop: true;
when {
m: Message m.name like /hello/;
}
then { modify(m, function() {
m.text = 'hello world'
});
}
```
<a name="rule-scope"></a>
Expand Down
67 changes: 67 additions & 0 deletions test/flow/noLoop.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"use strict";
var it = require("it"),
assert = require("assert"),
nools = require("../../");

it.describe("no-loop", function (it) {
/*jshint indent*/
function Message(name) {
this.name = name;
}
var cnt = 0;

var flow1 = nools.flow("noLoop1", function () {

this.rule("Hello2", { noLoop: true }, [Message, "m", "m.name =~ /Hello/"], function (facts) {
var m = facts.m;
m.name = 'Hello World';
this.modify(m);
});
}),

flow2 = nools.flow("noLoop2", function () {

this.rule("Hello1", [Message, "m", "m.name =~ /Hello/"], function (facts) {
var m = facts.m;
if (cnt++ < 2) {
m.name = 'Hello World';
this.modify(m);
}
});
});

var noolsSource = "rule 'Hello3' { no-loop: true; when {m: Message m.name =~/Hello/;}then {modify(m, function () { this.name = 'Hello World'; });}}";

var flow3 = nools.compile(noolsSource, {
name: 'testDsl'
,define: {
Message: Message
}
});

it.should("not loop with option on and loop otherwise", function () {
var fired1 = [], fired2 = [], fired3 = [];
var session1 = flow1.getSession(new Message("Hello")).on("fire", function (name) {
fired1.push(name);
}),
session2 = flow2.getSession(new Message("Hello")).on("fire", function (name) {
fired2.push(name);
}),
session3 = flow3.getSession(new Message("Hello")).on("fire", function (name) {
fired3.push(name);
});
return session1.match()
.then(function () {
return session2.match().then(function () {
return session3.match().then(function () {
})
})
})
.then(function () {
assert.deepEqual(fired1, ["Hello2"]);
assert.deepEqual(fired2, ["Hello1", "Hello1", "Hello1"]);
assert.deepEqual(fired3, ["Hello3"]);
});
});

});

0 comments on commit a2429b3

Please sign in to comment.