rules-machine
rules-machine copied to clipboard
Returns Affect Subsequent Rules
This one may be by design, but it makes removal of an item via rules a little clumsy.
Perhaps there's a better example that can be added to the docs for removing an object from an array by rules that check properties of the objects.
Succeeds
import { ruleFactory } from '@elite-libs/rules-machine';
const things = [
{
name: "ABC",
value: "A Thing"
},
{
name: "DEF",
value: "A Stuff"
},
{
name: "GHI",
value: "A Thing"
},
{
name: "JKL",
value: "A Thing"
}];
const thingRules = ([
{
if: {
and: [
'CONTAINS(thing.name, ["ABC", "GHI"])',
'CONTAINS("thing", SPLIT(" ", LOWER(thing.value)))'
]
}, then: "keep = false"
},
{
if: 'LAST(SPLIT("", thing.name)) == "C"', then: 'keep = false'
},
{
return: "keep"
}
]);
const filterWithRules = ruleFactory(thingRules)
function isRateFiltered(thing: Record<string, string>): boolean {
const results = filterWithRules({
thing,
keep: true
});
return results;
}
console.dir(things.filter(isRateFiltered));
Fails
PARSER FAIL: Error: Expected string, found: undefined undefined
import { ruleFactory } from '@elite-libs/rules-machine';
const things = [
{
name: "ABC",
value: "A Thing"
},
{
name: "DEF",
value: "A Stuff"
},
{
name: "GHI",
value: "A Thing"
},
{
name: "JKL",
value: "A Thing"
}];
const thingRules = ([
{
if: {
and: [
'CONTAINS(thing.name, ["ABC", "GHI"])',
'CONTAINS("thing", SPLIT(" ", LOWER(thing.value)))'
]
}, then: "thing = false"
},
{
if: 'LAST(SPLIT("", thing.name)) == "C"', then: 'thing = false'
},
{
return: "thing"
}
]);
const filterWithRules = ruleFactory(thingRules)
function isRateFiltered(thing: Record<string, string>): boolean {
const results = filterWithRules({
thing
});
return results;
}
console.dir(things.filter(isRateFiltered));
Can you clarify what you mean by "returns affect subsequent rules"? It's not clear from your examples.
I'm not sure exactly where the error is coming from, but in your second example thing is being treated both as an object and as a boolean.
'CONTAINS(thing.name, ["ABC", "GHI"])'
then: "thing = false"
The following reproduces the error message in v1.4.9:
import { ruleFactory } from "@elite-libs/rules-machine";
const rule = 'SPLIT("", foo.bar)';
ruleFactory(rule)({ foo: true });
Closing because there is no bug here. The error message could certainly be more helpful, but that's a separate issue.
I think there may be a misunderstanding about the context the rules are run. The rules are run in sequence in the same mutable context. Your second example is doing something very much like this:
let thing = { foo: { bar: true } };
if (thing.foo.bar) thing = true;
// TypeError: Cannot read properties of undefined (reading 'bar')
if (thing.foo.bar) thing = "Whoops!";
Oh yea, @dara-rockwell, there's a trace option that details what is going on at each step, I can add more examples of that & its output.
@justsml @chhatch Yeah, that's what I figured it was doing.
Is there a more elegant way to decide that we're removing an item besides adding a flag alongside the item?
We'd like to use the rules engine to filter a list of items based on rules, but something like keep = false is a proprietary hack, and we'd like to avoid each usage of rules engine to do filtering re-implement the mechanism for removing an item in some other way.
import { ruleFactory } from '@elite-libs/rules-machine';
const things = [
{
name: "ABC",
value: "A Thing to Remove"
},
{
name: "DEF",
value: "A Stuff to Keep"
},
{
name: "GHI",
value: "A Thing to Remove"
},
{
name: "JKL",
value: "A Thing to Keep"
}];
const thingRules = ([
{
if: {
and: [
'CONTAINS(thing.name, ["ABC", "GHI"])',
'CONTAINS("thing", SPLIT(" ", LOWER(thing.value)))'
]
}, then: "keep = false"
},
{
return: "keep"
}
]);
const filterWithRules = ruleFactory(thingRules)
function isFiltered(thing: Record<string, string>): boolean {
const result = filterWithRules({
thing,
keep: true
});
return result;
}
console.dir(things.filter(isFiltered));
Ongoing work in #47 - with filter support hopefully soon. @chhatch crushing it over here 💯.
@dara-rockwell With the work that was done by @chhatch in https://github.com/elite-libs/rules-machine/pull/47, and the additional work in https://github.com/elite-libs/rules-machine/pull/51, this adds support to filtering with multiple conditions.
This changes your example to be the following:
...
const things = [
{
name: "ABC",
value: "A Thing to Remove"
},
{
name: "DEF",
value: "A Stuff to Keep"
},
{
name: "GHI",
value: "A Thing to Remove"
},
{
name: "JKL",
value: "A Thing to Keep"
}];
const thingRules = ([
{
filter: 'list',
run: { and: [
'CONTAINS(thing.name, ["ABC", "GHI"])',
'CONTAINS("thing", SPLIT(" ", LOWER(thing.value)))'
] },
set: 'results',
},
{ return: 'results' },
]);
...
const filterWithRules = ruleFactory(thingRules)
expect(filterWithRules({ list: things })).toEqual([
{
name: "DEF",
value: "A Stuff to Keep"
},
{
name: "JKL",
value: "A Thing to Keep"
}
])