On a related note, I've been working on a mechanism for using
decision tables using a spreadsheet such as Excel. The great advantage of
decision tables is that conditions and the actions are completely separate and
can be rearranged by users at will. I'm putting together a little "hotel
booking" app that checks for various conditions: is the user a silver level
member? is the user a gold level member? is a deluxe room available? is a
penthouse room available?
Actions such as "book standard room", "book deluxe room", "book
penthouse", and "issue discount" can all be mixed and matched to reflect what
users want to do. These actions equate to methods that will be run. The neat
thing about a rules engine is that users can change both the actions run AND the
order they're run in by simply altering the spreadsheet. And since many users
are very comfortable with the idea of laying out a decision table (or can be
taught it very quickly), it means that my code implementation is not tied to the
business logic as of the time I coded it.
Anyway, I'm going to be putting a writeup and sample code together
and will have it ready for my February newsletter. There's a little bit of funky
code to parse the spreadsheet file and get it into a useful data structure, but
otherwise, it's pretty straightforward.
From:
[EMAIL PROTECTED] [mailto:[EMAIL PROTECTED] On Behalf Of
Peter Bell
Sent: Thursday, January 19, 2006 5:19 PM
To: [email protected]
Subject: RE: SPAM-LOW: Re: [CFCDev] Generic Business Rules - Where?!
Sent: Thursday, January 19, 2006 5:19 PM
To: [email protected]
Subject: RE: SPAM-LOW: Re: [CFCDev] Generic Business Rules - Where?!
Hi
Seth,
Firstly, many thanks for all of the responses. Greatly appreciated. I owe
you one and as soon as I'm smart enough to answer (rather than just ask)
questions feel free to take me up on this (or if you're ever in NYC . .
.)!
Secondly, I think VisitorContext is much closer than globals. I'm open to
other wording as I'm not sure this is 100% it, but it's closer than anything I
can come up with, so that's my operating name for now!
I
believe that objects are passed by refernce in CF, so that should be
relatively cheap both in terms of memory and processing time (please chime in
anyone who knows this to be wrong). I think having composition of UserContext
and PageContent using methods of the user and page objects is a brilliant idea
as it puts the responsibilities back onto the appropriate objects, and the
VisitorContext acting as a single repository for all of the combined contexts
makes all the sense in the world. I would probably also give it direct
responsibility for obtaining generic request information (IP address, current
server date/time, browser type and the like), just refactoring that out into a
different object if the need ever arose. It would only require the recoding of
the Init() method for the VisitorContext object, so it should be an easy
change.
Bottom
line - great feedback. If anyone else chimes in I'm open to the input, but let
me go code this and see how it works.
Thanks
again!
Best
Wishes,
Peter
Peter
---------------------------------------------------------------Original Message-----
From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED] On Behalf Of Seth Petry-Johnson
Sent: Thursday, January 19, 2006 4:45 PM
To: [email protected]
Subject: SPAM-LOW: Re: [CFCDev] Generic Business Rules - Where?!I've never written the type of app we're discussing before, but I'm also the only one responding to your post, so hopefully my thoughts are better than nothing :)If we fall back on the rule of "identify what changes, and encapsulate it" then we need to find a way to encapsulate all of the things that change about a visitor request... the user group, the URL its visiting, etc. This is basically the gobals object you mentioned, but I'd call it VisitorContext or something like that. (To me, "global" indicates a globally available, usually static variable, but "context" indicates per-user values for a predefined list of properties, which is exactly what you're describing).Now, the best way to expose this data to your objects is to pass this context object as a method argument to your CFCs. Let's say you have a TemplateManager.cfc that is responsible for knowing where templates are stored and retrieving them and preparing them for use during a visitor session. You could do TemplateManager.getTemplate(visitorContext), and then leave it up to TemplateManager to use VisitorContext's public interface to gather whichever pieces of information are relevant. Maybe it only needs the URL being visited, or maybe it needs the user's group assignment, username, logged in status and IP address, it doesn't matter: let the TemplateManager ASK the context object for what it needs to know instead of trying to tell it what you think it might need.If you have a lot of information available for a visitor you might want to use composition within the context object: maybe there's a UserSession object that stores session specific info and PageView object that stores information about a specific page view like its filename and the visitor's IP address. In that case I'd wrap both of those smaller objects into the VisitorContext and then have the context act as a facade to them. That way you expose a single, consistent interface that your template objects can code against without breaking encapsulation.Does that help at all? There may be better ways, perhaps someone wiser than I will chime in.On 1/19/06, Peter Bell <[EMAIL PROTECTED]> wrote:Hi Seth,That is exactly it. If I have a RULE for selecting a TEMPLATE that may need information about the USER, PAGE and possibly other things, we have a lot of objects or candidate objects (in all caps) that could be responsible for enforcing the rules.When I write it out this way, it seems to me that it is up to the template to know which template to return, so I'd probably have a SelectTemplate() or CallTemplate() method that would encapsulate the rules.My question is how does Template.SelectTemplate() KNOW the Session.User.Group or Page.Name or the Request.IPAddress without breaking encapsulation?The best answer I have so far is as follows:Have a small set of global variables (via getter methods) exposed through a GLOBALS object (better name anyone?) which has a request scope and acts as an interface. Bad news is that I can't change that list of getters without rewriting my code, but the base list is fixed anyway. I've been doing this 6 years and I CAN'T throw away basic concepts like user groups - old apps logically depend upon them. The benefit of the interface is that if I change where I store Session.User.Group or what I call it or how I store it (data type), I only need to update my interface (the GLOBALS object) to get all of the necessary properties and to transform them as required for backwards compatibility, so request.GLOBALS would act as an internal API or list of variables/getters that users could access.Smells anyone? Better ideas? Flames? (I'll take whatever I can get!!!)Best Wishes,
Peter-----Original Message-----
From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED]] On Behalf Of Seth Petry-Johnson
Sent: Thursday, January 19, 2006 3:31 PM
To: [email protected]
Subject: Re: [CFCDev] Generic Business Rules - Where?!Sorry if I'm being dense today, but I'm finding it difficult to create a mental model of the system you're describing. This is what I understand so far:1) You have a form that lets users create simple rules that act upon different variables that, I assume, are exposed by your framework or runtime environment.2) A publishing script parses those rules and creates CFML code that implements them. This CFML code is included during a visitor's page view to control what that visitor sees.3) As you move from CF 5 to MX you are looking to replace the procedural code generated in step 2 with something "more OO"; you want the generated code for your rules you use CFC method calls to implement the logic defined in step 1.4) You are struggling with the best way to expose the context of the visitor's session to the objects that enforce the rules.Am I getting closer?On 1/19/06, Peter Bell <[EMAIL PROTECTED] > wrote:Not quite.We have a rules builder.- Users use a form which allows them to select from a list of globally available request variables that clients have asked to base rules on in the past. Examples would be user group (always set to "visitor" if user not authenticated), page name, section name, time of day, day of week (different website skin on the weekend anyone?!), etc.- They then select from a list of operators (equal to, less than, longer than, etc.) and then either select from a pick list of statics or variables or enter a custom static.- They then select the appropriate actions for both the evaluates true and evaluates false tree (0..n actions each)- They select the parent rule (is it a top level rule or a child of the true or false paths of one of the other rules)- They set a precedence (action order) for any sibling rules within the rules hierarchy- They hit submitMy script checks for a bunch of possible logical errors (primarily based upon data type matching)If the rule is invalid, I redisplay the form along with any errors for them to try againIf it is valid, I save all of the little bits of data to a couple of SQL tables (one for rules and another for actions)In CF 5, the user then calls a publisher script that generates well formed CFML from the above data using nested cfif's and I include it in the appropriate place (there is a template selector custom tag, etc.).The problem I have in MX is finding the best place to save the logic to. As I think about it, latest thinking is as follows. Each rule builder should be saved to the object the rules relate to (not which ones they use data from). For instance, a template object will have a GetTemplate() method which includes all of the appropriate logic and handles getting the right template.The only downside is that GetTemplate() may need to know user group, page name, section name, URL, or a bunch of other things from my "variables list". Do I (shudder) just define a formal interface that always gets and displays those global variables like a GlobalVariables struct or object with request scope? I suppose a slightly neater solution would be at generation time to generate an array of all of the variables and which objects they belong to that are currently within a given rule set. I could then dynamically concatenate code to make the calling script pass those variables or make the GetTemplate() call them using the appropriate getters. I am not a big fan of getters as I like to keep my objects as shy as possible, but I can't immediately see a better approach.Input from anyone who has ever actually written an object oriented site would be much appreciated!!!Best Wishes,
Peter-----Original Message-----
From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED]] On Behalf Of Seth Petry-Johnson
Sent: Thursday, January 19, 2006 1:36 PM
To: [email protected]
Subject: Re: [CFCDev] Generic Business Rules - Where?!Just to make sure I understand your situation:1) Users write out their page logic using a simplified sort of pseudo-code2) The pseudo-code is then submited to a parser or "compiler" which generates actual CFML3) That CFML is saved to a file which is then included during a page visitIs that correct? Are you looking to replace step 2 or step 3 (or both) with an OOP approach?
On 1/19/06, Peter Bell <[EMAIL PROTECTED] > wrote:End user uses a web based admin to generate business rules (Simple "if
variable operator variable/static then action else action endif" with
support for nesting and precedence). They also get a "rebuild" button which
takes the individual elements (variable, operator, action, etc.) and
concatenates well formed Cold Fusion which it then saved to a CFM that gets
included at design time. That was how the CF5 app worked. Now the string
would be saved as a method of a cfc (I'm writing my cfc generator as we
speak) so semi-technical end users can effectively change simple business
logic (if this user group show that template, etc.) without breaking the app
or affecting the runtime performance.
I'm fanatical about letting end users change just about anything. That way I
can charge them a monthly fee for the use of my software rather than having
to actually work for a living and manually make the changes they require :->
-----Original Message-----
From: [EMAIL PROTECTED] [mailto: [EMAIL PROTECTED]] On Behalf
Of Phillip Senn
Sent: Thursday, January 19, 2006 9:14 AM
To: [email protected]
Subject: RE: [CFCDev] Generic Business Rules - Where?!
> implementing the rules was easy - I just needed a global list of
> variables
and to generate the logic at design time
Are you saying you have somehow opened up your program to the end user?
----------------------------------------------------------
You are subscribed to cfcdev. To unsubscribe, send an email to
[email protected] with the words 'unsubscribe cfcdev' as the subject of the
email.
CFCDev is run by CFCZone (www.cfczone.org) and supported by CFXHosting
( www.cfxhosting.com).
An archive of the CFCDev list is available at
www.mail-archive.com/[email protected]
----------------------------------------------------------
You are subscribed to cfcdev. To unsubscribe, send an email to [email protected] with the words 'unsubscribe cfcdev' as the subject of the email.
CFCDev is run by CFCZone ( www.cfczone.org) and supported by CFXHosting ( www.cfxhosting.com).
An archive of the CFCDev list is available at www.mail-archive.com/[email protected]
----------------------------------------------------------
You are subscribed to cfcdev. To unsubscribe, send an email to [email protected] with the words 'unsubscribe cfcdev' as the subject of the email.
CFCDev is run by CFCZone (www.cfczone.org) and supported by CFXHosting ( www.cfxhosting.com).
An archive of the CFCDev list is available at www.mail-archive.com/[email protected]----------------------------------------------------------
You are subscribed to cfcdev. To unsubscribe, send an email to [email protected] with the words 'unsubscribe cfcdev' as the subject of the email.
CFCDev is run by CFCZone ( www.cfczone.org) and supported by CFXHosting (www.cfxhosting.com).
An archive of the CFCDev list is available at www.mail-archive.com/[email protected]
----------------------------------------------------------
You are subscribed to cfcdev. To unsubscribe, send an email to [email protected] with the words 'unsubscribe cfcdev' as the subject of the email.
CFCDev is run by CFCZone (www.cfczone.org) and supported by CFXHosting ( www.cfxhosting.com).
An archive of the CFCDev list is available at www.mail-archive.com/[email protected]----------------------------------------------------------
You are subscribed to cfcdev. To unsubscribe, send an email to [email protected] with the words 'unsubscribe cfcdev' as the subject of the email.
CFCDev is run by CFCZone ( www.cfczone.org) and supported by CFXHosting (www.cfxhosting.com).
An archive of the CFCDev list is available at www.mail-archive.com/[email protected]
----------------------------------------------------------
You are subscribed to cfcdev. To unsubscribe, send an email to [email protected] with the words 'unsubscribe cfcdev' as the subject of the email.
CFCDev is run by CFCZone (www.cfczone.org) and supported by CFXHosting (www.cfxhosting.com).
An archive of the CFCDev list is available at www.mail-archive.com/[email protected]
You are subscribed to cfcdev. To unsubscribe, send an email to [email protected] with the words 'unsubscribe cfcdev' as the subject of the email.
CFCDev is run by CFCZone (www.cfczone.org) and supported by CFXHosting (www.cfxhosting.com).
An archive of the CFCDev list is available at www.mail-archive.com/[email protected] ----------------------------------------------------------
You are subscribed to cfcdev. To unsubscribe, send an email to [email protected] with the words 'unsubscribe cfcdev' as the subject of the email.
CFCDev is run by CFCZone (www.cfczone.org) and supported by CFXHosting (www.cfxhosting.com).
An archive of the CFCDev list is available at www.mail-archive.com/[email protected]
