Extending NL

From FlexRule Wiki
Jump to: navigation, search

Approach

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

  1. Calling an object's method
  2. Calling a 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 do the check

Check names database for duplication

The implementation below 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 can always inject an object into a 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 logic in the code and reuse it in a rule (e.g., web service call, complex algorithm, etc.).

To do this, you just need to define an input for your utility object, as shown below:

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 that are very similar to method, but the way these are used is independent of the object reference. The other benefit of using function is that you can associate a new name to the method if you need to change the name of the function. However, these need to be registered before use.


To register your method as a 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 the function
  2. The new name NameExists is used to call the method of class

Using logic

The other benefit of using Natural Language is that you can introduce a logic in the rules that is easier to read.

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