Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Returns Affect Subsequent Rules #35

Open
dara-rockwell opened this issue Sep 23, 2022 · 7 comments
Open

Returns Affect Subsequent Rules #35

dara-rockwell opened this issue Sep 23, 2022 · 7 comments

Comments

@dara-rockwell
Copy link

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));
@chhatch
Copy link
Collaborator

chhatch commented Sep 24, 2022

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"

@chhatch
Copy link
Collaborator

chhatch commented Sep 24, 2022

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.

@chhatch chhatch closed this as not planned Won't fix, can't repro, duplicate, stale Sep 24, 2022
@chhatch chhatch reopened this Sep 24, 2022
@chhatch
Copy link
Collaborator

chhatch commented Sep 24, 2022

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!";

@justsml
Copy link
Contributor

justsml commented Sep 26, 2022

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.

@dara-rockwell
Copy link
Author

dara-rockwell commented Sep 26, 2022

@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));

@justsml
Copy link
Contributor

justsml commented Oct 31, 2022

Ongoing work in #47 - with filter support hopefully soon. @chhatch crushing it over here 💯.

@cjbt
Copy link
Collaborator

cjbt commented Feb 14, 2023

@dara-rockwell With the work that was done by @chhatch in #47, and the additional work in #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"
  }
])

@cjbt cjbt assigned cjbt and unassigned cjbt Feb 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants