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 + + + + + +
+
+

Conways Game Of Life

+
+ +
+ +
+ + +

+ 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"); + } +} +