NO MORE CAREER
POLITICIANS!
Get Out Of Our House: Replacing congress with TRUE citizens!
Contact Doug!
Learn About Doug!
View Doug Boude's online resume
updated 11/18/2009

View Doug Boude's profile on LinkedIn
Link to me!

Follow Doug Boude on Twitter
Follow me!

Be Doug's friend on Facebook
Befriend me!
(I promise not to follow you home)
OO Lexicon
Chat with Doug!
Recent Entries
You may also be interested in...
Web Hosting

best web hosting - top web hosting sites, thetop10bestwebhosting.com

Czech your Page Rank!
Check Page Rank of any web site pages instantly:
This free page rank checking tool is powered by Page Rank Checker service
Surf's Up!
Visit Egosurf.org and massage YOUR web ego!
My Score: 9,001
Doug's Books

Read (and recommend)

  • Men are from Mars, Women are from Venus
  • The Wisdom of Crowds: Why the Many Are Smarter Than the Few and How Collective Wisdom Shapes Business, Economies, Societies and Nations
  • Blink: The Power of Thinking Without Thinking
  • Head First Design Patterns
  • Transact-SQL Programming
  • What's So Amazing About Grace?
  • Just So Stories (Rudyard Kipling collection)

Reading

  • Prayer: Does it Make Any Difference?
  • Data Mining (Practical Machine Learning Tools and Techniques)
<< November, 2008 >>
SMTWTFS
1
2345678
9101112131415
16171819202122
23242526272829
30
Search Blog

Recent Comments
Re: Railo 3.1 on Windows Server 2008 and IIS7 - Part 3 of 3 (by Jon at 8/27 2:04 PM)
Re: Hosts File Changes Not Acknowledged on Vista 64 (by Spacy at 8/24 3:46 PM)
Re: THE DAY CFUNITED DIED (by ComboFusion at 8/23 10:50 AM)
Re: My Grandpa (by Tasha at 8/10 4:29 PM)
Re: Just What IS a 'Service Layer', Anyway? (by dougboude at 8/02 10:10 AM)
Re: Just What IS a 'Service Layer', Anyway? (by Isaac at 8/02 2:25 AM)
Re: PayPal IPN Coldfusion CFC (by Soyestudiambre at 7/25 6:12 PM)
Re: PHP vs COLDFUSION (by Tony Garcia at 7/17 11:24 AM)
Re: PHP vs COLDFUSION (by dougboude at 7/14 8:45 AM)
Re: PHP vs COLDFUSION (by Lola LB at 7/14 5:51 AM)
Categories
Archives
Photo Albums
Funnies (5)
Family (3)
RSS

Powered by
BlogCFM v1.11

01 September 2006
DeoxyRiboNucleic LISTS (DNL)
I love lists. Although they *seem* to take a backseat to their apparently more elegant "collection brethren" Structure and Array, to me Coldfusion lists have a hidden potential rivaled only by that ultimate List of all Lists, DNA. Four individual components used to create patterns within patterns within patterns, and within those patterns the blueprints for an entire species and everything about it...it's just amazing to me. I have personally used Coldfusion lists in similar fashion (though much less glorious) and so wanted to share a little of that passion and my reasons for it, in the hopes that others too (if they don't already) might be able to appreciate the beauty of a List as I do.

Here's a scenario:

I'm tasked with writing a component to perform e-commerce transactions against one of several different (and as yet unknown) merchant systems. I do know that there are only a handful of transaction types that can be performed (Sale, Pre-Authorization, Capture, Void, or Credit), and that along with each of these transaction types there will be some arguments that are required and some that are not. I also know that each of these arguments will need some additional validation before being submitted to the merchant system, and every argument's validation needs could potentially be different. On top of that, because of the fact that we'll be adding in new merchant systems all the time, it's entirely possible that a whole new set of validation rules could be needed tomorrow in addition to what's already in existence. And to top it all off, because the number of arguments could change overnight, I'm going to rely on the receipt of an argumentCollection rather than hard code my arguments via CFARGUMENT.

I actually did have this scenario a couple of years back, and although there are always many approaches that can be taken to accomplishing a task, I chose to utilize a List to hold my argument validation rules. Why a list? Because since I was a contractor, I knew I wouldn't be around the next time the rules changed, and being the nice guy that I am, wanted to make it as simple as possible for the person who would come after me. By encoding my validation rules for each merchant system within a List (just like a strand of DNA) and by receiving my arguments via a collection, the next programmer only needed to edit one line of an entire CFC in order to make their changes.

Here is an example of validation rules encoded within a List:

<cfset var validationRules = "TransactionType~Always~L|S-A-C-V-CR,CCNum~S-A-V-CR~CC,ExpDate~S-A-V-CR~D,Amount~S-A-CR~N|0,RefNumber~C-V-CR~T|7" />
<!--- --->
<!--- --->


In essence, just like DNA, it is a series of nested lists. Here is the key for deciphering the List:

  • Outer list: [Argument and associated rules],[Argument and associated rules], etc. delimiter ","
    • Argument and associated rules: [Arg Name]~[required when]~[validation] delimiter "~"
      • Required when: [Always||S||A||C||V||CR] delimiter "-"
      • validation: [validation type | validation param] delimeter "|"
        • validation type: [L||T||N||D||CC]
        • validation param: [[list of values]||[integer]]

In the sample list then, we can see for example that the argument "TransactionType" is always required and its value should be any of "S","A","C","V",or "CR".  The argument RefNumber is only required when the transactiontype is "C","V", or "CR", and when it is required should be a text value with length of at least 7 characters. You get the picture. SO then when our merchant system adds a new transaction type, such as "Reversal", we will add the letter "R" to represent this transaction type to any of the existing arguments that will be required for a Reversal. We'll then add any additional arguments with the appropriate values. All done in a single variable within the CFC.

What do we do with such a list? What beast is it that can consume it and actually use it? Well, You write yourself a method that loops through it and, one major list item at a time, interpret whatever rules have been defined and apply them. I've created a template and cfc for demonstrating this in action.

Step 1: template instantiates Payit.cfc, sets up an argument collection, and calls the "DoTransaction" method, passing in the arg structure.
<cfscript>
    argstruct = structnew();
    argstruct.TransactionType = "CR";
    argstruct.ccnum = "423339010000930";//cc number is only 15 characters...should be 16
    argstruct.expdate = "05/01/08";
    argstruct.amount = "35.67";
    argstruct.refnumber = "0098345";
    objPay = createobject("component","Payit").init();
    results = objPay.DoTransaction(argumentcollection=argstruct);
</cfscript>
<cfdump var="#results#" label="Results">

Step 2: "DoTransaction" first passes the incoming argument collection to the "validateArgs" method.
<cffunction name="DoTransaction" access="public" returntype="struct" output="false">
    <cfset var incomingArgs = arguments />
    <cfset var results = structnew() />
    <cfset results = validateArgs(incomingArgs) />
    <cfif results.error IS "[NONE]">
        <!--- Do a transaction... --->
    </cfif>
    <cfreturn results />
</cffunction>   

Step 3: "validateArgs" performs validation and passes a structure back to "DoTransaction" containing the results. (view the entire method at the bottom of this post)

Step 4: DoTransaction either performs its transaction, or it halts processing. In either case, it returns the validation structure to the calling page where it is output. In our example here, the credit card number being submitted was too short, and thus the following response:



Conclusion
Even though my example of using a list to encode detailed validation rules was very much geared towards an e-commerce example, I'm confident that one could find other uses of "ColdFusion DNA" by applying just a little bit of imagination and creativity. In any event, I recommend at least considering a list rather than automatically assuming a structure or array is the best fit every time.

Doug out.

[Download template and CFC]

validateArgs Method:
    <cffunction name="validateArgs" access="private" returntype="struct">
        <cfargument name="args" type="struct" required="true" />
        <cfset var validation = structnew() />
        <cfset var mode = "" />
        <cfset var currlist = "" />
        <cfset var validationRules = "TransactionType~Always~L|S-A-C-V-CR,CCNum~S-A-V-CR~CC,ExpDate~S-A-V-CR~D,Amount~S-A-CR~N|0,RefNumber~C-V-CR~T|7" />
        <cfset validation.success = "true" />
        <cfset validation.error = "[NONE]" />
        <!--- Since everything will always hinge upon the existence of the TransactionType argument, we check for it first thing, stopping if it isn't present.' --->
        <cfif not structkeyexists(args,"TransactionType")>
            <cfset validation.success = false />
            <cfset validation.error = "Transaction Type was required but not passed in" />
            <cfreturn validation />
        <cfelse>
            <!--- grab value of the transaction type. It may not be a valid value, but we'll find that out soon enough' --->
            <cfset mode = args.TransactionType />
            <!--- stuff our mode into the validation structure for troubleshooting purposes --->
            <cfset validation.mode = mode />
           
            <cfloop list="#validationRules#" index="i" delimiters=",">
                <!--- if this item is required for our current mode, make sure it exists... --->
                <cfif listgetat(i,2,"~") IS "Always"><!--- if this argument is always required, check for its existence --->
                    <cfif not structkeyexists(args,listfirst(i,"~"))>
                        <cfset validation.success =  "false" />
                        <cfset validation.error = listfirst(i,"~") & " was required but not passed in." />
                        <cfreturn validation />
                    </cfif>
                <cfelse><!--- our item is only required *sometimes*... --->
                    <cfif listgetat(i,2,"~") IS NOT "">
                        <cfif listfind(listgetat(i,2,"~"),mode,"-") neq 0><!--- if the current mode is in our list of required modes, make sure this arg is present... --->
                            <cfif not structkeyexists(args,listfirst(i,"~"))>
                                <cfset validation.success = "false" />
                                <cfset validation.error = listfirst(i,"~") & " was required but not passed in." />
                                <cfreturn validation />
                            </cfif>
                        </cfif>
                    </cfif>
                </cfif>
                <!--- if this item needs requires that the argument's value be validated, do so' --->
                <cfif listlen(i,"~") eq 3><!--- a third item indicates some validation needed --->
                    <cfset currlist = listgetat(i,3,"~") /><!--- grabbing the validation portion of our item --->
                    <cfif listfind(listgetat(i,2,"~"),mode,"-") neq 0><!--- if this was a required item in the first place... --->
                        <cfswitch expression="#listfirst(currlist,"|")#"><!--- perform the needed validation --->
                            <cfcase value="L"><!--- value must be in a specified list of values... --->
                                <cfif listfind(listlast(currlist,"|"),args[listfirst(i,"~")],"-") eq 0>
                                    <cfset validation.success = "false" />
                                    <cfset validation.error = listfirst(i,"~") & " contained an invalid value." />
                                    <cfreturn validation />   
                                </cfif>
                            </cfcase>
                            <cfcase value="T"><!--- value must be text of at least so many characters... --->
                                <cfif len(args[listfirst(i,"~")]) LT listlast(currlist,"|") >
                                    <cfset validation.success = "false" />
                                    <cfset validation.error = listfirst(i,"~") & " was not at least " & listlast(currlist,"|")  & " characters in length."  />
                                    <cfreturn validation />   
                                </cfif>
                            </cfcase>
                            <cfcase value="N"><!--- value must be a number greater than x... --->
                                <cfif not isnumeric(args[listfirst(i,"~")]) OR args[listfirst(i,"~")] lte listlast(currlist,"|")>
                                    <cfset validation.success = "false" />
                                    <cfset validation.error = listfirst(i,"~") & " is not a numeric value or is less than or equal to " & listlast(currlist,"|")/>
                                    <cfreturn validation />           
                                </cfif>
                            </cfcase>
                            <cfcase value="D">
                                <cfif not isDate(args[listfirst(i,"~")])>
                                    <cfset validation.success = "false" />
                                    <cfset validation.error = listfirst(i,"~") & " is not a valid Date."/>
                                    <cfreturn validation />               
                                </cfif>
                            </cfcase>
                            <cfcase value="CC"><!--- value must be a credit card number ... --->
                                <cfif len(args[listfirst(i,"~")]) NEQ 16 ><!--- this test done purely for simulation. actual cc validation is more involved and complex. --->
                                    <cfset validation.success = "false" />
                                    <cfset validation.error = listfirst(i,"~") & " was not a valid credit card number."/>
                                    <cfreturn validation />   
                                </cfif>
                            </cfcase>
                            <cfdefaultcase><!--- the specified validation rule isn't accounted for! throw error' --->
                                    <cfset validation.success = "false" />
                                    <cfset validation.error = listfirst(i,"~") & " was given a validation code that was not recognized."/>
                                    <cfreturn validation />   
                            </cfdefaultcase>
                        </cfswitch>
                    </cfif>
                </cfif>
            </cfloop>       
        </cfif>
        <cfreturn validation />
    </cffunction>



Posted by dougboude at 4:32 PM | PRINT THIS POST! |Link | 0 comments
Subscription Options

You are not logged in, so your subscription status for this entry is unknown. You can login or register here.

No comments found.

Name:   Required
Email:   Required your email address will not be publicly displayed.

Want to receive notifications when new comments are added? Login/Register for an account.

Time to take the Turing Test!!!

2 plus 9 equals
Type in the answer to the question you see above:

Your comment:

Sorry, no HTML allowed!