Giter Site home page Giter Site logo

grool's Introduction

Important Notice

Grool rule engine has been donated to Hyperjump.tech so now its part of Hyperjump's OSS Family. You can visit grule-rule-engine to continue using this software.

This project is archived.

drawing

"Gopher Holds The Rules"

Grool


import "github.com/newm4n/grool"

Rule Engine for Go

Travis CI

Grool is a Rule Engine library for Golang programming language. Inspired by the acclaimed JBOSS Drools, done in a much simple manner.

Like Drools, Grools have its own DSL comparable as follows.

Drools's DRL be like :

rule "SpeedUp"
    salience 10
    when
        $TestCar : TestCarClass( speedUp == true && speed < maxSpeed )
        $DistanceRecord : DistanceRecordClass()
    then
        $TestCar.setSpeed($TestCar.Speed + $TestCar.SpeedIncrement);
        update($TestCar);
        $DistanceRecord.setTotalDistance($DistanceRecord.getTotalDistance() + $TestCar.Speed)
        update($DistanceRecord)
end

And Grools's GRL be like :

rule SpeedUp "When testcar is speeding up we keep increase the speed." salience 10  {
    when
        TestCar.SpeedUp == true && TestCar.Speed < TestCar.MaxSpeed
    then
        TestCar.Speed = TestCar.Speed + TestCar.SpeedIncrement;
        DistanceRecord.TotalDistance = DistanceRecord.TotalDistance + TestCar.Speed;
}

What is RuleEngine?


There are no better explanation compared to the article authored by Martin Fowler. You can read the article here (RulesEngine by Martin Fowler).

Taken from TutorialsPoint website (with very slight modification),

Grools is Rule Engine or a Production Rule System that uses the rule-based approach to implement and Expert System. Expert Systems are knowledge-based systems that use knowledge representation to process acquired knowledge into a knowledge base that can be used for reasoning.

A Production Rule System is Turing complete with a focus on knowledge representation to express propositional and first-order logic in a concise, non-ambiguous and declarative manner.

The brain of a Production Rules System is an Inference Engine that can scale to a large number of rules and facts. The Inference Engine matches facts and data against Production Rules – also called Productions or just Rules – to infer conclusions which result in actions.

A Production Rule is a two-part structure that uses first-order logic for reasoning over knowledge representation. A business rule engine is a software system that executes one or more business rules in a runtime production environment.

A Rule Engine allows you to define “What to Do” and not “How to do it.”

What is a Rule?

(also taken from TutorialsPoint)

Rules are pieces of knowledge often expressed as, "When some conditions occur, then do some tasks."

When
   <Condition is true>
Then
   <Take desired Action>

The most important part of a Rule is its when part. If the when part is satisfied, the then part is triggered.

rule  <rule_name> <rule_description>
   <attribute> <value> {
   when
      <conditions>

   then
      <actions>
}

Advantages of a Rule Engine

Declarative Programming

Rules make it easy to express solutions to difficult problems and get the solutions verified as well. Unlike codes, Rules are written in less complex language; Business Analysts can easily read and verify a set of rules.

Logic and Data Separation

The data resides in the Domain Objects and the business logic resides in the Rules. Depending upon the kind of project, this kind of separation can be very advantageous.

Centralization of Knowledge

By using Rules, you create a repository of knowledge (a knowledge base) which is executable. It is a single point of truth for business policy. Ideally, Rules are so readable that they can also serve as documentation.

Agility To Change

Since business rules are actually treated as data. Adjusting the rule according to business dynamic nature become trivial. No need to re-build codes, deploy as normal software development do, you only need to roll out sets of rule and apply them to knowledge repository.

Hello Grool

Grool Rule Language (GRL)

The GRL is a DSL (Domain Specific Language) designed for Grool. Its a simplified language to be used for defining rule evaluation criteria and action to be executed if the criteria were met.

Generally, the language have the following structure :

rule <RuleName> <RuleDescription> [salience <priority>] {
    when
        <boolean expression>
    then
        <assignment or operation expression>
}

RuleName identify a speciffic rule. The name should be unique in the entire knowledge base, consist of one word thus it should not contains white-spece.

RuleDescription describes the rule. The description should be enclosed with a double-quote.

Salience defines the importance of the rule. Its an optional rule configuration, and by default, when you don't specify them, all rule have the salience of 0 (zero). The lower the value, the less important the rule. Whenever multiple rule are a candidate for execution, highest salience rule will be executed first. You may define negative value for the salience, to make the salience even lower. Like any implementation of Rule-Engine, there are no definitive algorithm to specify which rule to be execute in case of conflicting candidate, the engine may run which ever they like. Salience is one way of hinting the rule engine of which rule have more importance compared to the other.

Boolean Expression is an expression that will be used by rule engine to identify if that speciffic rule are a candidate for execution for the current facts.

Assignment or Operation Expression contains list of expressions (each expression should be ended with ";" symbol.) The expression are designed to modify the current fact values, making calculation, make some logging, etc.

Boolean Expression

Boolean expression comes natural for java or golang developer in GRL.

when
     contains(User.Name, "robert") &&
     User.Age > 35
then
     ...

Constants and Literalss

Literal Description Example
String Hold string literal, enclosed a string with double quote symbol " "This is a string"
Decimal Hold a decimal value, may preceeded with negative symbol - 1 or 34 or 42344 or -553
Real Hold a real value 234.4553, -234.3
Boolean Hold a boolean value true, TRUE, False

Math operator such as +, -, /, *; Logical && and ||; Comparison <,<=,>,>=,==,!= all are supported by the language.

Comments

You can always put a comment inside your GRL script. Such as :

// This is a comment
// And this

/* And also this */

/*
   As well as this
*/

Examples

rule SpeedUp "When testcar is speeding up we keep increase the speed."  {
    when
        TestCar.SpeedUp == true && TestCar.Speed < TestCar.MaxSpeed
    then
        TestCar.Speed = TestCar.Speed + TestCar.SpeedIncrement;
		DistanceRecord.TotalDistance = DistanceRecord.TotalDistance + TestCar.Speed;
}

rule StartSpeedDown "When testcar is speeding up and over max speed we change to speed down."  {
    when
        TestCar.SpeedUp == true && TestCar.Speed >= TestCar.MaxSpeed
    then
        TestCar.SpeedUp = false;
		log("Now we slow down");
}

rule SlowDown "When testcar is slowing down we keep decreasing the speed."  {
    when
        TestCar.SpeedUp == false && TestCar.Speed > 0
    then
        TestCar.Speed = TestCar.Speed - TestCar.SpeedIncrement;
		DistanceRecord.TotalDistance = DistanceRecord.TotalDistance + TestCar.Speed;
}

rule SetTime "When Distance Recorder time not set, set it." {
	when
		isNil(DistanceRecord.TestTime)
	then
		log("Set the test time");
		DistanceRecord.TestTime = now();
}

Loading GRL on to Knowledge

One knowledge base may consist of many rules. Tens to hundreds of rules. They may be loaded from multiple sources. Those rules will go to the same knowledge as long as you use the same knowledge when loading each of the resource.

Before you load any rule, you need to have your own knowledge

knowledgeBase := model.NewKnowledgeBase()
ruleBuilder := builder.NewRuleBuilder(knowledgeBase)

From File

fileRes := NewFileResource("/path/to/rules.grl")
err := ruleBuilder.BuildRuleFromResource(fileRes)
if err != nil {
    panic(err)
}

From String or ByteArray

byteArr := NewBytesResource([]byte(rules))
err := ruleBuilder.BuildRuleFromResource(byteArr)
if err != nil {
    panic(err)
}

From URL

urlRes := NewUrlResource("http://host.com/path/to/rule.grl")
err := ruleBuilder.BuildRuleFromResource(urlRes)
if err != nil {
    panic(err)
}

Preparing Facts

In Grool, fact is merely a simple struct instance.

Suppose you have the following struct.

type TestCar struct {
	SpeedUp        bool
	Speed          int
	MaxSpeed       int
	SpeedIncrement int
}

type DistanceRecorder struct {
	TotalDistance int
	TestTime      time.Time
}

And then you instantiate those struct.

tc := &TestCar{
    SpeedUp:        true,
    Speed:          0,
    MaxSpeed:       100,
    SpeedIncrement: 2,
}
dr := &DistanceRecorder{
    TotalDistance: 0,
}

Add those struct instances (fact) into data context.

dataContext := context.NewDataContext()
dataContext.Add("TestCar", tc)
dataContext.Add("DistanceRecord", dr)

Now your fact is ready to be executed in the rule engine that already prepared with some knowledge.

Executing A Knowledge On Facts and get result

You already know how to load rules into knowledge base, and you also know how to prepare your fact. Now we can execute the knowledge base agains facts.

engine := NewGroolEngine()
err = engine.Execute(dataContext, knowledgeBase)
if err != nil {
    t.Errorf("Got error : %v", err)
    t.FailNow()
} else {
    t.Log(dr.TotalDistance)
}

The rule engine will use loaded knowledgebase to work upon sets of fact data in data context.

Calling Function in Grool

All invocable functions which are invocable from the DataContext is Invocable from within the rule, both in the "When" scope and "Then" scope.

Assuming you have a struct with some functions.

type MyPoGo struct {
}

func (p *MyPoGo) GetStringLength(sarg string) int {
    return len(sarg)
}

func (p *MyPoGo) AppendString(aString, subString string) string {
    return sprintf("%s%s", aString, subString)
}

And add the struct into knowledge base

dctx := context.NewDataContext()
dctx.Add("Pogo", &MyPoGo{})

You can call the fuction within the rule

when
    Pogo.GetStringLength(some.variable) < 100
then
    some.variable = Pogo.AppendString(some.variable, "Groooling");

Default Functions

All functions defined in context/GroolFunction.go are built-in functions. This means, that you can call the function straight away without having to mention the instance name as it automatically added by the engine.

I don't maintain list of built in function as you can look into the Go source code directly. (the context/GroolFunction.go)

Important Thing you must know about Custom Function in Grool

When you make your own function to be called from rule engine, you need to know the following rules.

  1. The function must be visible. Public function convention should start with capital letter. Private functions cant be executed.
  2. The function must only return 1 type. Returning multiple variable from function are not acceptable, the rule execution will fail if there are multiple return variable.
  3. The way number literal were treated in Grool's DRL is; decimal will always taken as int64 and real as float64, thus always consider to define your function argument and returns between int64 and float64 when your function works with numbers.

Tasks and Help Wanted.

  • Need to do more and more and more test.
  • Better code coverage test.
  • Better commenting for go doc best practice.
  • Improve function argument handling to be more fluid and intuitive.

grool's People

Contributors

newm4n avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

grool's Issues

Can not set string value, if func receives interface

Describe the bug
Can not set string value, if func receives interface

To Reproduce
use rule like that

rule UserCheckSuccess "test"  salience 10{
    when
      User.Age == 1
    then
      User.SetName("FromRule");
}

use struct

type User struct {
	Id      int
	Name    string
}

func (u *User) SetName(name interface{}) {
	u.Name = name.(string)
}

Expected behavior
Func exec normally, and set param to object

Additional context

time="2019-11-01T15:10:38+03:00" level=error msg="Failed execution rule : UserCheckSuccess. Got error invalid argument types for function SetName(). argument #0, require interface but string"

Panic if use func that return interface in "where" clause

Describe the bug
Panic if use func that return interface in "where" clause

To Reproduce

package examples

import (
	"github.com/newm4n/grool/builder"
	"github.com/newm4n/grool/context"
	"github.com/newm4n/grool/engine"
	"github.com/newm4n/grool/model"
	"github.com/newm4n/grool/pkg"
	"testing"
)

const (
	Rule81 = `
rule UserTestRule8 "test 8"  salience 10{
	when
	  User.GetParam() == "string_param"
	then
	  User.SetName("FromRule");
	  Retract("UserTestRule8");
}
`
	Rule82 = `
rule UserTestRule8 "test 8"  salience 10{
	when
	  User.GetParam() == 42
	then
	  User.SetName("FromRule");
	  Retract("UserTestRule8");
}
`
)

type AUserIssue8 struct {
	Name  string
	Param interface{}
	Age   int
}

func (u *AUserIssue8) GetName() string {
	return u.Name
}

func (u *AUserIssue8) GetParam() interface{} {
	return u.Param
}

func (u *AUserIssue8) SetName(name interface{}) {
	u.Name = name.(string)
}

func TestMethodCall_Issue8_1(t *testing.T) {
	user := &AUserIssue8{
		Name: "Watson",
		Age:  7,
		Param: "string_param",
	}

	dataContext := context.NewDataContext()
	err := dataContext.Add("User", user)
	if err != nil {
		t.Fatal(err)
	}

	knowledgeBase := model.NewKnowledgeBase()
	ruleBuilder := builder.NewRuleBuilder(knowledgeBase)

	err = ruleBuilder.BuildRuleFromResource(pkg.NewBytesResource([]byte(Rule81)))
	if err != nil {
		t.Log(err)
	} else {
		eng1 := &engine.Grool{MaxCycle: 5}
		err := eng1.Execute(dataContext, knowledgeBase)
		if err != nil {
			t.Fatal(err)
		}
		if user.GetName() != "FromRule" {
			t.Errorf("User should be FromRule but %s", user.GetName())
		}
	}
}

func TestMethodCall_Issue8_2(t *testing.T) {
	user := &AUserIssue8{
		Name: "Watson",
		Age:  7,
		Param: 42,
	}

	dataContext := context.NewDataContext()
	err := dataContext.Add("User", user)
	if err != nil {
		t.Fatal(err)
	}

	knowledgeBase := model.NewKnowledgeBase()
	ruleBuilder := builder.NewRuleBuilder(knowledgeBase)

	err = ruleBuilder.BuildRuleFromResource(pkg.NewBytesResource([]byte(Rule82)))
	if err != nil {
		t.Log(err)
	} else {
		eng1 := &engine.Grool{MaxCycle: 5}
		err := eng1.Execute(dataContext, knowledgeBase)
		if err != nil {
			t.Fatal(err)
		}
		if user.GetName() != "FromRule" {
			t.Errorf("User should be FromRule but %s", user.GetName())
		}
	}
}

=== RUN   TestMethodCall_Issue8
--- FAIL: TestMethodCall_Issue8 (0.00s)
panic: reflect: call of reflect.Value.Type on zero Value [recovered]
	panic: reflect: call of reflect.Value.Type on zero Value

goroutine 19 [running]:
testing.tRunner.func1(0xc0000e4a00)
	/usr/local/go/src/testing/testing.go:874 +0x3a3
panic(0x866820, 0xc00023fa80)
	/usr/local/go/src/runtime/panic.go:679 +0x1b2
reflect.Value.Type(0x0, 0x0, 0x0, 0x98, 0x0)
	/usr/local/go/src/reflect/value.go:1877 +0x166
github.com/newm4n/grool/model.(*Predicate).Evaluate(0xc0001f7600, 0xc00011d920, 0x1, 0x0, 0xc0002281b0, 0xc000195c60)
	/home/id/hdd/go-nogopath/src/github.com/newm4n/grool/model/Predicate.go:104 +0x1845
github.com/newm4n/grool/model.(*Expression).Evaluate(0xc0001f75c0, 0x0, 0xd11df0, 0x861760, 0xc000256008, 0xc00011d950)
	/home/id/hdd/go-nogopath/src/github.com/newm4n/grool/model/Expression.go:53 +0x460
github.com/newm4n/grool/model.(*WhenScope).ExecuteWhen(0xc00023f760, 0x410763, 0xc000195e60, 0xd11df0)
	/home/id/hdd/go-nogopath/src/github.com/newm4n/grool/model/WhenScope.go:41 +0x32
github.com/newm4n/grool/model.(*RuleEntry).CanExecute(0xc0000a7e60, 0xc00011d950, 0xc000195e60, 0xd11df0)
	/home/id/hdd/go-nogopath/src/github.com/newm4n/grool/model/RuleEntry.go:48 +0x39
github.com/newm4n/grool/engine.(*Grool).Execute(0xc000195f08, 0xc00011d920, 0xc0000a5650, 0x0, 0x0)
	/home/id/hdd/go-nogopath/src/github.com/newm4n/grool/engine/GroolEngine.go:58 +0x1c6
github.com/newm4n/grool/examples.TestMethodCall_Issue8(0xc0000e4a00)
	/home/id/hdd/go-nogopath/src/github.com/newm4n/grool/examples/Issue8_test.go:63 +0x337
testing.tRunner(0xc0000e4a00, 0x918a08)
	/usr/local/go/src/testing/testing.go:909 +0xc9
created by testing.(*T).Run
	/usr/local/go/src/testing/testing.go:960 +0x350

Process finished with exit code 1

Expected behavior
Rule works correctly

Add support for nested structs functions in rules

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
This rule not work

rule UserTestRule4 "test 3"  salience 10{
					when
					  User.Auth.GetEmail() == "[email protected]"
					then
					  User.Name = "FromRuleScope4";
 					  Retract("UserTestRule4");
				}

returns error

Failed testing condition for rule : UserTestRule4. Got error error while fetching function GetEmail() parameter types. Got function GetEmail not found

Describe the solution you'd like
Add possibility to use nested struct funcs

Funcs called in "then" section, that return nothing not work

Describe the bug
Function called in "then" section not work

To Reproduce
Steps to reproduce the behavior:
use this rule

rule UserTestRule3 "test 3"  salience 10{
					when
					  User.GetName() == "Watson"
					then
					  User.SetName("FromRuleScope3");
 					  Retract("UserTestRule3");
				}

and this object

type User struct {
	Name string
}

func (u *User) GetName() string {
	return u.Name
}

func (u *User) SetName(name string) {
	u.Name = name
}

returns

level=error msg="Failed execution rule : UserTestRule3. Got error no assignment or function call to evaluate"

Expected behavior
Success call function in "then" section

Additional content
It only works if func return any value, like this

func (u *User) SetName(name string)string {
	u.Name = name
	return name
}

and rule then looks like this

rule UserTestRule3 "test 3"  salience 10{
					when
					  User.GetName() == "Watson"
					then
					  Log(User.SetName("FromRuleScope3"));
 					  Retract("UserTestRule3");
				}

String Liternal includes the Quotes in the string it self

Describe the bug
When passing string literals as constants or as function argument. The string Value actually contains the entire literal including the double quotes that encloses the string. This makes string comparison become invalid.

To Reproduce
Steps to reproduce the behavior:

  1. Create a rule that executes a function with string arguments.
  2. Make the function to log out the string argument. The resulting log will contains the enclosing quotes.

Expected behavior
String value should not contains the enclosed quotes.

when call Custom function, runtime err: invalid memory address or nil pointer dereference

code:

package main

import (
	"fmt"
	"github.com/newm4n/grool/builder"
	"github.com/newm4n/grool/context"
	"github.com/newm4n/grool/engine"
	"github.com/newm4n/grool/model"
	"github.com/newm4n/grool/pkg"
	"testing"
)


const (rule2  = `
rule AgeNameCheck "test" {
    when
		Pogo.GetStringLength("9999") > 0 
    then
		Log(User.Name);
}
`
)

type MyPoGo struct {
}

func (p *MyPoGo) GetStringLength(sarg string) int {
	return len(sarg)
}

type User struct {
	Name string
	Age int
	Male bool
}

func testMyRule(t *testing.T){
	user := &User{
		Name: "Calo",
		Age:  0,
		Male: true,
	}

	dataContext := context.NewDataContext()
	dataContext.Add("User", user)
	dataContext.Add("Pogo", &MyPoGo{})

	//初始化规则引擎
	knowledgeBase := model.NewKnowledgeBase()
	ruleBuilder := builder.NewRuleBuilder(knowledgeBase)

	err := ruleBuilder.BuildRuleFromResource(pkg.NewBytesResource([]byte(rule2)))
	if err != nil{
		fmt.Println(err)
	}else{

		eng1 := &engine.Grool{MaxCycle:1}
		eng1.Execute(dataContext,knowledgeBase)
		if err !=nil{
			fmt.Println("err:",err)
		}else {
			fmt.Println(user)
		}
	}
}

func main()  {

	t := &testing.T{}
	testMyRule(t)
}
ERROR:
GOROOT=/Users/renyunyi/sdk/go1.13 #gosetup
GOPATH=/Users/renyunyi/go #gosetup
/Users/renyunyi/sdk/go1.13/bin/go build -o /private/var/folders/k2/3y7zc6yx7nvbxp4xsbbdrknc0000gn/T/___go_build_main_go /Users/renyunyi/go/src/blive-risk/main.go #gosetup
/private/var/folders/k2/3y7zc6yx7nvbxp4xsbbdrknc0000gn/T/___go_build_main_go #gosetup
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x20 pc=0x13baec9]

goroutine 1 [running]:
github.com/newm4n/grool/model.(*ExpressionAtom).Evaluate(0x0, 0x400, 0x1b13900, 0x20300000000000, 0x1cfffff, 0xc0001b38c0)
        /Users/renyunyi/go/src/github.com/newm4n/grool/model/ExpressionAtom.go:25 +0x29
github.com/newm4n/grool/model.(*ExpressionAtom).Evaluate(0xc0000c7b00, 0x400, 0x400, 0x1b13900, 0x1cfffff, 0xc0001b3900)
        /Users/renyunyi/go/src/github.com/newm4n/grool/model/ExpressionAtom.go:32 +0x60
github.com/newm4n/grool/model.(*Predicate).Evaluate(0xc000242700, 0xc000141380, 0x1, 0x0, 0xc0002521e8, 0xc0001b3b50)
        /Users/renyunyi/go/src/github.com/newm4n/grool/model/Predicate.go:53 +0x51
github.com/newm4n/grool/model.(*Expression).Evaluate(0xc0002426c0, 0x0, 0x18b8200, 0x14302e0, 0xc0000caea8, 0xc0001432c0)
        /Users/renyunyi/go/src/github.com/newm4n/grool/model/Expression.go:53 +0x460
github.com/newm4n/grool/model.(*WhenScope).ExecuteWhen(0xc00025cb80, 0x10106f3, 0xc0001b3d28, 0x18b8200)
        /Users/renyunyi/go/src/github.com/newm4n/grool/model/WhenScope.go:41 +0x32
github.com/newm4n/grool/model.(*RuleEntry).CanExecute(0xc000268410, 0xc0001432c0, 0xc0001b3d28, 0x18b8200)
        /Users/renyunyi/go/src/github.com/newm4n/grool/model/RuleEntry.go:43 +0x2f
github.com/newm4n/grool/engine.(*Grool).Execute(0xc0001b3dd8, 0xc000141380, 0xc0000c55c0, 0x0, 0x0)
        /Users/renyunyi/go/src/github.com/newm4n/grool/engine/GroolEngine.go:54 +0x1ad
main.testMyRule(0xc000060e50)
        /Users/renyunyi/go/src/blive-risk/main.go:58 +0x2f6
main.main()
        /Users/renyunyi/go/src/blive-risk/main.go:70 +0x6f

Process finished with exit code 2

Can you help me? My email is [email protected]

Error if "when" condition contains field name "Id"

Describe the bug
Error appears if field used in "when" section but not in "then"

Got error Grool successfully selected rule candidate for execution after 2 cycles, this could possibly caused by rule entry(s) that keep added into execution pool but when executed it does not change any data in context. Please evaluate your rule entries "When" and "Then" scope. You can adjust the maximum cycle using Grool.MaxCycle variable.

To Reproduce
Just run UserSuccess and UserFail

ppackage examples

import (
	"github.com/newm4n/grool/builder"
	"github.com/newm4n/grool/context"
	"github.com/newm4n/grool/engine"
	"github.com/newm4n/grool/model"
	"github.com/newm4n/grool/pkg"
	"github.com/stretchr/testify/assert"
	"testing"
)

func Run(name string, i interface{}, rule []byte, t *testing.T) {

	dataContext := context.NewDataContext()
	dataContext.Add(name, i)

	knowledgeBase := model.NewKnowledgeBase()
	ruleBuilder := builder.NewRuleBuilder(knowledgeBase)

	err := ruleBuilder.BuildRuleFromResource(pkg.NewBytesResource(rule))
	if err != nil {
		t.Log(err)
	} else {
		eng1 := &engine.Grool{MaxCycle: 2}

		err := eng1.Execute(dataContext, knowledgeBase)
		if err != nil {
			t.Logf("Got error %v", err)
			t.FailNow()
		} else {
			t.Logf("updated object: %+v\n", i)
		}
	}
}

const ruleUserWork = `
rule UserCheckSuccess "test"  salience 10{
    when
      User.Age == 0  
    then
      User.Name = "FromRule";
      User.Age = 3;
}
`

const ruleUserFail = `
rule UserCheckFail "test"  salience 10{
    when
      User.Id == 1  
    then
      User.Name = "FromRule";
      User.Age = 3;
}
`

type User struct {
	Id   int
	Name string
	Age  int
	Male bool
}

func getUser() *User {
	return &User{
		Id:   1,
		Name: "Calo",
		Age:  0,
		Male: true,
	}
}

func TestUserSuccess(t *testing.T) {
	user := getUser()

	Run("User", user, []byte(ruleUserWork), t)
	assert.Equal(t, "FromRule", user.Name)
	assert.Equal(t, 3, user.Age)
}

func TestUserFail(t *testing.T) {
	user := getUser()
	Run("User", user, []byte(ruleUserFail), t)
	assert.Equal(t, "FromRule", user.Name)
	assert.Equal(t, 3, user.Age)
}

Expected behavior
I expected that "Id" field will be processing as other fields

Desktop (please complete the following information):

  • OS: Linux Debian

Additional context

> go test  examples/AgeCheckSample_test.go -v
=== RUN   TestUserSuccess
time="2019-10-08T12:10:35+03:00" level=info msg="Executing rule : UserCheckSuccess. Salience 10"
time="2019-10-08T12:10:35+03:00" level=info msg="Finished Rules execution. Total #2 cycles."
--- PASS: TestUserSuccess (0.00s)
    AgeCheckSample_test.go:32: updated object: &{Id:1 Name:FromRule Age:3 Male:true}
=== RUN   TestUserFail
time="2019-10-08T12:10:35+03:00" level=info msg="Executing rule : UserCheckFail. Salience 10"
time="2019-10-08T12:10:35+03:00" level=info msg="Executing rule : UserCheckFail. Salience 10"
--- FAIL: TestUserFail (0.00s)
    AgeCheckSample_test.go:29: Got error Grool successfully selected rule candidate for execution after 2 cycles, this could possibly caused by rule entry(s) that keep added into execution pool but when executed it does not change any data in context. Please evaluate your rule entries "When" and "Then" scope. You can adjust the maximum cycle using Grool.MaxCycle variable.
FAIL
FAIL    command-line-arguments  0.004s
FAIL

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.