With the Vocabulary ready, you can now focus on modeling the rules. Let’s
begin with the first Rulesheet, which models the rules in the Raise Alerts substep.
Before building or modeling anything, you should think about how to approach
this part of the problem.
The business rule identified for the Raise Alerts substep examines all items
in a customer’s shopping cart and determines which items (if any) come from the Liquor
department.
Each Item has a barcode. The department code occupies the
4th through 6th characters of the barcode. Let’s assume that the
department code for liquor is 291. So if an item has 291
occupying the 4th through 6th characters of the barcode, the cashier
must be alerted to check the customer’s identification.
Considering this scenario, define two rules in Corticon Studio:
Determine the department code for every item in the shopping cart.
Determine if any of the items come from the Liquor department and if
so, the rule raises an alert of some kind.
It is normal to not always have a one-to-one correlation between the business
rule defined in a business scenario and the corresponding rules modeled in Corticon Studio.
A good guideline is to keep your individual rules relatively simple and let them work
together to perform more complex logic defined by the business rules.
Create the Rulesheet
Before you start defining the rules, create a Rulesheet.
Name the Rulesheet checks, and then click
Next.
Select groceryStore.ecore as the Vocabulary.
Note: You named
this Rulesheet checks as reminder of the overall organization—this
Rulesheet performs any necessary checks and raises alerts as required.
Click Finish.
Define rule scope
You need to choose the point of view in the Vocabulary that best
represents the terms required by the rules themselves. This point of view, called the
scope of the rule, changes from Rulesheet to Rulesheet.
Scope is a powerful and important concept. It determines which entity
instances and attributes are evaluated and acted upon by a rule. For the first
Rulesheet, define rules that act only on Items associated with a ShoppingCart, which in
turn is associated with a Customer. Using Customer as the root entity and working with
the associated ShoppingCart and its items makes sense because it is a Customer’s
transaction that is processed by the Checkout step. It forms the scope of the rules in
the Rulesheet.
To define the scope:
Open the Scope pane of the Rulesheet by ensuring that checks.ers is
open and active, and selecting Rulesheet > Advanced View. The Scope pane
opens in the Rulesheet.
Drag the Customer entity from the Rule Vocabulary view and
drop it into the Scope pane.
Drag shoppingCart from under Customer into the
Scope pane.
To complete the scope, drag item from under
shoppingCart and drop it into the Scope pane.
Next, let’s enter an alias for a customer’s shopping cart and call it
currentCart by double-clicking shoppingCart in the Scope pane
and enter currentCart. From now on, when you model rules involving a customer’s
shopping cart, use this alias to represent the perspective of a customer’s shopping
cart.
Because a shopping cart can contain many items, let’s define another
alias items that represents all the items in a customer’s shopping cart.
Assigning meaningful alias names is a good practice and using the plural
form of item reminds us that the alias represents all the items in the customer’s
shopping cart.
Using aliases is optional in many cases—they serve to simplify and
shorten rule expressions. But in certain cases, using aliases is mandatory. For example,
applying collection operators to sets or collections of data in rules requires the use
of aliases. Because you work with the collection of items in a customer’s shopping cart
a bit later, keep the items alias defined and ready.
Aliases always insert themselves automatically when terms are dragged
from the Scope section or Vocabulary window to the Rulesheet. Because all
Studio expressions are case-sensitive, to avoid errors, drag and drop terms instead of
typing them manually.
Model the first rule
To model the business rule for the Raise Alerts substep, create two
rules in Corticon Studio in the checks Rulesheet:
Rule 1: Determine the department code for every item in the shopping
cart.
Rule 2: Use the department code to determine if any of the items
come from the Liquor department and if so, raise an alert.
Let’s start modeling the first rule—determining the department codes.
You know an item’s department is identified by the 4th through 6th
characters in its barcode. So using the items alias, let’s add an action-only
rule in an Actions row of column 0 using the String operator
.substring as shown.
Note: A preferred user language might use different separator symbols than
those documented for decimal values, list ranges, and dates. Here, you might need to
write substring(4;6).
The expression items.department=items.barCode.substring(4,6)
extracts the characters from positions 4 to 6 in the barcode string and assigns the
substring to items.department. The checkbox in the corresponding cell
of column 0 indicates that this is an action-only rule, which fires whenever the
Rulesheet receives any data. Action-only rules fire first in the Rulesheet. In this
example, it is useful because you need to extract the department code before identifying
whether any items come from the Liquor department.
Because the alias items represents the collection of all items
associated with a customer’s shopping cart, this rule evaluates and processes every item
in a customer’s shopping cart, extracts each department code, and then assigns that code
to the item’s department attribute. This iteration is a natural behavior of the
rule engine: it automatically processes all data that matches the rule’s scope.
Notice that when you dragged the terms barcode and
department from the Vocabulary to the Action row, they were
automatically added to the Scope pane. Over time, the Scope pane becomes a
reduced version of the Vocabulary, containing only those terms used by the rules in that
Rulesheet.
Save the Rulesheet.
Test the first rule
To test the first rule, create a Ruletest.
Name the Ruletest checks, and then choose the test subject
checks.ers.
In the Input pane of the Ruletest, define a customer with an
associated shopping cart containing two items, one of which is from the Liquor
department.
Drag the Customer entity, and drop it into the Input pane. Then, drop
shoppingCart (under Customer) onto the Customer entity: Then drag and drop item (under shoppingCart) onto the shoppingCart
entity twice.
Note: You must drop
the items from the Vocabulary into the Input pane of the Ruletest in the
indicated order so that you can duplicate the scope of the rule which that
processes this data.
When finished, enter test data as shown.
Note: A preferred user language
might use different separator symbols than those documented for decimal values,
list ranges, and dates. In that case, you might need to write the price of Miller
Beer as:
6,990000
As you
can see, one of the items is from the liquor department (remember that the department
code for Liquor is 291, which occupies characters 4 through 6 in the barcode).
Finally, execute the Ruletest. The output should look like this:
The first rule has worked as expected. Characters 4-6 have been
successfully parsed from each item’s barCode and assigned to its
department attribute.
By modeling a rule and then immediately testing it, you have
demonstrated a good Studio modeling practice. Testing right away helps expose flaws in
the rules as you go along.
Model the second rule
Now that department codes are readily available for every item in a
customer’s shopping cart, you need to determine if any came from the Liquor
department.
It requires you to look inside the collection of items to see if an item
exists with department = '291'. You only need one check ID alert per
checkout transaction, this is a job for a collection operator.
A collection operator, because it acts on collections, evaluates once
per collection and not once per item, as the previous rule did. You only need one check
ID alert if the shopping cart contains any liquor. You do not want multiple alerts if
the shopping cart contains several liquor items.
Using the items alias, let’s add a Condition for the second rule
that determines if any liquor items exist in the customer’s shopping cart. To add this
condition, use the Collection operator →exists. The →exists operator
checks if a specific value exists for an attribute in the entity instances in the
collection. In this case the collection is items. So the condition expression is
items->exists(department='291').
Note: Always use plain single-quotation marks to specify a text string.
Then, define an Action for the second rule to assign a value of
true to the shopping cart’s checkID attribute, if any are
found. (Here, the assumption is that the checkID term will act as the alerting mechanism
to signal the cashier that an ID check is required during this checkout transaction.)
The second rule looks like this:
As mentioned earlier, using aliases to represent collections is
mandatory when collection operators (like →exists) are used.
Add rule statements for each rule as shown:
Test the second rule
Now, let’s re-run the same Ruletest as before:
The Condition and Action rule has worked as expected. A
customer’s shopping cart containing an item from the Liquor department has been
identified, and the checkID attribute is set to true to alert
the cashier to check the customer’s ID. Notice that the business rule statement has also
been posted in the Message box. Often, a simple message is all you need to raise
an alert or warning.
Note: Ordinarily, you would check
for Conflicts and Completeness before testing with data. But because this tutorial
focuses on advanced rule modeling features, the Analyze phase of the rule development
lifecycle is skipped.
Add more rules to the checks
Rulesheet
You have implemented two rules representing the first business rule in
the checks Rulesheet. Let's use this Rulesheet to model two more rules:
Check if a customer has a preferred account
Add the total price of items in a shopping cart
You use the output of the first rule in the next Rulesheet to filter
out customers who do not have preferred accounts, since that Rulesheet contains
promotional rules that apply only to preferred account holders.
The second rule, which calculates total price, must be included in the
first Rulesheet, because the second Rulesheet filters out customers who do not have
preferred accounts, whereas you want to calculate the total price for every
customer.
Check if a customer has a preferred account
Any customer who has a Preferred Account has an associated
preferredCard. So, let’s begin by defining the scope. Drag preferredCard under
Customer in the Vocabulary to the Scope pane. Give it the Alias
account.
The account alias represents a potential collection, where a customer
has a Preferred Card only if they have a Preferred Account. The
many-to-one nature of the association means a customer can have at most one account, and
other customers (within the same family) share the same Preferred Account. For
customers who do not have Preferred Accounts, the alias account represents
an empty collection (the collection contains no elements).
The →notEmpty collection operator checks a collection for the
existence of at least one element in the set. You can use this operator to check if a
customer has a preferred card. Because →notEmpty acts on a collection, the
account alias must be used with it.
Model a Boolean condition in row b in the Conditions pane
that uses the →notEmpty collection operator as shown. If the account alias
is not empty, the customer has a preferred account.
Let’s add an action to this rule that assigns the value of
true to the isPreferredMember attribute (from the
Customer entity) and posts an informational message, as shown. Recall that
isPreferredMember is a transient attribute whose value is only used in the
rules.
Now, whenever you want to know if a customer is a preferred customer,
simply refer to the value of the isPreferredMember attribute. This method of
flagging an entity with a Boolean attribute is convenient when modeling larger
Ruleflows. The value of the flag, like all attributes, carries over to other rules in
this and other Rulesheets in the same Ruleflow.
Now, let’s test this rule. For this rule to detect the presence of a
preferred card account associated with a customer, you need to provide the appropriate
test data. Drag the preferredCard entity and drop it onto the Customer
entity in the Ruletest Input.
Note: If you do not see the indented structure identical to the following image, delete
the entity and try again.
Run the Ruletest
The Output shows the results.
Notice that the isPreferredMember transient attribute has been
inserted and assigned the value true, and that an informational message
has been posted. Our rule has worked as expected.
Calculate the total price of items in a shopping cart
Finally, you add one more action-only rule to calculate the
totalAmount of all items in a customer’s shopping cart by using the collection
operator →sum as shown. This operator adds up the price attributes of all
elements in the items alias, and then assigns that value to the
totalAmount attribute.
Now, add a rule statement for this rule. Adding rule statements is good
practice, even if you do not post them as messages.
Finally, let’s test this rule. In the Input pane of the Ruletest
there is a customer with two items in their shopping cart. Let’s see if the last rule
calculates the totalAmount for the items in the Customer’s shopping cart.
Run the Ruletest
The following happened in this Ruletest:
The rules to determine if an ID check is required and if the customer is a
preferred card holder still work.
Note: You should
double-check cumulative test results to make sure nothing has broken along the
way.
The totalAmount attribute has returned a value of 8.98, which is
the correct sum of the prices of items 1 and 2, showing that the latest rule also
works as expected.
You have now completed modeling and testing our first Rulesheet.