Extending NL

From FlexRule Wiki
Jump to: navigation, search

Approach

Extending a Natural Language is not very different from extending other types of the logic (i.e. Validation or Decision tree). You have the following options:

  1. Calling an object's method
  2. Calling function
  3. Encapsulate method/function with logic

Example

Let's say we have the following rule and we want to extend it.

input person    

when Person has name
        Name of person is not null and
        Name of person is not empty
end
Required Extension: Checking in Database for Name duplication

Extension

First we need to implement a type that knows how to does the check

Check names database for duplication

The bellow implementation is just to give you an example, you can fill the TODO part on your own:

 class PersonDbUtil
 {
     public bool CheckDbExists(object input)
     {
         var name = input as string;
         if (name == null)
             return false;
 
         var result = true;
         // TODO: go to your database and check for 
         //       duplication of name
         return result;
     }
 }

Calling Method

You always can inject an object into rule and use the object members (i.e. methods) inside your rule. In this way, you can take advantage of your existing code. Or sometimes it is just easier to implement a logic in code and reuse it in rule (e.g. web service call, complex algorithm,...)

To doing this, you just need an define an input for your utility object like bellow:

input person, db    

when Person has name
        Name of person is not null and
        Name of person is not empty and
        db.CheckDbExists(person.Name) is false
end

Calling Function

Functions are a type of expression very similar to method, but you the way they are used are independent of the object reference. other benefit of using function is that you can associate a new name to the method if you need to change the name of function. However they need to be registered before use.


To register your method as function:

1. You need to add an attribute on top of your method:

 class PersonDbUtil
 {
     [Function("NameExists")]     public bool CheckDbExists(object input)
 }

2. You need to register on execution:

var engine = RuntimeEngine.FromXml(Encoding.UTF8.GetBytes(ruleDocument));
engine.OnRunning = (e) =>
{
    e.Context.VariableContainer.RegisterFunction(typeof(PersonDbUtil));};

Then the function will be ready to use:

input person

when Person has name
        Name of person is not null and
        Name of person is not empty and
        NameExists(person.Name) is false
end

As you can see in the above example,

  1. There is no input parameter for the reference of the extension class, so the usage is direct by calling the name of function
  2. The new name NameExists is used to call the method of class

Using logic

Other benefit of using Natural Language is you can introduce a logic that is easier to read on rules.

input person

when Person has name
        Name of person is not null and
        Name of person is not empty and
        Ensure (person) name is not duplicated
end

when Ensure {person} name is not duplicated
        NameExists(person.Name) is false
end

Complete Sample

Rule

Here is the complete rule in the Natural Language XML container:

<Natural name="test a person identity">
    <Dsl>
input person

when Person has name
        Name of person is not null and
        Name of person is not empty and
        Ensure (person) name is not duplicated
end

when Ensure {person} name is not duplicated
        NameExists(person.Name) is false
end
    </Dsl>
</Natural>

Database Class Utility

 class PersonDbUtil
 {
     [Function("NameExists")]
     public bool CheckDbExists(object input)
     {
         var name = input as string;
         if (name == null)
             return false;
 
         var result = true;
         // TODO: go to your database and check for 
         //       duplication of name
         return result;
     }
 }

Running Sample

var engine = RuntimeEngine.FromXml(Encoding.UTF8.GetBytes(ruleDocument));
engine.OnRunning = (e) =>
{
    e.Context.VariableContainer.RegisterFunction(typeof(PersonDbUtil));
};
 
var per = new Person();
per.Name = "test";
per.Family = "test";
 
var res = engine.Run(per);
Assert.IsTrue(res.Outcome.Value);