diff --git a/examples/conways/assets/conways.css b/examples/conways/assets/conways.css
new file mode 100644
index 0000000..783d999
--- /dev/null
+++ b/examples/conways/assets/conways.css
@@ -0,0 +1,92 @@
+body {
+ font-size: 14px;
+ line-height: 24px;
+ font-weight: bold;
+ font-family: 'Londrina Shadow', cursive;
+}
+
+.container {
+ padding: 10px;
+ width: 600px;
+ margin-left: auto;
+ margin-right: auto;
+ -webkit-border-radius: 10px;
+ -moz-border-radius: 10px;
+ border-radius: 10px;
+ background: -webkit-gradient(linear, bottom, left 175px, from(#CCCCCC), to(#EEEEEE));
+ background: -moz-linear-gradient(bottom, #CCCCCC, #EEEEEE 175px);
+ border-top: 1px solid #999;
+ border-bottom: 1px solid #999;
+ border: inset 1px solid #333;
+ -webkit-box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.3);
+ -moz-box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.3);
+ box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.3);
+}
+
+.container > textarea {
+ width: 500px;
+ height: 120px;
+ border: 3px solid #cccccc;
+ padding: 5px;
+ font-family: Tahoma, sans-serif;
+ background-position: bottom right;
+ background-repeat: no-repeat;
+}
+
+.container > header {
+ text-align: center;
+ text-align: center;
+}
+
+hr {
+ margin: 10px;
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ -webkit-box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.3);
+ -moz-box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.3);
+ box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.3);
+}
+
+input[type="submit"], input[type="button"] {
+ width: 100px;
+ right: 20px;
+ bottom: 20px;
+ background: #09C;
+ color: #fff;
+ font-family: Tahoma, Geneva, sans-serif;
+ height: 30px;
+ -webkit-border-radius: 15px;
+ -moz-border-radius: 15px;
+ border-radius: 15px;
+ border: 1px solid #999;
+ margin: 10px;
+}
+
+#grid {
+
+ overflow: hidden;
+ width: 350px;
+ height: 360px;
+ margin-left: auto;
+ margin-right: auto;
+
+}
+
+.row {
+ display: table-row;
+}
+
+.col {
+ width: 10px;
+ height: 10px;
+ display: table-cell;
+ border: 1px solid #cbcbcb;
+ content: " "
+}
+
+.col.live {
+ background-color: black;
+}
+
+.col.dead {
+ background-color: white;
+}
diff --git a/examples/conways/index.html b/examples/conways/index.html
new file mode 100644
index 0000000..901f23b
--- /dev/null
+++ b/examples/conways/index.html
@@ -0,0 +1,126 @@
+
+
+
+
+
+ Conways
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Pattern
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/conways/index.js b/examples/conways/index.js
new file mode 100644
index 0000000..a98cc46
--- /dev/null
+++ b/examples/conways/index.js
@@ -0,0 +1,83 @@
+"use strict";
+
+var nools = require("../.."),
+ p = require("promise-extended"),
+ patterns = require("./patterns");
+
+
+var flow = nools.compile(__dirname + "/rules/conways.nools"),
+ Cell = flow.getDefined("Cell");
+
+var run = (function () {
+
+ var cells = [], rows = 30, cols = 30;
+
+ function print() {
+ var str = [];
+ console.log("\x1b[2J\x1b[H");
+ for (var i = 0; i < rows; i++) {
+ var row = [];
+ for (var j = 0; j < cols; j++) {
+ if (cells[i][j].state === "live") {
+ row.push("#");
+ } else {
+ row.push(" ");
+ }
+ }
+ str.push(row.join(" "));
+ }
+ console.log(str.join("\n"));
+ }
+
+ function addCell(row, col, cell) {
+ if (!cells[row]) {
+ cells[row] = [];
+ }
+ cells[row][col] = cell;
+ return cell;
+ }
+
+ function createPattern(pattern) {
+ var cell;
+ for (var i = 0; i < rows; i++) {
+ for (var j = 0; j < cols; j++) {
+ cell = addCell(i, j, new Cell());
+ if (pattern[i] && pattern[i][j]) {
+ cell.state = "live";
+ }
+ if (i > 0) {
+ cell.addNeighbor(cells[i - 1][j]);
+ if (j <= (cols - 2)) {
+ // neighbor to the northeast
+ cell.addNeighbor(cells[i - 1][j + 1]);
+ }
+ }
+ if (j > 0) {
+ // neighbor to the west
+ cell.addNeighbor(cells[i][j - 1]);
+ if (i > 0) {
+ // neighbor to the northwest
+ cell.addNeighbor(cells[i - 1][j - 1]);
+ }
+ }
+ }
+ }
+ return cell;
+ }
+
+ return function (pattern) {
+ createPattern(pattern);
+ var session = flow.getSession("populate", cells);
+ session.on("print", print);
+ return session.match().then(function () {
+ print();
+ session.dispose();
+ });
+ };
+}());
+
+var pattern = process.argv.slice(2)[0] || "hi";
+run(patterns[pattern]).addErrback(function (err) {
+ console.log(err.stack);
+});
+
diff --git a/examples/conways/patterns.json b/examples/conways/patterns.json
new file mode 100644
index 0000000..41331e9
--- /dev/null
+++ b/examples/conways/patterns.json
@@ -0,0 +1,89 @@
+{
+ "hi": [
+ [true, false, false, false, true, false, false, true, true, true, true, true, true, true],
+ [true, false, false, false, true, false, false, false, false, false, true, false, false, false],
+ [true, false, false, false, true, false, false, false, false, false, true, false, false, false],
+ [true, false, false, false, true, false, false, false, false, false, true, false, false, false],
+ [true, true, true, true, true, false, false, false, false, false, true, false, false, false],
+ [true, false, false, false, true, false, false, false, false, false, true, false, false, false],
+ [true, false, false, false, true, false, false, false, false, false, true, false, false, false],
+ [true, false, false, false, true, false, false, false, false, false, true, false, false, false],
+ [true, false, false, false, true, false, false, true, true, true, true, true, true, true]
+ ],
+
+ "border": [
+ [true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true],
+ [true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true]
+ ],
+
+ "pulsar": [
+ [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false],
+ [false, false, false, true, true, true, false, false, false, true, true, true, false, false, false],
+ [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false],
+ [false, true, false, false, false, false, true, false, true, false, false, false, false, true, false],
+ [false, true, false, false, false, false, true, false, true, false, false, false, false, true, false],
+ [false, true, false, false, false, false, true, false, true, false, false, false, false, true, false],
+ [false, false, false, true, true, true, false, false, false, true, true, true, false, false, false],
+ [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false],
+ [false, false, false, true, true, true, false, false, false, true, true, true, false, false, false],
+ [false, true, false, false, false, false, true, false, true, false, false, false, false, true, false],
+ [false, true, false, false, false, false, true, false, true, false, false, false, false, true, false],
+ [false, true, false, false, false, false, true, false, true, false, false, false, false, true, false],
+ [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false],
+ [false, false, false, true, true, true, false, false, false, true, true, true, false, false, false],
+ [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
+ ],
+
+ "glider": [
+ [false, false, false, false, false],
+ [false, false, true, false, false],
+ [false, false, false, true, false],
+ [false, true, true, true, false],
+ [false, false, false, false, false]
+ ],
+
+ "pentadecathalon": [
+ [true, true, true, true, true, true, true, true, true]
+ ],
+
+ "blinker": [
+ [false, true, false],
+ [false, true, false],
+ [false, true, false]
+ ],
+
+ "beacon": [
+ [false, true, true, false, false, false],
+ [false, true, true, false, false, false],
+ [false, false, false, true, true, false],
+ [false, false, false, true, true, false]
+ ]
+}
\ No newline at end of file
diff --git a/examples/conways/rules/conways.js b/examples/conways/rules/conways.js
new file mode 100644
index 0000000..d4d2b4c
--- /dev/null
+++ b/examples/conways/rules/conways.js
@@ -0,0 +1 @@
+(function(){ function _getCompiled(nools){ return nools.compile({"define":[{"name":"Cell","properties":"({\n state : \"dead\",\n evaluated: false,\n __queuedState : null,\n constructor: function(){\n this.neighbors = [];\n },\n\n numberOfLiveNeighbors: function(){\n var live = 0, neighbors = this.neighbors, neighbor;\n for(var i = 0, l = neighbors.length; i < l; i++){\n if(neighbors[i].state === \"live\"){\n live++;\n }\n }\n return live;\n },\n\n addNeighbor: function(neighbor){\n this.neighbors.push(neighbor);\n neighbor.neighbors.push(this);\n return this;\n },\n\n queueNextState: function(state){\n if(this.state !== state){\n this.__queuedState = state;\n }\n },\n\n transition: function(){\n var ret = false;\n if(this.__queuedState !== null){\n this.state = this.__queuedState;\n this.__queuedState = null;\n ret = true;\n }\n return ret;\n }\n\n})"}],"rules":[{"name":"Populate","options":{},"constraints":[["String","state","state == 'populate'"],["Array","cells"]],"action":"loop(cells, function(cell){\n assert(cell);\n })\n emit(\"evaluate\");\n retract(state);\n assert(\"evaluate\");\n "},{"name":"KillTheLonely","options":{},"constraints":[["String","state","state == 'evaluate'"],["Cell","c","c.state == 'live' && c.numberOfLiveNeighbors() < 2"]],"action":"c.queueNextState(\"dead\");\n "},{"name":"KillTheOverCrowded","options":{},"constraints":[["String","state","state == 'evaluate'"],["Cell","c","c.state == 'live' && c.numberOfLiveNeighbors() > 3"]],"action":"c.queueNextState(\"dead\");\n "},{"name":"GiveBirth","options":{},"constraints":[["String","state","state == 'evaluate'"],["Cell","c","c.state == 'dead' && c.numberOfLiveNeighbors() == 3"]],"action":"c.queueNextState(\"live\");\n "},{"name":"ShouldTransition","options":{},"constraints":[["String","state","state == 'evaluate'"]],"action":"retract(state);\n assert(\"transition\");\n "},{"name":"Transition","options":{},"constraints":[["Array","cells"],["String","state","state == 'transition'"]],"action":"retract(state);\n var transitioned = false;\n loop(cells, function(cell){\n retract(cell);\n if(cell.transition()){\n emit(\"cell-transition\", cell);\n transitioned = true;\n }\n });\n assert(transitioned ? \"populate\" : \"done\");\n "},{"name":"Done","options":{},"constraints":[["Array","cells"],["String","state","state == 'done'"]],"action":"console.log(\"Done\");\n "}],"scope":[{"name":"loop","body":"function(cells, cb){\n var rows = cells.length, cols = cells[0].length;\n for (var i = 0; i < rows; i++) {\n for (var j = 0; j < cols; j++) {\n cb(cells[i][j], i, j, cells);\n }\n }\n}"}]}, {name : "conways"}); } if ("undefined" !== typeof exports) { if ("undefined" !== typeof module && module.exports) { return _getCompiled(require("nools")); } } else if ("function" === typeof define && define.amd) { define(["nools"], function (nools) { return _getCompiled(nools); }); } else { _getCompiled(this.nools); } }).call(this);
diff --git a/examples/conways/rules/conways.nools b/examples/conways/rules/conways.nools
new file mode 100644
index 0000000..0ccb766
--- /dev/null
+++ b/examples/conways/rules/conways.nools
@@ -0,0 +1,137 @@
+define Cell {
+ state : "dead",
+ evaluated: false,
+ __queuedState : null,
+ constructor: function(){
+ this.neighbors = [];
+ },
+
+ numberOfLiveNeighbors: function(){
+ var live = 0, neighbors = this.neighbors, neighbor;
+ for(var i = 0, l = neighbors.length; i < l; i++){
+ if(neighbors[i].state === "live"){
+ live++;
+ }
+ }
+ return live;
+ },
+
+ addNeighbor: function(neighbor){
+ this.neighbors.push(neighbor);
+ neighbor.neighbors.push(this);
+ return this;
+ },
+
+ queueNextState: function(state){
+ if(this.state !== state){
+ this.__queuedState = state;
+ }
+ },
+
+ transition: function(){
+ var ret = false;
+ if(this.__queuedState !== null){
+ this.state = this.__queuedState;
+ this.__queuedState = null;
+ ret = true;
+ }
+ return ret;
+ }
+
+}
+
+function loop(cells, cb){
+ var rows = cells.length, cols = cells[0].length;
+ for (var i = 0; i < rows; i++) {
+ for (var j = 0; j < cols; j++) {
+ cb(cells[i][j], i, j, cells);
+ }
+ }
+}
+
+rule Populate {
+ when {
+ state: String state == 'populate';
+ cells: Array;
+ }
+ then {
+ loop(cells, function(cell){
+ assert(cell);
+ })
+ emit("evaluate");
+ retract(state);
+ assert("evaluate");
+ }
+}
+
+rule KillTheLonely{
+ when {
+ state: String state == 'evaluate';
+ c : Cell c.state == 'live' && c.numberOfLiveNeighbors() < 2;
+ }
+ then {
+ c.queueNextState("dead");
+ }
+}
+
+rule KillTheOverCrowded{
+ when {
+ state: String state == 'evaluate';
+ c : Cell c.state == 'live' && c.numberOfLiveNeighbors() > 3;
+ }
+ then {
+ c.queueNextState("dead");
+ }
+}
+
+rule GiveBirth{
+ when {
+ state: String state == 'evaluate';
+ c : Cell c.state == 'dead' && c.numberOfLiveNeighbors() == 3;
+ }
+ then {
+ c.queueNextState("live");
+ }
+}
+
+
+rule ShouldTransition {
+ when {
+ state: String state == 'evaluate';
+ }
+ then {
+ retract(state);
+ assert("transition");
+ }
+}
+
+rule Transition {
+ when {
+ cells: Array;
+ state: String state == 'transition';
+ }
+ then {
+ retract(state);
+ var transitioned = false;
+ loop(cells, function(cell){
+ retract(cell);
+ if(cell.transition()){
+ emit("cell-transition", cell);
+ transitioned = true;
+ }
+ });
+ assert(transitioned ? "populate" : "done");
+ }
+}
+
+
+rule Done {
+ when {
+ cells: Array;
+ state: String state == 'done';
+ }
+ then {
+ console.log("Done");
+ }
+}
+
diff --git a/examples/validator/assets/validator.css b/examples/validator/assets/validator.css
new file mode 100644
index 0000000..76e8341
--- /dev/null
+++ b/examples/validator/assets/validator.css
@@ -0,0 +1,71 @@
+form {
+ background: -webkit-gradient(linear, bottom, left 175px, from(#CCCCCC), to(#EEEEEE));
+ background: -moz-linear-gradient(bottom, #CCCCCC, #EEEEEE 175px);
+ margin: auto;
+ position: relative;
+ width: 550px;
+ height: 500px;
+ font-family: Tahoma, Geneva, sans-serif;
+ font-size: 14px;
+ font-style: italic;
+ line-height: 24px;
+ font-weight: bold;
+ color: #09C;
+ text-decoration: none;
+ -webkit-border-radius: 10px;
+ -moz-border-radius: 10px;
+ border-radius: 10px;
+ padding: 10px;
+ border: 1px solid #999;
+ border: inset 1px solid #333;
+ -webkit-box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.3);
+ -moz-box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.3);
+ box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.3);
+}
+
+.errors {
+ color: #f90008;
+}
+
+input {
+ margin-top: 30px;
+ margin-bottom: 20px;
+ width: 375px;
+ display: block;
+ border: 1px solid #999;
+ height: 40px;
+ font-size: 1.2em;
+ -webkit-box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.3);
+ -moz-box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.3);
+ box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.3);
+}
+
+input[type="submit"] {
+ width: 100px;
+ float: right;
+ right: 20px;
+ bottom: 20px;
+ background: #09C;
+ color: #fff;
+ font-family: Tahoma, Geneva, sans-serif;
+ height: 30px;
+ -webkit-border-radius: 15px;
+ -moz-border-radius: 15px;
+ border-radius: 15px;
+ border: 1px solid #999;
+}
+
+input[type="submit"]:hover {
+ background: #fff;
+ color: #09C;
+}
+
+textarea:focus, input:focus {
+ border: 1px solid #09C;
+}
+
+.container {
+ margin-left: auto;
+ margin-right: auto;
+ width: 400px;
+}
\ No newline at end of file
diff --git a/examples/validator/index.html b/examples/validator/index.html
new file mode 100644
index 0000000..5dff0c7
--- /dev/null
+++ b/examples/validator/index.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+ Validators
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/validator/index.js b/examples/validator/index.js
new file mode 100644
index 0000000..de2ff4b
--- /dev/null
+++ b/examples/validator/index.js
@@ -0,0 +1,36 @@
+"use strict";
+
+var nools = require("../..");
+
+var flow = nools.compile("./rules/validator.nools"),
+ Model = flow.getDefined("model");
+
+var models = [
+ new Model({id: 1}),
+ new Model({id: 2, firstName: "Bob"}),
+ new Model({id: 3, firstName: "Bob", lastName: "Yukon"}),
+ new Model({id: 4, firstName: "Bob", lastName: "Yukon", dob: new Date(2000, 10, 10)}),
+ new Model({id: 5, firstName: "Bob", lastName: "Yukon", dob: new Date(1980, 10, 10)}),
+ new Model({id: 6, firstName: "Bob", lastName: "Yukon", dob: new Date(1980, 10, 10), email: "bob"}),
+ new Model({id: 7, firstName: "Bob", lastName: "Yukon", dob: new Date(1980, 10, 10), email: "bob@yukon"}),
+ new Model({id: 8, firstName: "Bob", lastName: "Yukon", dob: new Date(1980, 10, 10), email: "bob@yukon.com"}),
+ new Model({id: 9, firstName: "Bob1", lastName: "Yukon", dob: new Date(1980, 10, 10), email: "bob1@yukon.com"}),
+ new Model({id: 10, firstName: "Bob", lastName: "Yukon1", dob: new Date(1980, 10, 10), email: "bob2@yukon.com"}),
+ new Model({id: 11, firstName: "Bobalicious", lastName: "Yukon", dob: new Date(1980, 10, 10), email: "bob3@yukon.com"}),
+ new Model({id: 12, firstName: "Sally", lastName: "GregorianCalendar", dob: new Date(1980, 10, 10), email: "sally@yukon.com"}),
+ new Model({id: 13, firstName: "Sally", lastName: "Yukon", dob: new Date(1980, 10, 10), email: "sally@yukon.com"})
+];
+
+var session = flow.getSession.apply(flow, models);
+session.match().then(function () {
+ models.forEach(function (m) {
+ if (m.errors.length) {
+ console.log("%s \nerrors : [ \n\t%s \n]", m, m.errors.join(",\n\t"));
+ } else {
+ console.log("%s is valid!", m);
+ }
+ });
+ session.dispose();
+}).addErrback(function (err) {
+ console.log(err.stack);
+ });
diff --git a/examples/validator/rules/validator.js b/examples/validator/rules/validator.js
new file mode 100644
index 0000000..f55f41c
--- /dev/null
+++ b/examples/validator/rules/validator.js
@@ -0,0 +1 @@
+(function(){ function _getCompiled(nools){ return nools.compile({"define":[{"name":"Model","properties":"({\n\n constructor: function(options){\n this.id = options.id;\n this.firstName = options.firstName;\n this.lastName = options.lastName;\n this.dob = options.dob;\n this.email = options.email;\n this.errors = [];\n },\n\n toString : function(){\n return [\"Model : [\", [this.id, this.firstName, this.lastName, this.dob, this.email].join(\":\"), \"]\"].join(\"\");\n }\n})"}],"rules":[{"name":"CheckFirstNameRequired","options":{},"constraints":[["Model","m","isUndefinedOrNull(m.firstName) or m.firstName.length eq 0"]],"action":"m.errors.push(\"First name is required\");\n "},{"name":"CheckFirstNameNoNumbers","options":{},"constraints":[["Model","m","(isString(m.firstName) and m.firstName like /[0-9]/)"]],"action":"m.errors.push(\"First name is must not contain numbers\");\n "},{"name":"CheckFirstNameLength","options":{},"constraints":[["Model","m","(isString(m.firstName) and m.firstName.length gt 10)"]],"action":"m.errors.push(\"First name cannot be longer than 10 characters\");\n "},{"name":"CheckLastNameRequired","options":{},"constraints":[["Model","m","isUndefinedOrNull(m.lastName) or m.lastName.length eq 0"]],"action":"m.errors.push(\"Last name is required\");\n "},{"name":"CheckLastNameNoNumbers","options":{},"constraints":[["Model","m","(isString(m.lastName) and m.lastName like /[0-9]/)"]],"action":"m.errors.push(\"Last name is must not contain numbers\");\n "},{"name":"CheckLastNameLength","options":{},"constraints":[["Model","m","(isString(m.lastName) and m.lastName.length gte 10)"]],"action":"m.errors.push(\"Last name cannot be longer than 10 characters\");\n "},{"name":"CheckDobRequired","options":{},"constraints":[["Model","m","isUndefinedOrNull(m.dob) or isDate(m.dob) == false"]],"action":"m.errors.push(\"Date of birth is required\");\n "},{"name":"CheckDobAgeToYoung","options":{},"constraints":[["Model","m","isDate(m.dob) and m.dob gte yearsAgo(18)"]],"action":"m.errors.push(\"Date Of Birth must be >= 18 years ago\");\n "},{"name":"CheckDobAgeToOld","options":{},"constraints":[["Model","m","isDate(m.dob) and m.dob lte yearsAgo(100)"]],"action":"m.errors.push(\"You cannot be older than 100 to use this app\");\n "},{"name":"CheckEmail","options":{},"constraints":[["Model","m","m.email notLike /^(?:[\\w\\!\\#\\$\\%\\&\\'\\*\\+\\-\\/\\=\\?\\^\\`\\{\\|\\}\\~]+\\.)*[\\w\\!\\#\\$\\%\\&\\'\\*\\+\\-\\/\\=\\?\\^\\`\\{\\|\\}\\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\-](?!\\.)){0,61}[a-zA-Z0-9]?\\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\\[(?:(?:[01]?\\d{1,2}|2[0-4]\\d|25[0-5])\\.){3}(?:[01]?\\d{1,2}|2[0-4]\\d|25[0-5])\\]))$/"]],"action":"m.errors.push(\"Invalid email address\");\n "},{"name":"CheckEmailUnique","options":{},"constraints":[["Model","m1","isString(m1.email)",{"id":"m1Id","email":"m1Email"}],["Model","m2","isString(m2.email) and m1Id neq m2.id and m2.email eq m1Email"]],"action":"m2.errors.push(\"Email is not unqiue\");\n "}],"scope":[]}, {name : "validator"}); } if ("undefined" !== typeof exports) { if ("undefined" !== typeof module && module.exports) { return _getCompiled(require("nools")); } } else if ("function" === typeof define && define.amd) { define(["nools"], function (nools) { return _getCompiled(nools); }); } else { _getCompiled(this.nools); } }).call(this);
diff --git a/examples/validator/rules/validator.nools b/examples/validator/rules/validator.nools
new file mode 100644
index 0000000..6bb7bc7
--- /dev/null
+++ b/examples/validator/rules/validator.nools
@@ -0,0 +1,123 @@
+
+define Model {
+
+ constructor: function(options){
+ this.id = options.id;
+ this.firstName = options.firstName;
+ this.lastName = options.lastName;
+ this.dob = options.dob;
+ this.email = options.email;
+ this.errors = [];
+ },
+
+ toString : function(){
+ return ["Model : [", [this.id, this.firstName, this.lastName, this.dob, this.email].join(":"), "]"].join("");
+ }
+}
+
+function isEmail(email){
+ return /^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/.test(email);
+}
+
+rule CheckFirstNameRequired {
+ when {
+ m : Model isUndefinedOrNull(m.firstName) or m.firstName.length eq 0;
+ }
+ then {
+ m.errors.push("First name is required");
+ }
+}
+
+rule CheckFirstNameNoNumbers {
+ when {
+ m : Model (isString(m.firstName) and m.firstName like /[0-9]/);
+ }
+ then {
+ m.errors.push("First name is must not contain numbers");
+ }
+}
+
+rule CheckFirstNameLength {
+ when {
+ m : Model (isString(m.firstName) and m.firstName.length gt 10);
+ }
+ then {
+ m.errors.push("First name cannot be longer than 10 characters");
+ }
+}
+
+
+rule CheckLastNameRequired {
+ when {
+ m : Model isUndefinedOrNull(m.lastName) or m.lastName.length eq 0;
+ }
+ then {
+ m.errors.push("Last name is required");
+ }
+}
+
+rule CheckLastNameNoNumbers {
+ when {
+ m : Model (isString(m.lastName) and m.lastName like /[0-9]/);
+ }
+ then {
+ m.errors.push("Last name is must not contain numbers");
+ }
+}
+
+rule CheckLastNameLength {
+ when {
+ m : Model (isString(m.lastName) and m.lastName.length gte 10);
+ }
+ then {
+ m.errors.push("Last name cannot be longer than 10 characters");
+ }
+}
+
+
+rule CheckDobRequired {
+ when {
+ m : Model isDate(m.dob) == false;
+ }
+ then {
+ m.errors.push("Date of birth is required");
+ }
+}
+
+rule CheckDobAgeToYoung {
+ when {
+ m : Model isDate(m.dob) and m.dob gte yearsAgo(18);
+ }
+ then {
+ m.errors.push("Date Of Birth must be >= 18 years ago");
+ }
+}
+
+rule CheckDobAgeToOld {
+ when {
+ m : Model isDate(m.dob) and m.dob lte yearsAgo(100);
+ }
+ then {
+ m.errors.push("You cannot be older than 100 to use this app");
+ }
+}
+
+rule CheckEmail {
+ when {
+ m : Model isEmail(m.email) == false;
+ }
+ then {
+ m.errors.push("Invalid email address");
+ }
+}
+
+rule CheckEmailUnique {
+ when {
+ m1 : Model isString(m1.email) {id:m1Id, email:m1Email};
+ m2 : Model isString(m2.email) and m1Id neq m2.id and m2.email eq m1Email;
+ }
+ then {
+ m2.errors.push("Email is not unqiue");
+ }
+}
+