{"action":"created","issue":{"url":"https://api.github.com/repos/anywhichway/rule-reactor/issues/11","repository_url":"https://api.github.com/repos/anywhichway/rule-reactor","labels_url":"https://api.github.com/repos/anywhichway/rule-reactor/issues/11/labels{/name}","comments_url":"https://api.github.com/repos/anywhichway/rule-reactor/issues/11/comments","events_url":"https://api.github.com/repos/anywhichway/rule-reactor/issues/11/events","html_url":"https://github.com/anywhichway/rule-reactor/issues/11","id":178408480,"number":11,"title":"rule re-firing incorrectly","user":{"login":"tve","id":39480,"avatar_url":"https://avatars.githubusercontent.com/u/39480?v=3","gravatar_id":"","url":"https://api.github.com/users/tve","html_url":"https://github.com/tve","followers_url":"https://api.github.com/users/tve/followers","following_url":"https://api.github.com/users/tve/following{/other_user}","gists_url":"https://api.github.com/users/tve/gists{/gist_id}","starred_url":"https://api.github.com/users/tve/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tve/subscriptions","organizations_url":"https://api.github.com/users/tve/orgs","repos_url":"https://api.github.com/users/tve/repos","events_url":"https://api.github.com/users/tve/events{/privacy}","received_events_url":"https://api.github.com/users/tve/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/anywhichway/rule-reactor/labels/bug","name":"bug","color":"fc2929"}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":3,"created_at":"2016-09-21T17:12:18Z","updated_at":"2016-09-22T08:50:22Z","closed_at":null,"body":"This issue may well be the same as #10, but just in case I'm opening a separate issue. When I run rule reactor and subsequently add new facts and re-run rule-reactor I see that old rule activations re-fire. I hope this is a bug and not the way it's supposed to work. Here is a simple example:\r\n\r\n```\r\nvar RuleReactor = require(\"rule-reactor\");\r\nvar reactor = new RuleReactor();\r\n\r\nfunction Person(r) { Object.assign(this, r); }\r\nfunction Home(r) { Object.assign(this, r); }\r\nglobal.Person = Person;\r\nglobal.Home = Home;\r\n\r\nLL = console.log;\r\n\r\nreactor.createRule(\"has-a-home\", 0, {p: Person, h: Home},\r\n        function(p, h) { return p.home_id == h.id; },\r\n        function(p, h) { LL(\"person\", p.name, \"has home\", h.id); }\r\n);\r\n\r\nreactor.createRule(\"stop\", -100, {},\r\n    function() { return true; },\r\n    function() { reactor.stop(); }\r\n);\r\n\r\nreactor.trace(0);\r\n\r\n// define objects\r\nfor (var i=0; i<4; i++) {\r\n        reactor.assert(new Person({name: \"p\"+i, home_id: i}));\r\n}\r\nfor (var i=0; i<4; i++) {\r\n        reactor.assert(new Home({id: i}));\r\n}\r\n\r\nfunction runReactor(cb) {\r\n        reactor.run(Infinity, false, function() {\r\n                console.log(\"Data Count: \", reactor.data.size);\r\n                console.log(\"Executions: \", reactor.run.executions);\r\n                console.log(\"RPS: \", reactor.run.rps);\r\n                Object.keys(reactor.rules).forEach(function(rulename) {\r\n                        var rule = reactor.rules[rulename];\r\n                        console.log(rule.name, \"-- matches:\"+rule.potentialMatches,\r\n                                \"tested:\"+rule.tested, \"activated:\"+rule.activated, \"fired:\"+rule.fired);\r\n                });\r\n                if (cb) cb();\r\n        });\r\n}\r\n\r\nLL(\"start\");\r\nrunReactor(function() {'\r\n        // now add one fact and re-run the reactor\r\n        LL(\"add\");\r\n        reactor.assert(new Home({id: 5}));\r\n        runReactor();\r\n});\r\n```\r\n\r\nA run looks as follows:\r\n```\r\n> node test2.js\r\nstart\r\nperson p3 has home 3\r\nperson p2 has home 2\r\nperson p1 has home 1\r\nperson p0 has home 0\r\nperson p0 has home 0\r\nperson p0 has home 0\r\nperson p0 has home 0\r\nData Count:  8\r\nExecutions:  8\r\nRPS:  1600\r\nhas-a-home -- matches:16 tested:4 activated:10 fired:7\r\nstop -- matches:0 tested:1 activated:1 fired:1\r\nadd\r\nperson p3 has home 3\r\nperson p2 has home 2\r\nperson p1 has home 1\r\nperson p0 has home 0\r\nData Count:  9\r\nExecutions:  5\r\nRPS:  1250\r\nhas-a-home -- matches:20 tested:5 activated:14 fired:11\r\nstop -- matches:0 tested:2 activated:2 fired:2\r\n```\r\nI did not expect any rule to fire after the \"add\".\r\n"},"comment":{"url":"https://api.github.com/repos/anywhichway/rule-reactor/issues/comments/248845830","html_url":"https://github.com/anywhichway/rule-reactor/issues/11#issuecomment-248845830","issue_url":"https://api.github.com/repos/anywhichway/rule-reactor/issues/11","id":248845830,"user":{"login":"anywhichway","id":4958795,"avatar_url":"https://avatars.githubusercontent.com/u/4958795?v=3","gravatar_id":"","url":"https://api.github.com/users/anywhichway","html_url":"https://github.com/anywhichway","followers_url":"https://api.github.com/users/anywhichway/followers","following_url":"https://api.github.com/users/anywhichway/following{/other_user}","gists_url":"https://api.github.com/users/anywhichway/gists{/gist_id}","starred_url":"https://api.github.com/users/anywhichway/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/anywhichway/subscriptions","organizations_url":"https://api.github.com/users/anywhichway/orgs","repos_url":"https://api.github.com/users/anywhichway/repos","events_url":"https://api.github.com/users/anywhichway/events{/privacy}","received_events_url":"https://api.github.com/users/anywhichway/received_events","type":"User","site_admin":false},"created_at":"2016-09-22T08:50:22Z","updated_at":"2016-09-22T08:50:22Z","body":"With respect to: \"Related to this bug, the semantics of modifying a fact are not actually clear to me at all. I suspect that the theoretical formulation of a rules engine doesn't support fact modification, only assertion and retraction. If I consider a rule R with a condition Rc and an action Ra, plus a fact F1, then I believe it's clear that if I want to modify fact F1 I have to retract F1 and assert F2. When F2 is asserted it is irrelevant whether Rc was evaluated on F1 or Ra was fired on F1. F2 simply gets evaluated as a new stand-alone fact.\" Most rule engines do support fact modification. In your example above there is no need to retract F1, just assert F2 and the rule will fire again.\r\n\r\nWith respect to: \"If fact modification is allowed (as in rule-reactor) then things are not very clear to me. Suppose I assert F1 with attributes a1 and a2 (F1.a1, F1.a2). Suppose Rc checks F1.a1 and returns true, then Ra is fired on F1. I now modify F1.a1. It is clear that Rc needs to be reevaluated. Suppose it returns true again, then is Ra fired again because this is a new \"version\" of F1 or is it not fired again because Ra has already been run on F1?\" It should be fired again because the rule engine reacts to changes at the attribute level. Think of each object as though it were represented as a series of facts (objectid attribute value), e.g. (1 name \"joe\"),(1 age 21),(2 name \"mary\"),(2 age 20). The addition of or changes to facts will should rules to fire, e.g. (1 name \"joey\"),(3 name \"bill\"). In fact, the early rule engines this is exactly how object were represented.\r\n\r\nWith respect to \"Suppose I now modify F1.a2. Rc does not need to be reevaluated because it does not check F1.a2. But Rc is true on F1. Is Ra fired again because this is a new version of F1 or is it not fired again because Ra has already been run on F1?\" Ra should not be fired again. Ra should only fire if Rc is relevant and returns true.\r\n\r\nWith respect to: \"Now suppose I modify F1.a1 twice: the first time it renders Rc false, the second time it renders Rc true again. Is Ra fired again on this second modification because the evaluation of Rc transitioned from false to true or is it not fired again because Ra was fired on F1 before and an action only ever gets fired once on any fact?\" Ra should fire again. Once again, think of the (object attribute values) as facts and the objects themselves simply as convenient containers.\r\n\r\nPerhaps a little explanation regarding rule-reactor internals will help:\r\n\r\n1) Every rule is \"compiled\" so that its domain (class) and range (properties that get tested) are known. During this compilation the classes prototypes of domains are modified to note which properties on instances are referenced by any rule at all. These are called \"activeKeys\". The prototypes are also modified to track which rules reference the \"activeKeys\".\r\n2) For each object asserted all active keys are are modified using Object.defineProperty so that changes to their values force re-evaluation of the rules relevant to the key modified. If the re-evaluation returns true, then the action is put on an agenda (queue) in the form of an Activation for potential invocation. The Activations keep track of which data combinations they exist to represent so that if the data changes they can be removed from the agenda without the action being invoked.\r\n\r\nBased on your questions in this issue and other issues, we are suspicious you have either uncovered a bug or that compilation is occurring incorrectly as a nuance of how you have implemented your classes. We get a sense that despite your self skepticism, you do have a sense of the correct behavior. We will be investigating further over the next few days.\r\n\r\n"}}
{"actor":{"display_login":"anywhichway"}}