Triggers and Reactors

Each Dynamic Rule Reactor is triggered by a change. There are 3 types of change: Create , Modify , and Delete. Since creation of a dynamic rule can be a modification of a non-dynamic rule found in a Design, creation and modification are indistinguishable, and handled by the same Reactor.

Intent uses Dynamic Rules for many purposes behind the scenes, and not all of these uses are subject to Reactors. Reactors are only enabled in certain situations. For example, creation of a rule via the Part.CreateDynamicRule API enables reaction, but an undo operation which removes (deletes) that same rule will not. Undo and Redo operations do not enable reactions to take place. There are several other situations where reactions are suppressed.

When a Dynamic Rule change is made, Intent checks to see whether reactors should be invoked. If so, it looks for reactors using a name-matching process. For Basic Rules, the name is constructed from the rule name followed by an underscore and the trigger name. For Child Rules, the names are just the trigger name, and must be present on the Part. In addition, each trigger has a corresponding "allow" name that can be used to prevent the change. If no matching name is found, no further action takes place.

If a matching "allow" name is found, it is evaluated to determine if the change should proceed. If not present, the default behavior is to allow the change. The allow methods always return a Boolean, not an action list. If allowed, then the corresponding reactor method is evaluated if present.

All non-allow reactors return an "action list". This is structured as a list of actions, each of which is a property list (a "plist") of Name, value pairs. The permitted Names are a function of the actual reactor type, but must always contain :action followed by one of :CreateDynamicRule, DeleteDynamicRule, or CreateDynamicPart. Deleting of a dynamic part is performed by DeleteDynamicRule. Any number of actions can be present in the list, so a single rule change can generate any number of other changes.

Once the action list is obtained, it is processed. Note that the rules being affected by an action can themselves be subject to Reactors . Thus, there can be a recursive cascade of changes, and it is in this potential complexity that most of the difficulties with Reactors arise. The processing of reactors is not inside a single outer Intent evaluation, but instead inside a potentially deep tree of Dynamic Rule compilation, and as a result, there is very little debugging support for developers.

Basic Rule Triggers

Rule Invoked Returns
<ruleName>_allowModify? Prior to modification of <ruleName> Boolean indicating whether modification is permitted
<ruleName>_preModify Prior to modification of <ruleName> List containing a set of actions to be performed
<ruleName>_postModify After modification of <ruleName> List containing a set of actions to be performed
<ruleName>_allowDelete? Prior to deletion of <ruleName> Boolean indicated whether deletion is permitted
<ruleName>_preDelete Prior to deletion of <ruleName>; if <ruleName> is a Child Rule, this occurs before any deleteSelf triggers. List containing set of actions to be performed. The Reactor can reference the rule.
<ruleName>_postDelete After deletion of <ruleName>; if <ruleName>was a Child Rule, this occurs after any deleteSelf triggers List containing set of actions to be performed. The Reactor cannot reference the rule, unless it was shadowing a Design rule.

Triggers for Child Rules

The following triggers and their Reactors are used to indicate when dynamic parts are added and deleted. These Reactors are normally coded as "design rules" (non-dynamic) in the Design of the part that is being added or deleted.

Rule Invoked Returns
preCreateSelf Just after the Part is created List containing a set of actions to be performed
postCreateSelf After the preCreateSelf step List containing a set of actions to be performed
allowDeleteSelf Prior to deletion Boolean indicating whether deletion can proceed
preDeleteSelf Prior to deletion, and prior to deletion of any dynamic rules underneath List containing a set of actions to be performed
postDeleteSelf After deletion of all dynamic rules underneath, but just prior to deletion of the Part itself List containing a set of actions to be performed

Actions

"Actions" are instructions for automated dynamic rule creation or deletion. These are the only actions that are supported.

Each action is specified as a list in "plist format" (alternating names and values). The first name is always :Action, followed by one of the following:
  • CreateDynamicRule creates an Any type rule, on any part, with any name, with any formula. Required fields are: part which specifies the parent (owner) of the new rule, name which is the name of the new rule and must be an Intent Name , and formula which is the formula for the new rule. The formula must be a string, even if it's a simple constant such as a number.
  • CreateDynamicPart creates a Child Rule. Required fields are: part which specifies the parent (owner) of the new rule, name which is the name of the new rule and must be an Intent Name. Following those required fields are the parameters and formulas of the new child rule, including the 'design' pseudo-parameter. The parameters must be Names. But the formulas are strings.
  • DeleteDynamicRule deletes any dynamic rule (or dynamic "modifying" rule). Required fields are: part which specifies the parent (owner) of the rule to be deleted, and name which is the name of the rule to be deleted. Note that if the deleted rule is a child rule, all dynamic/modified rules underneath will also be deleted and any relevant event Reactors will be recursively fired.

Action lists are constructed at the time of the trigger. It is normal to use rules to build action lists, and most of the features of the Intent model are available. The primary restrictions are due to the nature of the state of Intent when the actions are processed. Since the initial trigger is some kind of dynamic rule modification, Intent is in the process of modifying it state. Because changes can be recursive, it can be in a very complicated state and difficult for a developer to understand the precise presence or not of various dynamic rules subject to changes.

There is no "evaluate" action, and none is required. If you want to evaluate a rule as part of your Reactor, just do it. It doesn't have to contribute to the returned action list.

Important Notes

Because reactors use the Intent evaluator, it is illegal to trigger reactors while inside an evaluation. That is, it is not permitted to call the Intent API from inside a rule, changing a dynamic rule which will trigger a reactor. An exception will occur, and will note that an attempt to re-enter the evaluator was made. This is the primary reason why reactors must be triggered by API calls, usually in response to UI actions.

Actions will recursively trigger any relevant reactors. There is no way to suppress this behavior.

All the actions in the action-list are processed at once. Subsequent actions in the action-list cannot access the results of earlier actions in the action-list.

All the actions in the action-list take place within the same undo-group as the original trigger, including any actions invoked by cascading events. This implies that if the user "undoes" the original operation, all the Reactor-controlled changes will also be undone, in order to bring the model back to the original state. Reactors are not triggered when the operations happen as part of undo-ing or redo-ing.