Business Rules Scripting Overview

From RemObjects Wiki
Jump to: navigation, search

This is an Article about Data Abstract



One of the exciting features of Data Abstract is the Business Rules Scripting support. EcmaScript (also known as JavaScript) based scripting is a new feature that lets you define scripts inside the schema that the server and client will execute. Changing the business rules means merely updating the schema on the server, for the updated versions of the scripts to be applied.

The Business Rules scripting feature is cross-platform, so it is easy to use a .NET Server with business rules support and a Delphi client or vice versa. Also, Server Scripting is ideal for use from within Relativity, to add business logic that the server will enforce, or change it without having to recompile the whole Data Abstract server application.

Contents

Why client-side business rules scripting?

In multi-tier applications, the general rule of thumb is to keep all business logic on the middle (server) tier, meaning that it is your server's job to validate the incoming data for input errors and for compliance with business rules.

For example, in a banking application, a business rule might be that only $5000 may be transferred from a given account within a day - the server will need to ensure that this rule is enforced and that transactions that violate the rule will be rejected (for example when bank employee accidentally enters a transfer of $6000).

One of the downsides of processing all business logic on the server is that input errors lead to a large overhead because transactions need to be sent to the server to be validated and rejected there. When developing rich clients, it is often desirable to detect rule violations as early as possibly, preferably as the user is entering the data. It also requires all clients to be updated when a business rule changes.

Business Rules Scripting supplies a solution by providing the client side business rules in a centralized location - in the schema, together with the rest of your data definitions. The business rules can automatically be loaded at the start of the client application, or optionally refreshed by calling the engine.

Using Scripting


If you use .NET, all you have to do is place the EcmaScriptProvider Class on the Data Abstract service and assign it to the ScriptProvider property of the Data Service (for server scripting), or place the same class next to the RemoteDataAdapter Class/LinqRemoteDataAdapter Class and assign the ScriptProvider property on that component to the EcmaScriptProvider (for client scripting). The client side components will automatically retrieve the scripts from the server on the first "Fill" request, although this can be disabled through the AutoLoadScripts property.


If you use Delphi, you simply need to place the TDASpiderMonkeyScriptProvider on the Data Abstract service and assign the ScriptProvider property on the service to the new component (for server scripting), or place the same class next to the data table(s) (TDAMemDataTable class) and assign the ScriptingProvider property on that component to the TDASpiderMonkeyScriptProvider (for client scripting).


Client-side business rules scripting is not (yet) supported in Data Abstract for Xcode (but Mac and iOS applications can of course benefit from server-side scripting in custom servers or Relativity Server).

Script Editor

Schema Modeler includes support for creating and editing business rules scripts, both in Schema Modeler 7 for Windows and Server Explorer (Xcode (Mac)). On Windows, the script editor can be brought up by clicking the "Business Rules" button in the Data Table toolbar, which brings up a dedicated editor window:

Business Rules Editor

The Business Rules editor contains the following options:

  • Client&Server and Server panels. These panels allow to define Client and Server scripts, respectively.
  • Language. For client scripts, JavaScript and PascalScript languages can be selected. For servers scripts only JavaScript is available. Choose the JavaScript language on both sides to allow the usage of the EcmaScript feature.
  • Events. The client side contains the following events: onNewRow, beforePost and beforeDelete. The server side contains the beforeProcessDelta, afterProcessDelta, beforeProcessDeltaChange, afterProcesDeltaChange, onProcessError and onValidateDataTableAccess events. See more about the usage of client and server events at Data Abstract Scripting Events.
  • Variables. Contains the most commonly used client and server veriables.
  • Actions. The log action is used to log specified information, the fail action is used to raise an exception and newGuid is used to create a new Guid.

On the Mac, the script editor is integrated with the options pane for the individual data tables:

Schema Modeler for Mac Showing Script Editor.png

Since Pascal Script based scripting is considered legacy and supported on Delphi servers only, Schema Modeler for Mac only exposes the option to use the new JavaScript scripting. Two tabs of script code are provided, one for server-side scripts only and one for scripts shared between client and server. A popup button called "Add Event" at the bottom can be used to add stubs for new event handlers, while the "Validate" button can send the script to Relativity Server (when editing a Relativity-based schema) for validation.


Scripts Definition

Server Scripts

The server can have a global (server-side) script and individual scripts per table (server-only and Client&Server, respectively). All scripting events defined in the Client&Server script (except onNewRow) will be called on the server, too.

To write the global business script, click the "Business Rules" button in Schema Modeler for Windows, or select the root "Schema" node in Schema Modeler for Mac.

Editing Global Business Scripts.png

The server contains different server events that can be implemented by EcmaScript. For example, the beforeProcessDelta() event can be implemented to log information about Delta as follows:

function beforeProcessDelta(delta)
{
  log('Scripting: beforeProcessDelta(delta) script function fires.');
  log('Delta name: ' + delta.name);
  log('Delta count: ' + delta.count);
  log('Delta fieldcount: ' + delta.fieldcount);
} 

or the beforeProcessDeltaChange() event can be implemented to check the contents of changes:

function beforeProcessDeltaChange(delta, change, wasRefreshed, canRemove)
{
  log('Scripting: beforeProcessDeltaChange(delta) script function fires.');
  var exp = /[A-Za-z]/;
  if ( (!exp.test(change['Name'])) && !change.isDelete )
  {
    fail('Field "Name" should contain literal characters.');
  }
  return canRemove;
}

The beforeProcessDelta event is called for each delta on the server before it's applied. Any changes done to the delta will be posted to the database.
The beforeProcessDeltaChange event is called before each change that is applied to the database.
The afterProcessDelta event is called for each delta on the server after it's applied. Any changes done to the delta will only be sent back to the client.
The afterProcessDeltaChange event is called after each change that is applied to the database.
The full API can be found in the Business Rules Scripting Events topic.

Client Side Scripts

Client side scripts are always defined per-table, and created in the "Client and Server" tab.

Business Rules - Customers.png

Client side scripts support three methods, onNewRow(), beforePost() and beforeDelete(). The first event is called when creating new rows, this can be used to initialize new records. The beforePost() event is called before the changes are accepted, useful for validating the user input, and the beforeDelete() event is called just before a record is deleted (on the client and the server).

The beforePost() and beforeDelete'() events are ideal for encoding rules you want to check both on the client (for convenience) and the server (for security).

For example, the onNewRow() event can be used to define default values as follows:

function onNewRow(row) 
{
  log('Scripting: onNewRow(row) script function fires.');
  row['Id'] = newGuidString();
  row['Name'] = '[new customer]';
  row['Discount'] = 20;
  log('Scripting: Defaults added.');
}

The beforePost() event can be used to check a data table field value:

function beforePost(row) 
{
  log('Scripting: beforePost(row) script function fires.');
  if (row['Discount'] > 50)
  {
    log('Scripting: validation failed.');
    fail('Scripting rule: Discount has to be less than 50.');
  }
  if (row['Discount'] < 0)
  {
    log('Scripting: validation failed.');
    fail('Scripting rule: Discount has to be equal to or more than 0.');
  }
  log('Scripting: validation passed.');
}

beforeDelete() event can be used to forbid deleting records:

function beforeDelete(row) 
{
  log('Scripting: beforeDelete(row) script function fires. Attempt to delete ' + row['Name']);
  log('Scripting: Attempt rejected.');
  fail('Scripting rule: Deleting customers does not allowed.');
} 

Custom Variables And Functions


If you need additional functions to provide custom functionality, you can easily supply them with the RemObjects EcmaScripting functionality.

For example, if you need to have a function that will generate a GUID value in Base64 encoding, you can add this function as a custom function as follows:

1. Define the custom function on the server/client side:

public object Base64GUID(object[] args)
{
   return Convert.ToBase64String(Guid.NewGuid().ToByteArray());
}

2. Declare the function above as custom function:

ecmaScriptProvider.Engine.Globals.SetVariable("Base64GUID", new RemObjects.DataAbstract.Scripting.ParamsDelegate(Base64GUID));

3. Use the custom function Base64GUID() in the Business Rules editor. For example, on the client side, the custom function can be used in the onNewRow event as follows:

function onNewRow(row)
{
  row['userID'] = Base64GUID();
}

Note: If you want to define a custom variable (for example, with the name 'CustomVariable') with a specified value (for example, CustomValue = 10), you can declare the custom variable like this:

int CustomValue = 10;
ecmaScriptProvider.Engine.Globals.SetVariable("CustomVariable", CustomValue); 



With Delphi, you need to perform the following steps to create the custom function above:

1. Define the custom function on the server/client side:

function TClientDataModule.Base64GUID(args: array of variant): variant; 
var
  NewGuid:TGuid;
  Encoder:IdCoderMIME.TIdEncoderMIME;
  Bytes: TBytes;
begin
  try
    CreateGUID(NewGuid);
    Encoder:=TIdEnCoderMIME.Create;
    Bytes:=TEncoding.ASCII.GetBytes(GuidToString(NewGuid));
    result := Encoder.EncodeBytes(Bytes);
  finally
    Encoder.Free;
  end;
end;

2. Declare the function above as custom function:

DASpiderMonkeyScriptProvider1.Engine.Variables.SetVariable('Base64GUID', Base64GUID); 

3. Use the custom function Base64GUID() in Business Rules editor. For example, on the client side, the custom function can be used in the onNewRow event like this:

function onNewRow(row)
{
  row['userID'] = Base64GUID();
}

Note: If you want to define a custom variable (for example, with the name 'CustomVariable') with a specified value (for example, CustomValue = 10), you can declare the custom variable as follows:

CustomValue := 10;

DASpiderMonkeyScriptProvider1.Engine.Variables.SetVariable('CustomVariable',CustomValue);

Note: Arguments are passed in inverted order. So calling MyFunc(val1, val2, val3) you get args[0]=val3, args[1]=val2 and args[2]=val1

See Also

Product Articles Data Abstract RemObjects SDK Hydra


Product: RemObjects Data Abstract
Available Editions: Data Abstract for .NET, Xcode, Delphi, Java and JavaScript

GlossaryArticlesFeaturesLibrarySamples

Personal tools
Namespaces

Variants
Actions
Navigation
products
platforms
special
Toolbox