Gambit is a simple class that implements Declarative Programming. Declarative Programming allows abstracting away control flow for stating what outcomes are desired. The rules contract attempts to follow a more General Purpose Language to offload learning additional terms for development and better conceptualize outcomes. Gambit utilizes standard programming operators along with logic gates for more complex operations. Preconfigured definitions allows for evaluating operations without long blocks of complex control flow. This can limit tightly coupled business logic in an application. If/Else and Case Statements become instructions, focus on resolutions rather than complex business rules.
- CaC is a concept of storing application configuration along side code. An example would be feature toggling.
- Dynamic Forms
- Other business logic requiring deep object analysis and control flow
Syntax | Description |
---|---|
stricEquals | any property === |
equals | any property == |
startsWith | string property starts with |
endsWith | string property ends with |
contains | array or string property contains any value |
greaterThan | string or number property > |
greaterThanOrEqualTo | string or number property >= |
lessThanOrEqualTo | string or number property <= |
lessThan | string or number property < |
Syntax | Description | Total Clauses |
---|---|---|
AND | The result is true if all clauses are true. | any |
OR | The Result is true if any clause is true. | any |
XOR | Acts as an "either/or". The result true if either clause is true but not both. | 2 |
NOT | Acts as an inverter. The result of an individual clause is reversed. | 1 |
NAND | Acts as an AND followed by a NOT. The result is false if both clauses are true otherwise it is true. | 2 |
NOR | Acts as an OR with an inverter. The result is true if both clauses are false otherwise it is false. | 2 |
XNOR | Acts as a XOR with an inverter. The result is true if all clauses are the same otherwise it is false. | any |
import { gambit, Rules } from "Gambit";
const rules: Rules = [
{
clauses: [
{
variable: "environment",
operator: "equals",
value: "Production"
}
],
gate: "NOT",
assignment: "allNonProdFlagsEnabled"
},
{
clauses: [
{
variable: "project",
operator: "equals",
values: "CMS",
},
{
variable: "key",
operator: "equals",
values: "Cache",
},
{
variable: "environment",
operator: "contains",
values: "Prod",
},
],
gate: "AND",
assignment: "cacheIsEnabled",
}
];
const gambit = new Gambit(rules);
const getActiveFlags = async (): Promise<string[]> => {
const gambit = new Gambit(rules);
let flags: any[] = [];
await fetch("/featureApi/v1/toggles")
.then((response) => response.json())
.then((data) => {
flags = data;
});
return flags.map((flag) => gambit.evaluate(flag)?.assignment);
};
When working with values, values can accept any value or an array of values. When comparing an array against another array this should be created as a multidimentional array.
const someRule = [
{
clauses: [
{
variable: "array",
operator: "equals",
values: ["foo", "bar"]// this will compare each value in the array with the variable
}
]
}
]
const otherRule = [
{
clauses: [
{
variable: "array",
operator: "equals",
values: [["foo", "bar"]]// this will the compare the array with the variable
}
]
}
]
const getActiveFlags = async (): Promise<string[]> => {
let flags: any[] = [];
await fetch("/featureApi/v1/toggles")
.then((response) => response.json())
.then((data) => {
flags = data;
});
return flags.map(flag => {
if(!flag.environment === "Production") return "allNonProdFlagsEnabled";
if(flag.project === "CMS" && flag.key === "Cache" && flag.environment === "Prod") return "cacheIsEnabled";
});
}
Nested properties can be accessed with . notation.
const rules = [
{
variable: "someObj.someNestedObj.someProperty",
operator: "lessThanOrEqualTo",
value: "someValue"
},
{
variable: "someObj.someNestedObj",
operator: "equals",
value: { someProperty: "someValue" }
}
]
Assignments can accept a function with the fact object or any other as a parameter for more complex assignments. This concept can be abstracted to implement any number of functions or additional properties and DSL ontop of the basic rules evaluation. Feel free to examine the tests for some realistic and relatively contrived examples of applicable domains. If you find yourself having to write a lot of complex if statements give Gambit a TRY!
Add a scoped registry in your .npmrc
@lenaire:registry=https://npm.pkg.github.com
registry=https://registry.npmjs.org
Now the package can be installed
npm install @lenaire/gambit@1.0.0