]> err.no Git - sope/blobdiff - sope-core/NGExtensions/NGRuleEngine.subproj/README
Drop apache 1 build-dependency
[sope] / sope-core / NGExtensions / NGRuleEngine.subproj / README
index 906bc085c159877e1751980a16eadb4246fb6120..f14b0c15584e9bd824a9696542e77192f9cc43e8 100644 (file)
@@ -1,10 +1,10 @@
-# $Id$
-
 NGRuleEngine
 ============
 
 This is a rule engine inspired by the "BDRuleEngine" available from 
 NGRuleEngine
 ============
 
 This is a rule engine inspired by the "BDRuleEngine" available from 
-bDistributed.com (www.bdistributed.com).
+bDistributed.com (www.bdistributed.com) which in turn is inspired by the
+direct to web framework which is part of WebObjects.
+
 We have choosen different class names, so that NGExtensions can be used
 together with the BDRuleEngine framework.
 
 We have choosen different class names, so that NGExtensions can be used
 together with the BDRuleEngine framework.
 
@@ -17,6 +17,187 @@ classes:
   NGRuleContext
   NSRuleModel
 
   NGRuleContext
   NSRuleModel
 
+
+How does it work?
+=================
+
+The rule engine is an evaluator for a set of rules which map to values. It can
+be used to make all kinds of actions configurable without being required to
+write code.
+
+Example Ruleset:
+  (
+    "context.soRequestType='WebDAV'  => renderer = 'SoWebDAVRenderer' ; high",
+    "context.soRequestType='XML-RPC' => renderer = 'SoXmlRpcRenderer' ; high",
+    "context.soRequestType='SOAP'    => renderer = 'SoSOAPRenderer' ;   high",
+    "context.soRequestType='WCAP'    => renderer = 'SoWCAPRenderer' ;   high",
+    "*true* => renderer = 'SoDefaultRenderer' ; fallback",
+  )
+
+This is a rule from SOPE which selects the render class for SoObjects. As you
+can see a rule has a left hand side, eg:
+
+  context.soRequestType='WebDAV'
+
+and a right hand side, eg:
+
+  renderer = 'SoWebDAVRenderer'
+
+further control specifiers like the priority of the rule in the set (high) can
+be attached.
+
+The left hand side is just a regular EOQualifier which is evaluated against
+a rule context (an object of the NGRuleContext class). A rule context is the
+entry object for all rule processing.
+
+To configure rule evaluation, you need to set some variables in the context,
+those variables are basically the "parameters" of the rule. Eg in the above
+case we use:
+  [self->dispatcherRules reset];
+  [self->dispatcherRules takeValue:_rq           forKey:@"request"];
+  [self->dispatcherRules takeValue:[_rq headers] forKey:@"headers"];
+  [self->dispatcherRules takeValue:[_rq method]  forKey:@"method"];
+  [self->dispatcherRules takeValue:_ctx          forKey:@"context"];
+
+'dispatcherRules' is the NGRuleContext object. Because we reuse the same
+context for each WORequest, we need to 'reset' the context to remove all old
+information.
+
+As you can see the rule context gets set the 'context' variable which is used
+in the qualifier - "context.soRequestType='WebDAV'". If this left hand side
+(LHS) qualifier evaluates to true, the RHS will be run.
+
+
+So lets get to the right hand side. It is the so called "Assignment" and is
+actually someone similiar to a WOAssociation. The actual operation is triggered
+by some subclass of NGRuleAssignment in the -fireInContext: method.
+
+In the above example the RHS is
+
+  renderer = 'SoWebDAVRenderer'
+
+this says that if the rule context is asked for a value of 'renderer', the
+assignment will return the 'SoWebDAVRenderer' string constant.
+
+Note: the assignment does _not_ set the value in the rule context.
+TODO: should it set the value in the context? ;-)
+
+You can have as many assignment as you like. Assignments are only run if the
+user asks for a key which is set by the assignment!
+
+
+Now that we have the basics, how do we use the rule context? Here is a small
+example:
+
+  NGRuleModel   *model;
+  NGRuleContext *context;
+  
+  /* setup */
+  model   = [[NGRuleModel alloc] initWithContentsOfFile:@"myrules.plist"];
+  context = [NGRuleContext ruleContextWithModel:model];
+
+  /* fill in parameters */
+  [context takeValue:@"10" forKey:@"age"];
+
+  /* query values that depend on the parameter */
+  [context valueForKey:@"color"];
+
+A sample myrules.plist:
+
+  ( "age < 5 => color = 'white'", "age > 4 => color = 'green'" )
+
+This would return 'green' in the above example (because age = 10 is >4).
+
+Note that the cool aspect of the rule context is that the rule evaluation is
+queried using regular key/value coding methods! This way you can easily bind
+values to SOPE templates, eg:
+
+  TableCell: WOGenericContainer {
+    elementName = "td";
+    bgcolor     = rules.color;
+  }
+
+This assumes that the component returns a rule context in the 'rules' method.
+A component setup like this can be easily customized just by changing the rules
+avoiding the requirement to hack code.
+
+
+Another neat application for rules is the selection of the "next page", that
+is, to control the flow of a web application.
+Consider a ruleset like this:
+
+  ( "document.status = 'saved'    => pageName = 'MyReviewPage'",
+    "document.status = 'created'  => pageName = 'MyReviewPage'",
+    "document.status = 'reviewed' => pageName = 'MyPublishPage'" )
+
+and code like this:
+
+  - (id)showNextPage {
+    return [self pageWithName:[rules valueForKey:@"pageName"]];
+  }
+
+This code will automatically determine the correct page to be shown depending
+on the rules and the state of an object. Eg if you later decide that the
+publish page should also been selected for saved docuents which are green, just
+enhance the rule to:
+
+  ( "document.status = 'saved'    => pageName = 'MyReviewPage'",
+    "document.status = 'created'  => pageName = 'MyReviewPage'",
+    "document.status = 'reviewed' => pageName = 'MyPublishPage'",
+    "document.status = 'saved' AND document.color = 'green' 
+     => pageName = 'MyPublishPage'; priority = high",
+  )
+
+The rule context has a neat shortcut method in case you want to store rules in
+the defaults system:
+  
+  context = [NGRuleContext ruleContextWithModelInUserDefault:@"MyRules"];
+
+Since rules are often used to customize the behaviour of an application, this
+is quite useful.
+
+
+Another shortcut method you can use is the evaluation of a ruleset for a set
+of objects. Eg if you want to get the color of a set of objects with the 'age'
+property, you can run:
+  
+  colors = [rules valuesForKeyPath:@"color"
+                  takingSuccessiveValues:ageObjects
+                  forKey:@"document"];
+
+This will walk over the 'ageObjects' array and perform a
+
+  [rules takeValue:ageObject forKey:@"document"]
+
+for each object and add the result of
+
+  [rules valueForKeyPath:@"color"]
+
+to the result array.
+
+
+Finally remember that assignment results do not need to be base values, they
+can also be complex objects, eg:
+
+  [rules takeValue:bossObject forKey:@"boss"];
+  [rules takeValue:secretary  forKey:@"secretary"];
+
+  contactEMail = [rules valuesForKeyPath:@"contact.email"
+                        takingSuccessiveValues:mailObjects
+                        forKey:@"mail"];
+
+with the ruleset:
+
+  ( "mail.priority = 'high'   => contact = boss",
+    "mail.priority = 'normal' => contact = secretary",
+    "mail.priority = 'low'    => contact = secretary" )
+
+Note that another speciality with the above ruleset is that it uses
+NGRuleKeyAssignment assignments, that is, it retrieves the value of the
+assignment from the rule context (the boss or secretary objects previously
+set as parameters).
+
+
 Priorities
 ==========
 
 Priorities
 ==========