noolsjs / nools

Rete based rules engine written in javascript
http://noolsjs.com/
MIT License
950 stars 181 forks source link

Is it possible to define more complex objects for facts than just function() #117

Open twmobius opened 10 years ago

twmobius commented 10 years ago

I retrieve my facts from a database ORM and I was wondering if it possible to pass an object as fact definition.

At first adding an object returned an invalid param type, so I went on and changed in rule.js getParamType to allow for Objects. However it seems that when the rule is evaluated it doesn't match on the public fields.

It might be a problem with the object since it is rather complex, but I would like some pointers so that I can find where exactly it tries to invoke it.

doug-martin commented 10 years ago

Can you please provide an example of what you are trying to do.

twmobius commented 10 years ago

Well I am getting my facts from sequelize (http://sequelizejs.com/) which is an ORM for mySQL. So I thought it would be great to pass my ORM model down nools as a fact, change it by the rules and at the end update it back in the database.

The first problem was that the getParamType blocked the definition of an Object type. So I added it and now I am getting

TypeError: Expecting a function in instanceof check, but got [object Object]
    at wrapper.Constraint.extend.instance.assert (/home/mobius/app/node_modules/nools/lib/constraint.js:57:41)

And I am in the process of checking it out now.

twmobius commented 10 years ago

Well actually I've tested it, if you return true in the assert block instead of the check there, it will properly access the fact-model from the rule.

The error is somewhat confusing with regards to instanceof, and I am trying to figure out a better way to fix this.

doug-martin commented 10 years ago

So I may not be understanding but when creating an Model with sequelize you get a constructor back you can use that constructor as your type in the rules.

We do something similar in our baseline, we use patio but it should be comparable.

rule isLoggedIn {
   when {
         u: User u.isLoggedIn;
   }
   then {
      emit("isLoggedIn", u);
   }
}
var User = patio.addModel("user");

nools.compile(path.resolve(__dirname, "./rules/user.nools"), {
     define: {
        //The person class the flow should use
        User: User
     }
});

Does this help?

-Doug

twmobius commented 10 years ago

Hi Doug,

Well that's what I initially thought however sequelize returns (and I am saying that without being certain) something that looks like a factory, that in ORM terms in order to actually get the object you call something like Model.build().

When I first tried to apply the model to the definition, I got a TypeError:

Error: invalid param type [object Object]
    at /home/mobius/app/node_modules/nools/lib/rule.js:117:16
    at tester (/home/mobius/app/node_modules/nools/node_modules/is-extended/index.js:435:47)
    at Array.<anonymous> (/home/mobius/app/node_modules/nools/lib/rule.js:147:21)
    at Array.1 (/home/mobius/app/node_modules/nools/node_modules/is-extended/index.js:465:50)
    at tester (/home/mobius/app/node_modules/nools/node_modules/is-extended/index.js:427:49)
    at Object.createRule (/home/mobius/app/node_modules/nools/lib/rule.js:274:26)
    at exports.parse (/home/mobius/app/node_modules/nools/lib/compile/index.js:138:22)
    at /home/mobius/app/node_modules/nools/lib/compile/index.js:186:48
    at Array.forEach (native)
    at forEach (/home/mobius/app/node_modules/nools/node_modules/array-extended/index.js:176:21)

So I went in on the rule.js on the getParamType method and added a:

.isObject(function (object) {
  return object;
})

So that worked, and l then I got:

TypeError: Expecting a function in instanceof check, but got [object Object]
    at wrapper.Constraint.extend.instance.assert (/home/mobius/app/node_modules/nools/lib/constraint.js:58:41)
    at spreadArgs (/home/mobius/app/node_modules/nools/node_modules/function-extended/index.js:19:25)
    at wrapper.constraintAssert (/home/mobius/app/node_modules/nools/node_modules/function-extended/index.js:57:32)
    at wrapper.AlphaNode.extend.instance.assert (/home/mobius/app/node_modules/nools/lib/nodes/typeNode.js:8:22)
    at declare.instance.assertFact (/home/mobius/app/node_modules/nools/lib/nodes/index.js:74:30)
    at module.exports.declare.instance.assert (/home/mobius/app/node_modules/nools/lib/flow.js:63:27)
    at spreadArgs (/home/mobius/app/node_modules/nools/node_modules/function-extended/index.js:19:25)
    at null.assert (/home/mobius/app/node_modules/nools/node_modules/function-extended/index.js:57:32)
    at declare.instance.getSession (/home/mobius/app/node_modules/nools/lib/flowContainer.js:62:18)
    at /home/mobius/app/lib/engine.js:235:36

Looking into it at constraint.js it looks that the return param instanceof this.constraint || param.constructor === this.constraint; is failing on the param instanceof this.constraing but I've not been able to debug it further yet.

If I simply return true; (which in general is wrong) on the assert block everything seems to be working (I haven't run into any other problems... yet)

Btw is there a place where we could discuss other sort of issues with nools apart from opening issues here? (Cause I have a couple more :) )

Thank you for taking the time to reply back :)

doug-martin commented 10 years ago

So after looking into this a little more I found a way that works. When you call define your factory has a DAO attribute that can be used as the instance type so you can use that as the type when defining your rules.

Ill add an example using Sequelize and Patio to show how you can use nools with different orms.

twmobius commented 10 years ago

I will check it out. Thanks! Looking forward to the examples :)