Categories
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

<< June, 2013 >>
SMTWTFS
1
2345678
9101112131415
16171819202122
23242526272829
30
Search Blog

Recent Comments
Re: Disappearing IE Popup Window During Save/Open Dialog (by LZ at 4/20 7:58 AM)
Re: Create Dynamic WHERE Clauses in PHP (by pooja at 3/20 7:29 AM)
Re: Just What IS a 'Service Layer', Anyway? (by EugenK at 3/07 7:56 PM)
Re: Using Google as your CF Mail Server (by 5starwebteam.com at 2/25 1:27 AM)
Re: Why Provide for Service layer objects in CFWheels? (by Steven Benjamin at 1/25 11:43 AM)
Re: What is an 'Advanced' Coldfusion Developer? (by ColdFusion Developer at 12/24 5:14 AM)
Re: Equivalent of SQL "TOP X" in Oracle (by Ashenafi Desalegn at 12/06 5:29 AM)
Re: PHP Export to Excel Snippet (by serene at 12/05 1:44 AM)
Re: Just What Is 'Application Logic', Anyway? (by Arif at 11/13 8:06 AM)
Re: Hosts File Changes Not Acknowledged on Vista 64 (by Aaron at 10/22 2:31 PM)
Re: PHP Export to Excel Snippet (by Jafar Shah at 10/10 4:28 AM)
Re: Viewing Option Text (in IE7) that's Wider than the Select List (by Chenelle S at 10/04 12:53 PM)
Re: PHP Export to Excel Snippet (by Kilo at 9/26 5:20 PM)
Re: Porting Coldfusion Code to Mura (by tariq at 9/03 9:51 AM)
Re: Just What IS a 'Service Layer', Anyway? (by James at 8/27 4:06 PM)
Re: Calculating Business Hours (by helen at 8/14 2:54 AM)
Re: What IS 'Business Logic', Anyway? (by dougboude at 8/06 11:30 AM)
Re: What IS 'Business Logic', Anyway? (by Adrianne at 8/06 10:29 AM)
Re: Family Law: The Weapon of Choice for Woman Scorned (by dougboude at 8/04 4:39 PM)
Re: Family Law: The Weapon of Choice for Woman Scorned (by Lola LB at 8/04 7:43 AM)
Archives
Photo Albums
Funnies (5)
Family (3)
RSS

Powered by
BlogCFM v1.11

31 July 2010
THE DAY CFUNITED DIED
The Summation of My Experience

As you ALL know, this was the last CFUnited ever, and as an attendee, I am certain that this fact was actually a positive influence on how the conference played out. The camaraderie that is always an integral part of this event was magnified by the ever-present fact that we just don’t know when we may be able to assemble ourselves in these numbers again. I know for myself (and I do believe that I am representative of most of my CF brethren), I felt it to be even more important this year to seek out and get to know new faces, and to push myself outside of my hermit-like inclinations and spend good quality social face time.  During said face time, we all engaged in the discussions of the subjects that this conference’s demise has brought to the forefronts of our minds. Why was it ending? What did it mean for our community? What would be the ramifications? Was it a sign of anything to come? as confident as we are about the future of the language we embrace and earn our livings with, the demise of the grandest exclusively CF event to grace our professional landscape in the past six years has definitely been a cause of at least minimal concern to many. But, through the process of the fellowship, the sessions, the after parties, and the after parties’ after parties, I gained an absolute reassurance that what we have witnessed is nothing more than the results of an economy that’s been on a downward trend for the past three years and is in no way a prognostication of the future of ColdFusion. In fact, what it DOES mean is that now that one of the larger trees in the forest has sadly fallen, the vacancy it leaves us with is the opportunity and catalyst to allow for the currently smaller, more focused, and/or regional conferences to flourish and fill this niche in our professional ecosystem. I firmly believe this, and have found nothing but reassurance for this belief in the past three days of being immersed among my peers and mentors.

ON THE NEGATIVE (BUT POSITIVE) SIDE...

Having highlighted how excellent the conference was, I feel the need to share a less positive opinion based on my observations this year as an attendee. This particular CFUnited seemed to greatly lack in its administration as compared to those in the past that i have attended. The staff seemed more interested in doing their own thing as opposed to being focused on the details relevant to a conference, such as session start/stop times, ensuring sound levels, recording speakers, ensuring that the doors were closed once a speaker began, giving the speaker cues as to when his or her time was nearly up, and just maintaining a positive, prominent presence throughout the event. Fortunately, and I give HUGE kudos to these individuals, there were those individuals who were in attendance that took great responsibility upon themselves to fill in these gaps. They ensured that the registration desks were properly manned and that the attendees were given the proper attention at registration, they went the extra mile and turned what would probably have been blas’e social events into OUTSTANDING social events, they invested their own time to make certain that the fellowship, the tradition, and the kindred nature that we all share as a CF community was recognized and honored. I would give specific shout outs to those who I am aware were instrumental in making this conference as memorable and awesome as it was, but truth be told, everybody in attendance had some hand in that. Together, we made this CFUnited what it should be, and we can be proud of that fact. On the other hand, the actual organizers of the event...honestly, they couldn’t have seemed more disinterested to me. Oh, and the fact that I was solicited via email to help "save" CFUnited by BUYING THE RIGHTS TO IT??? Man, that's like telling your kid you love him and then putting a price tag on his forehead. That's just wrong in my book. Like I said, just my observation and opinions here.

 LOSING MY VIRGINITIES

This conference also happened to be one that resulted in a few major changes for me. Besides the hardly containable inspiration that the sessions were regarding things like alternatives to RDBMS, iPhone development, the exploration of HTML5, and a new found interest in the CF on Wheels and FW/1 frameworks, I also found myself doing things that I was certain I never would do. I, a profoundly staunch PC, discovered that I am now “i-curious”. I want a mac. It stuns me and frankly, freaks me out, every time i hear myself say such a thing, but it’s true: i want a mac. Any of my friends who know me very well are probably picking their jaws up off of the floor at reading that, as I have never even come CLOSE to waivering in my unrelenting despisings (and uninhibited vocalization OF those despisings!) of all things mac. Heck, when they first saw me palming an iPhone they nearly fainted. But after seeing the presentation on iphone development with the Coldbox framework as the backend data services, something just gave up inside of my head and I, for the first time in my life, understood what it felt like to desire a mac. I know, this almost sounds like I’m coming out of the closet, and in many ways it certainly does feel that way to me, too! A strange feeling, like I’m giving in to the thing I shunned for so long...I’m being dramatic, huh?

Oh, the second thing I did that I said I would purposefully NEVER do: I had my picture taken with Ben Nadel. Again, as any of my friends who know me well will attest to, I am in general an “anti-herd” guy; if everybody’s doing it, then to me that’s a sure sign that they’re doing something i do NOT want to do. And, since everybody seemed to be giddy at the idea of having their picture with Ben up on his site, I naturally vowed internally never to do so. But then, I actually got a chance to meet Ben and talk with him a little bit. Gosh darn it, he’s just too nice! :) So, I conceded and participated in a three way with him and Ryan Jeffords. Three way photo opp, that is. :)

WHAT NOW?

So NOW then... let’s all set our sights on the next conference or conferences we’re going to attend, shall we? Our time honored tradition of supporting our community by communally sharing what we all learn individually must continue, and it will do so in the form of ALL of us making it a point to contribute and participate in whatever ways we can. BLOG, y’all. BLOG the things you learn that have made your development life easier. TWEET. Share the tiny 140 character moments of your days that reveal your knowledge, wit, charm, losses, gains, wisdom, and personality to the rest of us. SPEAK. Overcome your erroneous belief that you have nothing of value to offer your peers and mentors and volunteer to speak at your local user group, the online cfmeetup group, other user groups in other cities, and conferences. When you get the word that such and such conference is having a call for speakers, you should be prepared to submit yourself and two or three topics that you have already presented on via the user groups. I know it can be difficult to overcome shyness and such, but believe me, all of us have something of value to offer. Heck, even the very fact that someone who never spoke publicly before is making that effort is in itSELF an inspiration to others, regardless of the topic or how smooth the preso goes!

Oh, let me leave you all with this awesome resource that Charlie Arehart maintains for the sake of his community: http://www.carehart.org/cf411/  There, you can find out about all the latest conferences and other resources whereby a CF developer can get “plugged in” to his or her community and begin partaking and sharing.

CONCLUSION

This conference has left me with a LOT of new friends, personalities and faces to connect with twitter icons, a boat load of technical inspiration, and the loss of at least two virginities. CFUnited 2010 rocked, y’all! If you missed it, you really did miss a lot.

I look forward to crossing paths with you in the future! In the meantime, if you're not already, let's be virtual buddies, eh? My twitter name is dougboude

Posted by dougboude at 1:27 AM | PRINT THIS POST! | Link | 1 comment



31 March 2010
Easy Way to Integrate ColdFusion into Non-ColdFusion Templates
Ajax to the Rescue!

Have you ever found yourself in a situation where you needed to implement some new feature using one scripting language within a template that is written in a different scripting language, or possibly no scripting language at all (pure HTML)?  Of course, you could just turn the pure HTML template into a CFML template, and at times that is indeed the best route to go, as long as breaking every link pointing back to this page is acceptable. If the target template is written in another scripting language, however, transforming it into a CFML template would probably not be a viable option. Ah, but there is at least one way to still be able to integrate your Coldfusion functionality into any template (HTML OR those written in other scripting languages): Leveraging the power of Ajax.

Typically the first route our minds go when asked to integrate two discrete templates is to look for a way to "include" one within the other. HTML does provide a type of inclusion ability (called 'Server Side Includes' (SSI)), but this approach is typically used to include other HTML documents and would be challenging to make work with a Coldfusion template. Other scripting languages have the ability to include other templates written in the same language, but getting them to play nicely including Coldfusion would also prove challenging. You could also go the route of using an IFRAME within your template, but this approach also presents limitations and hurdles that you may not wish to deal with. The simple solution, and the simplest approach to making it work, again, is Ajax.

The Scenario

In this scenario, the target template is a static HTML site and the code you want to integrate is written in Coldfusion. Our goal is to dynamically display the contents of an upcoming events database table in our Events section of the company's HTML home page. Here's how I approached it:

1. Create a target within the static HTML page where you want the event data to appear. In my case, I created this bit of HTML:

  <h3>Corporate Events</h3>
  <div id="eventTarg"></div>

 

2. Create a CFM template to act as your data provider. Mine is called eventAjax.cfm, and contains several different methods since it is used by other applications as well;

3. "Wire up" your HTML page with your favorite Javascript library. Mine (currently) is Prototype, so my code snippets will reflect Prototype's syntax, but you should be able to easily relate it to your favorite flavor

That's it!  Now, some details...

 

The Data Provider

Some may feel the need to write full blown web services to provide data to the consumer/caller, but personally I feel that most times this is major overkill when a RESTful approach works beautifully. Here is what my eventAjax.cfm looks like, showing only a couple of possible methods:

<!--- suppress any and all output except what we explicitly allow via cfoutput tags... --->
<cfsetting enablecfoutputonly="true" showdebugoutput="false" />


<!--- our switch expression value. this determines what action will take place --->
<cfparam name="meth" default="" />

<cfswitch expression="#meth#">
 <cfcase value="getEventLinks">
  <!--- retrieving all events that have not yet ended and are published --->
  <cfquery name="qryGetLiveEvents" datasource="#dsn#">
   select id,name,descrip,eventfromdate,eventtodate
   from registrationevents
   where <cfqueryparam value="#now()#" cfsqltype="cf_sql_date" /> <= eventToDate
   AND publish = <cfqueryparam value="1" cfsqltype="cf_sql_tinyint" />
   order by eventFromDate
  </cfquery>

  <!--- by default, create this return value... --->
  <cfset links = "no events currently scheduled" />

  <cfif qryGetLiveEvents.recordcount gt 0><!--- if we have records, loop over them and create the 'links' content --->
   <cfoutput>
   <cfsavecontent variable="links">
    <p id="eventlinks" class="eventlink">
     <cfloop query="qryGetLiveEvents">
      <a href="registration.cfm?eventid=#id#" target="_blank">#name#</a><span class="eventText"> <em>(from #dateformat(eventfromdate,"mm/dd/yyyy")# to #dateformat(eventtodate,"mm/dd/yyyy")#)</em><br>#descrip#</span><br><br>
     </cfloop>
    </p>
   </cfsavecontent>
   </cfoutput>
  </cfif>
  <cfoutput>#links#</cfoutput>
 </cfcase>

 <cfcase value="updatePublish">
  <cfparam name="eventid" default=""/>
  <cfif not isnumeric(eventid)>
   <cfoutput>INVALID EVENT ID</cfoutput>
   <cfabort>
  </cfif>
  <cfquery name="qryUpdatePublish" datasource="#dsn#">
   UPDATE registrationevents
   SET publish = <cfqueryparam value="#newval#" cfsqltype="cf_sql_tinyint" />
   WHERE id=<cfqueryparam value="#eventid#" cfsqltype="cf_sql_integer" />
  </cfquery>
 </cfcase>

 <cfdefaultcase>
  <cfoutput>UNAUTHORIZED ACCESS</cfoutput>
 </cfdefaultcase>
</cfswitch>
<!--- helper functions can reside below this line... --->

Reading through this code, it is pretty self-explanatory. Basically, when the ajax call is made, only values explicitly output using CFOUTPUT tags will be returned to the caller. My "getEventLinks" method is returning a pre-formed block of html that my client side javascript will simply dump into its target div.

Wiring up the HTML

Wiring up our HTML page consists of doing only two things:

1. include your javascript library;
2. create a function to be executed after the html has fully loaded;


My step 1 line looks like this:

<html>
...
<title>Company Name Here</title>
<script type="text/javascript" src="js/prototype.js"></script>
....
</html>

 

Step 2 is inline script written at the bottom of the template, just within the <body> tag. I don't believe it HAS to reside within the <body> tag, but that's where i chose to put it. It looks like this:

<script>
 document.observe("dom:loaded", function() {
  params = new Hash();
  params.set("meth","getEventLinks");
  new Ajax.Updater('eventTarg','eventAjax.cfm',{parameters:params,method:'post'});
 });
</script>

 

That's it! Now, everytime the page is loaded, Prototype will execute an ajax call to the template specified and will stuff the results into the targeted div 'eventTarg'. If no qualifying events are found, we'll simply get the phrase "no events currently scheduled".

Here is a screenshot of the html page loaded, using Firebug to see the ajax call that was made and the value returned:

firebug ajax call

Posted by dougboude at 3:52 PM | PRINT THIS POST! | Link | 5 comments
16 March 2010
Leveraging Response Headers in Ajax Calls

If you're doing any sort of Ajax development, then you undoubtedly already are quite familiar with the different ways you can use an Ajax call. For instance, you can have the call return a fragment of fully formed html (such as a table populated with data). For a more advanced user, you may have your Ajax calls returning raw data in the form of JSON or XML and parse it on the client side. In either case, though, your single Ajax call is returning a single value. But what if you want several different discrete pieces of "stuff" returned in a single call? You could get creative, and do something such as this in your backend code:

<cfsetting enablecfoutputonly="true" />
<cfscript>
  stReturnData = structnew();
  stReturnData.htmlBlob = "<table><tr><td>bla bla bla</td></tr></table>";
  stReturnData.statistics = structnew();
  stReturnData.statistics.recordcount = 212;
  stReturnData.statistics.redWidgets = 32;
  stReturnData.statistics.blueWidgets = 54;
  stReturnData.someQuery = qryIRanPriorToThis;
 
  returnVal = serializeJSON(stReturnData);
</cfscript>

<cfoutput>#returnVal#</cfoutput>

The above would work perfectly fine. It would, however, require that you be willing to write some serious Javascript to access the individual pieces of data, and compels you to have to visualize how the data sets are structured and nested as a whole.

Again, nothing wrong with doing something like the above. But, when I find myself wanting to return more than one value with a single Ajax call, more often than not I will approach it by leveraging response headers.

Response headers are like the saddle bags on a Harley. If your Ajax response is the Harley, then the burley tatted dude (or dudette) riding it is your primary response value. This Harley, however, comes equipped with a pre-defined set of saddle bags that contain what I'm calling "peripheral" information, or metadata about the response, AND (this is most critical), the ability for you to add your own custom saddle bags before the response comes riding home to its caller!

I have never had a need to even look at or care about those pre-canned response headers. They contain name value pairs such as "Transfer-encoding:chunked", "content-type:text-html", "Date: Tue, 16 Mar 2010 15:43:35 GMT", and a couple of others. But, in order to satisfy needs such as I described above in the code sample, I DO add my own response headers and stuff them with values my Javascript needs.

Doing this in Coldfusion is very simple and easy (as are most tasks). Re-doing the example above, then, I'm going to choose one of the values to be my burley tatted rider (typically the value consuming the most bytes), and designate the rest to response headers, like so:

<cfsetting enablecfoutputonly="true" />

<cfset ReturnData = "<table><tr><td>bla bla bla</td></tr></table>" />

<cfheader name="recordcount" value="212" />

<cfheader name="redWidgets" value="32" />

<cfheader name="blueWidgets" value="54" />

<cfheader name="someQuery" value="#serializeJSON(qryIRanPriorToThis,true)#" />

<cfoutput>#ReturnData#</cfoutput>

So now my Harley has several new, custom saddle bags attached, each containing the value I set in the "value" attribute of the cfheader tag, and my main rider, or the actual response value is my ReturnData (the blob of html).

Let's look at the Javascript needed to utilize this response. In this example we are going to use the Prototype library to create a new Ajax Updater. Updaters simplify the task of making an Ajax call and stuffing the response value directly into a target element. So let's say we have a div with an ID 'dataDiv' that will hold a table of data. The Ajax updater line will look like this:

new Ajax.Updater('dataDiv',getDataURL,{parameters:params,method:'get');

 

 

Now, the line above deals exclusively with the response value and ignores anything to do with the response headers. In order to access those headers, we have to tell our Updater to pass its response (Harley, rider, saddlebags, and all) to a function that we designate. This is called "using a callback function", and our line of code will now look like this:

new Ajax.Updater('dataDiv',getDataURL,{parameters:params,method:'get',onComplete:processSaddlebags);

 

 

The Updater will update its target div with the response value, then hand the Harley off to the processSaddlebags function, which will look something like this:

function processSaddlebags(ajaxResponse){
  //let's set each of our response header values to a local variable...
  var recordcount = ajaxResponse.getHeader('recordcount');
  var redWidgets = ajaxResponse.getHeader('redWidgets');
  var blueWidgets = ajaxResponse.getHeader('blueWidgets');
  var objQuery = ajaxResponse.getHeader('someQuery');

  //now we can do 'stuff' with these values!
  alert('total number of records in our query: ' + objQuery['ROWCOUNT']);
  //etc....
}

It should be noted at this point that a most MAHVELOUS way for you to get a clearer picture of what is happening when your code makes Ajax calls is to use the Firefox browser with the Firebug plugin. By doing this simple thing, Firebug will provide you with all the details of every Ajax call you make. Here's a screenshot of a sample Ajax call and the kinds of info you get back (notice the custom header 'total' that I added, and the fact that I'm outputting that value in the html as the "Total Records" value):

response headers

screenshot of firebug ajax call showing response headers

response value

screenshot of firebug ajax call showing the response value

Now, all that having been said, there are size limitations to the usage of response headers that you should keep in mind. I could not find a specific reference that talked about the size limit of response headers, but I did do some experimentation to try and approximate what that limit is. I stuffed as many characters as I could into a response header, and the value was truncated at 6,653 characters (including spaces). I created two custom headers with the same 6,653 characters in it, and both were returned, so I'm assuming the limitation is per header (though common sense tells me there is probably a cumulative maximum size as well). Knowing that there IS a limit, my recommendation is that you use headers for smaller pieces of data and never to convey something as large as a JSON data set unless you know that it will be limited in size.

That's it boys and girls! Now go out there and leverage those response headers!

Posted by dougboude at 12:47 PM | PRINT THIS POST! | Link | 5 comments
12 March 2010
Spreadsheet to XML Transformation Utility Overview

I am currently working on a project that has proven to be very challenging, architecturally speaking. I've been learning a lot along the way, and I have come up with some approaches to addressing the challenge that I feel are pretty doggone sweet, so I thought I'd share some of the details in case it helps spark ideas for other people.

Let me describe how this challenge came about, and the challenge itself.

I'm sitting in my office working on something when I hear several people in an informal meeting discussing how much in time and money it will cost them to pay several temps to enter data from a spreadsheet into a remote system. I wrestle with the idea of making work for myself for a few seconds, then inject myself into the conversation and ask them a few questions about the remote system and what ways it provides for inputting data. As it turns out, the remote system has an interface for receiving this same data in the form of an XML file. Simply drop the XML file into a special folder in their system and it is automatically imported. "So, can you turn our spreadsheets into xml files"?, they ask me. "Of course"!, I reply. And so it begins.

As I try to always do, I explored what potential future requests might entail surrounding this process, examined the possibility of this process' usefulness to other companies, and bore in mind the potential of using this process to accommodate OTHER spreadsheet sources and OTHER xml destination formats. The result was a full blown utility application that allows a user to define a spreadsheet layout, map that layout to a targeted xml layout, then be able to import spreadsheets and produce the xml files in the target format, fully populated and validated; upload those files to their FTP destination; and monitor the status of the uploaded files via feedback mechanisms built in to the target system.

Bear in mind this application is not finished, but I have a substantial amount of the architecture completed and code working up to the point of the FTP process, so its the ins and outs of some of those moving parts that I wanted to share with you.

System Overview: How it Works

1. User logs in and chooses 'Upload Claims'
2. selected spreadsheet is uploaded and validated
3. validated spreadsheet is imported via POI
4. xml node to spreadsheet column mappings are retrieved

for each row in the spreadsheet
  5. blank xml file is created by applying an XML stylesheet (xslt) to the xsd on file;
  6. node values are translated from their tokens to actual values
  7. nodes are located within the xml file via their id and their value replaced with translated value
  8. xml files are "cleaned up" (optional), removing all empty nodes and node trees by applying an XML stylesheet

9. completed xml files are FTP'd to their destination (based on client settings)


DETAILS

Maintaining XML Definitions in the System

So, the first aspect I tackled was the best way to maintain target XML definitions. Well, since I know I want to be able to validate the XML files I create, and since the existing methods of xml validation of necessity ARE a definition of the XML, it made perfect sense to me to start with either a DTD or an XML Schema that represents the targeted XML format. DTDs don't have near the depth of metadata that I would need for future use, so I opted to use XML Schemas (or, 'xsd' files). Every potential target XML format this system maintains will be represented by an existing XSD file stored in a subfolder of the app.

If you aren't familiar with XML Schemas, let me tell you they are invaluable if you want to validate your xml in any detail. You define elements, the data types they hold, any rules that should be applied to those values (such as "only one of the items in this list of values", "a date in the following format", etc.), what attributes an element has and the rules for the values THEY hold, what elements are required, how many instances of a particular element are legal, what order elements should appear in, whether an element is required or not, and pretty much ANYTHING else you can think of! If what you need to store about the XML is NOT available as a standard XSD item, then do what I did and create your OWN namespace within the XSD and add whatever other attributes you want!

For the system I am building, one vital step is to ensure that every node, or potential spot for storing a system value, had to have a unique id. Since there was nothing in the existing xs namespace that allowed for that, I created my own namespace named after the app and added an "ID" attribute to every element and attribute defined for the target XML format. Here's the header of my XSD (notice the 'xclaim' namespace defined): 

<xs:schema
   xmlns:xs="http://www.w3.org/2001/XMLSchema"
   elementFormDefault="qualified"
   attributeFormDefault="unqualified"
   xmlns:xclaim="http://www.somesite.com" >

 Having defined the xclaim namespace allows me to legally do the following: 

<xs:element name="XACTNET_INFO"  xclaim:id="id_1">

So, in my system, to add a new XML target, simply create an XSD schema file that 'describes' that format, complete with the xclaim namespace's 'id' attribute, and you have accomplished your goal! 

Maintaining Spreadsheet Definitions

I wanted to be able to "head off at the pass" as much confusion or needed validation code as possible, so I opted to 'validate' an uploaded spreadsheet before attempting to process it. What this means to me is that, when a spreadsheet is uploaded I will first check to see that it contains all of the columns (appropriately named) as the user said it should and that all of those columns appear in the correct order. If the spreadsheet passes these two checks, I call it good and we proceed. Otherwise, I reject it and give them a nice message so saying.

In my UI I provide them a way to create and maintain spreadsheet definitions. Essentially they tell the app what the name of the column is and what order it appears in.

phpmyadmin column definitions

Simple enough, eh? 

Maintaining Spreadsheet Column/XML Mappings

Ah, now THIS little bugger was much tougher than one may think at first glance. You see, properly populating your target XML poses many challenges, such as:

-not every column maps directly to only one xml node (element or attribute). This is a one to many relationship, for sure!;

-not every value needed in your xml file COMES from the incoming spreadsheet! Values such as today's date, the client's name, address, or other metadata, etc.;

-not every value needed in your xml file is a SINGLE value, but could very well be a CONCATENATED value! Such as in my case, where the values from three columns needed to be concated along with today's date and placed into a specific element's text, or where one attribute's value had to be the concatenation of a fixed string "COV" and an iterating value (COV1, COV2, etc.)

So you see, designing a storage mechanism to allow such fluidity and yet still be easily maintainable was no easy task to conquer! The solution I came up with accommodates all of the above, but still does have its limitations. Not a perfect solution, but close enough I'd say!

Here then is what the stored column-to-node mappings look like:

column to node mapping table

Some highlights of this approach:

-nodeID values correspond to the "xclaim:id" values spoken of earlier with regard to the XSD file;

-nodeID values with decimal counters indicate that there may be multiple occurrences of that node's branch in the final xml file. The decimal indicates which iteration of that branch the value belongs in;

-I created "tokens" as placeholders for those values that must be dynamically determined at run time. {column-x} refers to the value in column X, {column-x-name} is replaced with the actual column name, {iterator} is an incrementing value, {currentDateTime} is the current date and time, etc.;

-by including a targeted xsd for a node-value mapping (note the xsdID field), I can effectively map a single spreadsheet to multiple destination xml formats;

-the sortorder field is used in cases where a nodeid has multiple values indicated. This means that those multiple values should be concatenated in the order of 'sortorder'.

By using mysql's really awesome group_concat function, I can return a nice list of values, in their proper order. Here's the query I use to retrieve my mappings. Note the use of group_concat: 

   SELECT m.sheetID, m.xsdID, m.nodeID, GROUP_CONCAT( m.nodeValue ORDER BY m.sortorder SEPARATOR '|' ) AS nodeValue
   FROM mapping m
   WHERE m.sheetid = <cfqueryparam value="#arguments.sheetID#" cfsqltype="cf_sql_integer" />
   AND m.xsdID = <cfqueryparam value="#arguments.xsdID#" cfsqltype="cf_sql_integer" />
   GROUP BY m.sheetID, m.xsdID, m.nodeID
   ORDER BY m.nodeID

 The results looks like this:

node value query results

With the query above retrieved, I can now walk through each row of my imported spreadsheet and calculate the appropriate value for the targeted node IDs. :) 

Creating the Blank, or Skeleton XML File

In this system, the only thing we maintain regarding a tarteted xml format is its XSD schema file. Since the XSD schema file is itself an XML file, the logical most expeditious approach to creating our skeleton xml is to leverage XML Stylesheets, or XSLT. I searched high and low for an XSLT that would transform a valid schema file into the skeleton XML it represents and could not find a single example. I thought surely such a thing would have been done many times over! So, since I couldn't find one, I wrote one. It's not all that lengthy really, so here it is in all its glory:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:str="http://exslt.org/strings"
 xmlns:xalan="http://xml.apache.org/xalan"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:xclaim="http://www.somesite.com"
 exclude-result-prefixes="#default xsl str xalan xs xclaim">
 <xsl:output method="xml" indent="no" encoding="ISO-8859-1" omit-xml-declaration="yes" />
 <xsl:template match="/">
  <xsl:element name="XACTDOC">
   <xsl:apply-templates />
  </xsl:element>
 </xsl:template>
 <!-- getting our elements, but excluding complexTypes with a name attribute (entity definitions, as i refer to them) -->
  <xsl:template match="xs:complexType[not(@name)]/xs:sequence/xs:element|xs:complexType[not(@name)]/xs:all/xs:element|xs:complexType[not(@name)]/xs:choice/xs:element">
   <xsl:call-template name="outputElement">
   <xsl:with-param name="isEntity" select="false()" />
   <xsl:with-param name="parentID" select="@xclaim:id" />
  </xsl:call-template> 
 </xsl:template>
 <xsl:template name="outputElement">
  <xsl:param name="isEntity" />
  <xsl:param name="parentID" />
  <xsl:choose>
   <!-- if the element we are looking at has a type specified, and that type is not a predefined xs: type (must be an entity!)... -->
   <xsl:when test="@type and substring(@type,1,3)!='xs:'">
    <xsl:element name="{@name}">
      <xsl:call-template name="outputEntity">
      <xsl:with-param name="thisname" select="@type" />
      <xsl:with-param name="parentID" select="@xclaim:id" />
     </xsl:call-template>
    </xsl:element>
   </xsl:when>
   <xsl:otherwise><!-- dealing with an element that is NOT pre-defined -->
    <!-- does this element have a type attribute that is enumerable? -->
    <xsl:variable name="hasEnum">
    <xsl:choose>
     <xsl:when test="count(xs:complexType/xs:attribute[@name='type']/*/xs:restriction/xs:enumeration) > 0">
      <xsl:value-of select="true()"/>
     </xsl:when>
     <xsl:otherwise>
      <xsl:value-of select="false()"/>
     </xsl:otherwise>
    </xsl:choose>  
    </xsl:variable>

    <xsl:variable name="thisnode">
     <xsl:element name="{@name}"><!-- output the new element tag -->
      <!-- if this element will contain text... -->
      <xsl:if test="@type or count(xs:simpleType/xs:restriction|xs:complexType/xs:restriction)>0">
       <xsl:value-of select="@xclaim:id" />
      </xsl:if>
      
      <!-- output all the attributes this element has. if it has a type attribute and the values are enumerable, put a marker in -->
      <xsl:for-each select="xs:complexType/xs:attribute">
       <xsl:attribute name="{@name}">
        <xsl:choose>
         <xsl:when test="$isEntity">
          <!-- if this attribute is from a pre-defined entity, create a value placeholder for it that is unique yet predictable -->
          <xsl:value-of select="concat($parentID,'-',substring-after(@xclaim:id,'_'))" />
         </xsl:when>
         <xsl:otherwise>
          <xsl:value-of select="@xclaim:id" />
         </xsl:otherwise>
        </xsl:choose>
       </xsl:attribute>
      </xsl:for-each>
      <!-- this makes it all recursive! -->
      <xsl:apply-templates />
     </xsl:element> 
    </xsl:variable>
    <xsl:copy-of select="$thisnode" /> 
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template> 
 <xsl:template name="outputEntity">
  <xsl:param name="thisname" />
  <xsl:param name="parentID" />
  <xsl:for-each select="/xs:schema/xs:complexType">
   <xsl:if test="@name=$thisname">
    <xsl:for-each select="xs:sequence/xs:element|xs:choice/xs:element|xs:all/xs:element">
     <xsl:call-template name="outputElement">
      <xsl:with-param name="isEntity" select="true()" />
      <xsl:with-param name="parentID" select="$parentID" />
     </xsl:call-template>
    </xsl:for-each>
   </xsl:if>
  </xsl:for-each>
 </xsl:template>
 <xsl:template match="xs:annotation/xs:documentation">
 </xsl:template>
</xsl:stylesheet>

This XSLT is not quite generic enough to work with any XSD, but I hope to make it so before I'm finished (if anybody else wants to make that happen, please do!).  The XSD I'm applying this XSLT to is fairly complex, and includes some "named complexTypes" as well. For those who aren't aware (I know I wasn't), a named complexType is almost the equivalent of defining a variable, and then re-using that value throughout the rest of your document simply by referring to the variable's name. For instance, if I have a chunk of my schema that defines an address block, and there are several places within my xsd where this exact same xml is repeated, rather than actually write it each time i can write it one time, give it a name such as "addressblock", and then wherever I want to include that I simply refer to it by name.

Creating my skeleton XML then to fulfill step 5 in the system overview, I simply use the following line of code:
 

 <cfset skeletonXML = xmltransform(rtrim(ltrim(ToSTring(targXSD))),ltrim(rtrim(xslt))) />

 

CONCLUSION - FOR NOW

There are a LOT more details to all of this that I haven't touched on, like the xpath statements I use to find and populate the values of nodes, how I reproduce node trees that have multiple occurrences, etc. I'll save more of the nitty gritty details of that sort of thing for another post. In the meantime, if you have any questions or input (especially on how to make my xslt more efficient!), please do leave a comment for me! Most of this was fairly new territory for me, so there's likely lots of room for improvement and efficiency, and I'm all ears.

Posted by dougboude at 2:22 PM | PRINT THIS POST! | Link | 0 comments
19 February 2010
Registered Ajax Responders Not Responding Properly
Ajax Fundamentals
If you use the Prototype Javascript library and you chain your Ajax calls together at times, you may have run into the issue that I just dealt with regarding your registered responders not responding correctly! Since I spent more time than I care to share trying to figure out why my global responders weren't working like I thought they should, I thought I'd share what I learned. Hope it saves somebody some time! :)

Prelude

You may not be familiar with some of the terms I have used already, or some that I will use. If you feel you already have a good grasp on the lexicon surrounding Ajax and JS libraries, skip ahead to The Scenario; but if not, then allow me to clear up some of the fog before we press on. Please excuse me if I spend time on things that you may feel are "common knowledge", but in my experience there really is no such thing and I want to make sure every reader is thinking in the same context.

The Basic Nutshell of Ajax Chaining

In Prototype and other similar libraries, an Ajax call is typically of one of two flavors: simple request, and an update request.

Simple Requests

A simple request is one in which you ask your JS to make an http call to the server and hand you back the results that were delivered, whatever they be. At that point you will have written more Javascript to actually deal with those results manually. For example, let's say you display a select list containing possible food categories to your user. When the user selects a category, an Ajax request is made passing in the selected category, and a JSON representation of a record set containing foods in that category is returned. That JSON object is handed off to another function you wrote which will 'walk' through the foods and populate yet another select list with the specific items.

Update Requests

An update request is one in which you are asking your JS to make an http call to the server and stuff the results, whatever they be, directly into a DOM element (a DIV tag, a SPAN tag, etc.). Let's make a slight change to the example cited above. In this scenario, we already have a DIV tag in our html that we have designated as the place where our select list should appear. So, instead of having our server code return a raw JSON representation of the data, we write our server code to produce the HTML for the fully populated select list ("<select name='selFoods'><option value='1'>Crescent Rolls</option><option value='2'>Dog Biscuits</option>....</select>"). It is this string that our Ajax call receives, and this returned value is stuffed directly in to the DIV tag we designated as the recipient. Make sense?

Registering Responders

It is quite typical (and useful) to show to your users some kind of "working" image to indicate that the Ajax call is in progress. We may also wish to disable certain items on the user interface while calls are in progress, or overlay an opaque div to prevent the user from clicking anything. The thing we want to do and/or display while an Ajax call is in progress is what we refer to as our "Responder". Within our Javascript, we will tell our library something along the lines of "hey, anytime an Ajax call is created (started), I want you to do this; and anytime you see an Ajax call finish, I want you to do this". The act of telling our library what we want to happen automatically regardless of the Ajax call is called "registering responders". You define (register) them one time, and they just work. :)

Why Would I Ever Want to Chain My Calls?

I'm glad I asked! Let me just give you a working example to help illustrate why we might do this on occasion, and how we do it.
In my application, I am allowing a user to upload a data file. When the upload of the file is complete, I then need to kick off a series of things to occur, and I want to update a DIV with the current status of each call as they happen. The first call is to validate the uploaded file, so I write an Ajax simple request that tells the server where the file was uploaded and what kind of file it is supposed to be. This call will either return a true or false, depending on whether the file passed or not. If it failed, I want to tell the user that the file was bogus and cease any further processing. If it passed, I want to kick off another Ajax simple request that tells my app to go ahead and proceed with step 1 of the file processing. That call will return success or failure along with status messages, and then will kick off the third step in the processing.

Now, the way we "connect", or "chain" these calls together is by taking advantage of the fact that our library knows when a call is finished. When we write the JS for call 1, we tell it "when you receive a response from the server, pass that response on to Call 2 for evaluation". Call 2 is written to check the success status, and if all is well will make an Ajax simple request which itself has been told to pass its results on to Call 3. Here's an abbreviated example of what I'm talking about (some code ommitted for brevity):

function Call1(){
   new Ajax.Request(validateUploadURL,{parameters:params,method:'post',onSuccess:Call2});
}

function Call2(response){
   var objRetval = response.responseJSON;
   if(!objRetval.success){
        $('status').update(objRetval.msg);
        return;
   } else {//call successful! carry on
     new Ajax.Request(fileProcess1URL,{parameters:params,method:'post',onSuccess:Call3});
   }
  
}

function Call3(response){
   var objRetval = response.responseJSON;
   if(!objRetval.success){
        $('status').update(objRetval.msg);
        return;
   } else {//call successful! carry on
     new Ajax.Request(fileProcess2URL,{parameters:params,method:'post',onSuccess:TheEnd});
   }
  
}

function TheEnd(response){
  var objRetval = response.responseJSON;
  $('status').update(objRetval.msg);
}

See how they're all connected? Chain Chain Chaaaaaaainnnnnn... yeah.

The Scenario

In my scenario, I have a file upload process that is composed of three different Ajax requests, all chained together in a conditional, synchronous fashion (I know there are other ways to approach it, but this is the one that fit my needs the best). I have registered global responders to show/hide an animated spinner to indicate when a request has started and finished (I 'show' it when the request starts, I 'hide' it when its finished). Now, in my mind, these global responders should toggle that gif every time a request starts or stops, so in this scenario, it should show/hide three different times. But, that isn't how it worked. The gif would show during the initial call, and hide after that call was finished; Then it never showed again, despite the fact that there were multiple additional calls made! Here is how I registered my responders:

    Ajax.Responders.register({
          onCreate: function() {
            showUploadWorking();
          },
          onComplete: function() {
            hideUploadWorking();
          }
        });


After much experimentation, it occurred to me that perhaps even though the responder was global, maybe I needed to be concerned with manually 'watching' the active request count variable. I don't understand why I SHOULD have to, but that did turn out to be the solution. Here is the properly working responder registrations:

    Ajax.Responders.register({
          onCreate: function() {
            showUploadWorking();
          },
          onComplete: function() {
              if(Ajax.activeRequestCount == 0){
                  hideUploadWorking();
              }
          }
        });

Notice the only difference is in the onComplete function...I am 'watching' the activeRequestCount variable (which as you may have guessed is simply a counter telling us how many requests are currently not yet finished) and conditionally hiding my responder.

Doug out :0)
Posted by dougboude at 11:20 AM | PRINT THIS POST! | Link | 3 comments
17 September 2009
Batch Script to Backup MySQL Database

In setting up a new IIS7/Windows 2008 server, I had a need to automate the backup of my MySQL database and so pieced together a little batch file that I then scheduled via the Task Scheduler. The file first removes all backup files that are more than a week old, then performs a sql dump and finally zips the dump. Files are named according to the date and time they were created. Anyway, it took me quite a while to find all the pieces and parts I needed, so thought I'd post it here as a starting point for others who may be wanting to do the same.

Oh, one prerequisite for this particular script to function: You have to have a copy of winzip installed (I have version 12) WITH the optional command line utility, which is a separate download (http://www.winzip.com/downcl.htm). Hey, the whole deal costs $29 bucks...spring for it!

MySQLBackup.BAT

@echo off
for /f "tokens=1" %%i in ('date /t') do set DATE_DOW=%%i
for /f "tokens=2" %%i in ('date /t') do set DATE_DAY=%%i
for /f %%i in ('echo %date_day:/=-%') do set DATE_DAY=%%i
for /f %%i in ('time /t') do set DATE_TIME=%%i
for /f %%i in ('echo %date_time::=-%') do set DATE_TIME=%%i
rem Killing all files older than a week old...
forfiles /D -8 /M *.zip /C "cmd /c del @fname.zip"
"C:\mysql\bin\mysqldump" -u username -p"password" dbname >C:\mysqlbackup\%DATE_DAY%_%DATE_TIME%_database.sql
wzzip C:\mysqlbackup\%DATE_DAY%_%DATE_TIME%_database.zip C:\mysqlbackup\%DATE_DAY%_%DATE_TIME%_database.sql -mex

Posted by dougboude at 9:30 AM | PRINT THIS POST! | Link | 2 comments
26 February 2008
Making Your Home a Pleasant Place to Be: Project Birdbath
My lovely lady and I love to nest. That is, we are always looking for ways to make our home our favorite place to be. Lately, (since we south Texans are privileged to an early onset of GORGEOUS weather), our attention has been on the backyard. We have several projects in the works, all of them on a shoestring budget, so I thought I'd share one with you all in the hope that it will inspire you, too; a little bit of effort goes a long way to making your little piece of the world an enjoyable, relaxing place to be.

Okay, so I already had a bird feeder on the back porch hanging on a plant hanger. The other day I saw our first visitor in a long time: a male house finch (looks like a sparrow, but his head and chest are red). "Well, if I'm feeding him", I said to myself, "I ought to be watering him, too", so I set myself on getting a birdbath. Unfortunately, I also have a strong frugality about me, and could NOT bring myself to pay the price for any of those offered by home depot. Instead, my lady and I made our own! It was simple, cheap, and turned out better than those we saw for sale. Here's what you do:

Go to Walmart and head toward the garden center. You're looking for sets of cheap, plastic pots with matching designs and varying in diameter. Grab one of each size, and then grab the largest matching pan they have. Next, head over to the hardware section and grab a tube of some sort of glue (Gorilla Glue, Rhino Glue...anything that looks like it'll be strong and withstand the elements). Here's what we bought:

bird bath parts

I'll just show you the finished product...I think assembly instructions will be self-explanatory. :)

completed bird bath

There you have it, a gorgeous, ECONOMICAL, and perfectly functional bird bath. Oh, and since it's hollow, I think I may put some sort of nice looking rock in the very center on top to help hold it down. In addition, I'm going to dig a hole at the base of the bottom pot once I have it out in the yard so that the toads can find a safe haven there. You want toads in your yard as they do consume a LOT of unwanted pests at night.

Happy nesting!


Doug  :0)
Posted by dougboude at 1:11 PM | PRINT THIS POST! | Link | 3 comments
05 February 2008
Deaf and Hard of Hearing Friendliness in Branson: Let Your Opinion Count
(OT to the technical aggregators)

As some of you may have read in my previous post on the efforts being made in Branson, Missouri to make that city the country's first "Certified Deaf and Hard of Hearing Friendly" vacation destination, Signed Entertainment Enterprises is doing a marvelous job at raising awareness and encouraging the businesses there to hop on board. Already Randy (the President of S.E.E.) has certified several venues, opened the city's one and only "Everyone's Welcome" center (itself deaf and hard of hearing friendly), has held press conferences, provided complimentary tours, conducted radio and newspaper interviews, and is now in communication with Missouri's Governor Blunt regarding S.E.E.'s mission.

Every voice counts, however, and so I'm asking those who will to visit the S.E.E. site and, from this page, send each of the Theaters or Shows a pre-written email letting them know that you completely concur with S.E.E.'s mission to see them become Deaf and Hard of Hearing Friendly.

Thank you for taking a few minutes to do this, and please do pass the link along to friends and family who may be supportive of this endeavor as well.

Link to the personal petition page:  http://www.see-entertainment.com/certified.php

Doug  :0)
Posted by dougboude at 11:19 AM | PRINT THIS POST! | Link | 0 comments
04 February 2008
Using Google as your CF Mail Server
Ah, sounds like a no brainer, right? Why even do a blog post on such a thing, right? Because Google requires that an SMTP connection be encrypted...something that CFMX7 just doesn't do all by its lonesome. I need to vent a little, so here's my story followed by some useful info....

It's extremely late on a Sunday night and you're in the final final stages of a site migration for a client. Their "under construction" sign says that everything will be back online and kosher at 7 am Monday morning, so you're addressing one of the last things on your list: setting up the client's email hosting server.

It made it to the end of the list because of the fact that they're using a third party to relay their mail: Google. Now everybody knows Google's reputation for being logical and actually making sense most of the time, so how hard can it be to make sure that Coldfusion is pointing to the client's Google SMTP, especially when the client said it was a piece of cake when they set it up the first time? You figure out later that the person who told you that wasn't actually the person who set it up to begin with, as the passing time turns into a couple of hours and you still haven't sent a successful email.

(Here comes the venting part I mentioned...) You know what chaps my hide, grinds my gears, gets my goat (I wonder where that one evolved from), et al? It's the same thing that gets my goat most of the time, the incredibly cryptic nature of some things that ought to be simple and all of the other people out there on the net who managed to get it to work but insist on only sharing yet more cryptic or worse yet, elusive, information on how they did it (if they share any of the "hows" at all!). I want to share with you my journey, at 2 am on a Sunday night, to learning how to set up a secure tunnel to an smtp server for the purpose of using Google's smtp server for sending email. I want to do this, ALL IN ONE BLOG POST, just in case anybody else out there EVER finds themselves in the position I was...I wouldn't wish such a frustrating, elusive game of Clue on any other person.

CUTTIN' TO THE CHASE, if you're trying to use Google's smtp server to send mail and you're seeing errors like this one
coldfusion email error log

in your mail log file, it's telling you that Google requires you to authenticate to its smtp server via an SSL connection, and any Coldfusion version below 8 (I read a short post saying that version 8 could handle such things, but the poster FAILED to give any other clues as to how to implement it...sheesh) can't do that all by its lonesome. So, we need an intercessor, a little service running on the web server that will intercept requests to ports internally then reroute the request to an outward-facing port using a secure connection. That intercessor is called 'STunnel' (though there were a couple of other equal products I came across). In a numbered list then, here are the steps a body needs to do in order to make it work:

1. Make sure you have a Google email account.
My client is signed up to use Google Apps, which includes an email server. Here's a starter link for you: https://www.google.com/a/help/intl/en/admins/editions.html

2. Make sure you know the username and password for the Google email account

3. Download and install a program called STUNNEL (latest windows exe can be found on this page: http://www.stunnel.org/download/binaries.html)

4. Edit Stunnel's configuration file (the default config file is in the stunnel directory, and is called "stunnel.conf").

5. Empty out the config file and paste  in the following values:
client = yes
debug = debug
[pop3s]
accept = 127.0.0.1:1109
connect = pop.gmail.com:995

[smtps]
accept = 127.0.0.1:259
connect = smtp.gmail.com:465


6. Go into your CF Administrator, to the mail settings, and enter the appropriate values according to the illustration and your own username and password

coldfusion mail server settings

(note: 127.0.0.1 is not a placeholder value in this blog post: use that value! Also, notice the port number used, 259; it's the same number in the stunnel.conf file for accepting SMTP mail.)

7. Go to the stunnel directory under Programs and choose to install it as a service, then run it as a service.

8. Go to your services list and make sure stunnel is running.

9. Just for fun, restart the CF service

10. Execute a template you made that has a cfmail tag set up similar to the following:

    <CFMAIL
            TO="dougboude@gmail.com"
            FROM="admin@myclient.com"
            SUBJECT="test email from server"
            TYPE="HTML">
    testing
    </CFMAIL>

11. Log in to your google mail and look in your sent items folder. The email you sent should appear there tout suite. (Google apps allows you to point a subdomain to their email server...my client's is similar to "mailbox.clientdomain.com", fyi)

12. if you don't see the email in the sent items folder, check CF's mail log and make sure it didn't generate a STARTTLS error

If you do all of this and it still doesn't work, I'm afraid I have no more information for you. BUT, if you run into additional issues and DO manage to fix them, please post that info back to the comments area of this post for the benefit of others!

Okay, I feel better now. Being forced to invest SO much time and energy, at 2 am, and then finding nothing but a few nearly useless clues here and there to go on can really drain a person emotionally, ya know? :)

Doug out.
Posted by dougboude at 12:13 PM | PRINT THIS POST! | Link | 14 comments
11 January 2008
Branson, Missouri to Become Deaf and Hard of Hearing Friendly
(This is slightly off topic to those readers arriving from the technical aggregators, but nevertheless is an item of interest to all, I believe)

Okay i've been holding off on sharing this information for a little while now until things really began to materialize, but the time is now right to let the world know about S.E.E. ... Signed Entertainment Enterprises.

The organization was started by my brother Randy Boude in an effort to rally the entire city of Branson, Missouri together to make themselves extremely accessible to the Deaf and Hard of Hearing. Since Branson boasts more shows than even Las Vegas, it is one of the most popular tourist destinations in the midwest and has up to this point, been at best (and like nearly every other city in America)  minimally ADA compliant. Well, S.E.E. is changing all that, and thanks to the evangelizing of Randy Boude, two hotels and one theater have already become "S.E.E. Certified". They have invested in TTYs and Face to Face communication devices, trained their staff in basic ASL and deaf culture, and have made numerous other changes to further enhance the quality of the services and entertainment rendered to their deaf and hard of hearing patrons. Many other shows, restaurants, theaters, and even the city government are already on board and submitting to S.E.E.'s audits to ensure their own "Deaf and Hard of Hearing Friendliness".  Here's a link to my brother being interviewed via webcast.

In addition to having the nearly unanimous support of the Branson business community, S.E.E. has also been endorsed by several non-profit deaf and hard of hearing organizations, including The Deaf Welcome Foundation, who is, in cooperation with S.E.E., opening Branson's first official Deaf Welcome Center. Grand opening for that is near the end of January, 2008.

An awesome day trip to Branson's first official "Deaf and Hard of Hearing Friendly" establishments is planned for January 18th and 19th, including some shows, tours, and meals. If you're anywhere in the vicinity, show your support for these efforts by attending!

Spread the good news! In another year or two, Branson could very well be setting the standard for cities all across the country. Oh, and just so you know, Las Vegas is next on S.E.E.'s radar!

Doug out  :0)
Posted by dougboude at 6:58 PM | PRINT THIS POST! | Link | 4 comments
08 October 2007
Appropriate Usage of the "THIS" Scope
For the past several days, my good friend Jim and I have been having a series of discussions regarding the use of the 'THIS' scope within CFCs; specifically, CFCs that are to be used in an OO fashion. He's part of a small team that has just begun their OO journey and are embarking on a project where they hope to grow and refine their understanding in this arena. Jim being the stickler for correctness that he is, he often bounces approaches and ideas off of me to see what my take is, as well as compare the code he produces to that of his fellow teammates. Here is where a certain rift has manifested itself among them, as his fellow coders are producing CFCs that make heavy use of the 'THIS' scope and little to no use of 'Variables' and 'Var', while Jim is not using 'THIS' at all and is rather declaring his variables as public within the 'Variables' scope or private using 'Var'.

My personal take on the use of 'THIS' within a CFC (and what I firmly expressed to Jim) is that it would be a rare circumstance when I would see 'THIS' as the appropriate scope, since putting variables into 'THIS' exposes them to actions performed outside of the CFC. Just visualizing a CFC with all it's internal variables in the 'THIS' scope immediately causes me to think, "You've just reduced your object down to a very primitive state...a Transfer Object. Why don't you just rather create a structure to hold those values since you're ACCESSING them as if your object WERE a structure." Basically, I've been telling Jim that using 'THIS', except in very specific circumstances, is a bad thing to do.

His own intuition and experience up to this point tells him the same thing, since his comprehension of 'Variables' and 'Var' is quite clear. He has as of yet, however, been unable to convince his peers of this, and has even been admonished to STOP using 'Variables' and 'Var' and start putting his CFC variables in 'THIS'.

Because Jim is a stickler for learning to do things the "right" way, he set out on a Google journey to find documented proof that what he and I believe regarding 'THIS' is absolutely correct. His search has left him empty-handed, though, and so he and I turn to you, our fellow developers, for input on this topic.

The question then: What are your basic rules of thumb regarding the use of 'THIS' when writing a CFC to be used in an OO application (even a loosely structured one)?

Thanks in advance for sharing your opinion!

Doug  :0)
Posted by dougboude at 11:45 AM | PRINT THIS POST! | Link | 6 comments
27 September 2007
Global Configuration in Model-Glue: Using Your Own Config CFC
In a post I did a while back, I shared what I had learned about using Model-Glue's built in "SimpleConfig" cfc. Well, I just got a question from a fellow developer who was trying to leverage the information in that post to implement his own home grown configuration settings cfc, and what I had shared there didn't quite apply. I tried to email him back, but it was returned as undeliverable, so I thought I'd just post my response to him here for the sake of anybody else who is endeavoring to do the same thing.

His email:

Hi Doug,

I love your post (http://www.dougboude.com/blog/1/2007/06/Global-Configuration-Settings-in-ModelGlueUnity.cfm)

I was actually finding it hard to implement though. I basically have written a settings.cfc with a medium  sized struct containing all my site settings. I have functions like getProperty([name]) which returns the required value.

Now, I am just getting into MG and i  am finding it hard to simply put this in the global scope available to my Models, Views and Controllers.

I read your article but am still confused.

Can you help us out with this?

Thanks



My response to...we'll call him Johnny:


Sure can, Johnny!  Since you have created your OWN settings CFC, which I have also done in other MG apps, here's what I completely recommend doing: use Coldspring. 

What we're going to do is let Coldspring handle the "injection" of our settings object into those other objects that need it. Let's say for an example that we have a USER.cfc in our model directory that will need the DSN in order to perform a query. The DSN value is located in our SETTINGS.cfc. So, in the Coldspring.XML file we're going to have to do two things:

1. tell Coldspring about our settings.cfc
2. tell Coldspring about our user.cfc, at the same time telling it to put 'settings' inside of 'user' 

Now, two questions become immediately apparent:

1. What is the proper syntax for telling Coldspring the above two items?
2. What do I have to do special inside of my USER.cfc in order to help Coldspring do what I told it to do?

Telling Coldspring About Your Objects

Look at the Coldspring.XML snippet below. It is accomplishing the first two items we said we needed to tell Coldspring about (our settings cfc and our user cfc):

<bean id="Settings" class="model.settings" />

<bean id="User" class="model.user" >
    <property name="Settings">
       <ref bean="Settings"/>
    </property>
</bean>


Accommodating Coldspring Within Our CFCs

Now, what do we do special inside our User.CFC in order to help Coldspring accomplish what we told it to do? We create two additional methods, one called SetSettings and one called GetSettings.

Here's what those two methods should look like:

<CFFUNCTION name="SetSettings" access="public" returntype="void" output="false">
       <CFARGUMENT name="Settings" type="model.settings" required="true" />
       <CFSET variables._Settings = arguments.Settings />
</CFFUNCTION>

<CFFUNCTION name="GetSettings" access="public"
returntype="model.settings" output="false">
       <CFRETURN variables._Settings />
</CFFUNCTION>


By us including those two methods (notice the correlation between the names of the methods...get[bean id here] and set[bean id here] to the ID value we chose in the Coldspring.XML snippet), Coldspring will inject an instance of your settings object. Then, within any method inside of the User.cfc, we can access our settings
with "GetSettings().getProperty([name])".

For clarification, here is a sample USER.cfc to illustrate what I mean:
<CFCOMPONENT DISPLAYNAME="user object" >
    <CFFUNCTION name="SetSettings" access="public" returntype="void" output="false">
           <CFARGUMENT name="Settings" type="model.settings" required="true" />
           <CFSET variables._Settings = arguments.Settings />
    </CFFUNCTION>
   
    <CFFUNCTION name="GetSettings" access="public" returntype="model.settings" output="false">
           <CFRETURN variables._Settings />
    </CFFUNCTION>
   
    <CFFUNCTION NAME="login" ACCESS="public" RETURNTYPE="string">
        <CFARGUMENT NAME="username" TYPE="string" REQUIRED="yes" />
        <CFARGUMENT NAME="password" TYPE="string" REQUIRED="yes" />
       
        <cfset var retval = "" />
       
        <CFQUERY NAME="LogMeIn" datasource="#GetSettings().getProperty("DSN")#">
            select * from users where
            username = <cfqueryparam value="#arguments.username#">
            AND
            password = <cfqueryparam value="#arguments.password#">
        </CFQUERY>
       
        <cfif LogMeIn.recordcount eq 1>
            <cfset retval = "success" />
        <cfelse>
            <cfset retval = "failure" />
        </cfif>
       
        <CFRETURN retval />
    </CFFUNCTION>
</CFCOMPONENT>
This sample assumes that your homegrown settings cfc uses a method called 'getProperty' to retrieve values


That's it!

For every CFC that requires an instance of your Settings.cfc, just make sure that you define it in Coldspring.XML AND include the Set and Get methods named appropriately inside of the recipient CFC.


A SPECIAL CASE

IF you are trying to inject your Settings.cfc into a Modelglue CONTROLLER cfc, then there's one less step involved for you. The controller CFC still needs the GET and SET methods present inside itself, BUT you do NOT need to define the controller bean within Coldspring.XML. Allow me to repeat myself: As long as the Settings bean is defined in Coldspring.XML, all you have to do to have that injected into a CONTROLLER cfc is to create a SET and GET method identical to the examples above within the recipient controller cfc.


In Closing

I'll close by saying that there are other ways, using Coldspring, to get a Settings object into other objects, but in my opinion those approaches are no better or worse than the one I described above, so let's just keep it simple and stick to this one.

Hope this helps, Johnny, and let me know if you have any more questions; I'm always glad to share what I've learned.

Doug  :0)
Posted by dougboude at 3:21 PM | PRINT THIS POST! | Link | 0 comments
21 September 2007
Dynamically Outputting Query Data 'X' Columns Across
Dynamically outputting query data in "X number of columns across" is no new thing, but since I found myself having to do that very thing this morning, I thought I'd share this sweet little snippet for the benefit of everyone (but mostly for my own snippet collection since I seem to tend to "forget what I once new"... lol).

In this example I'm going to output a series of short questions using the following query result:
coldfusion query result set

WIth the final html and result looking like this:
html table final result of outputting x rows across

<table width="100%">
    <tr>
        <td>Dead loved ones present</td>
        <td>Animal(s) as prominent player(s)</td>
        <td>"Significant Other" present</td>
    </tr>
    <tr>
        <td>Familiar Setting</td>
        <td>Unfamiliar Setting</td>
        <td>Awoke Emotional</td>
    </tr>
    <tr>
        <td>Dreamed you were Awake</td>
        <td>&nbsp;</td>
        <td>&nbsp;</td>
    </tr>
</table>
generated html

Without further adieux, your snippet:

<cfset numColumns = 3 /><!--- set the number of columns to output --->

<!--- determine number of total rows to output --->
<cfif qryQuestions.recordcount mod numColumns eq 0>
    <cfset numrows = qryQuestions.recordcount/numColumns>
<cfelse>
    <cfset numrows = int(qryQuestions.recordcount/numColumns) + 1>
</cfif>

<!--- output the table! --->
<table width="100%">
    <cfloop from="1" to="#numrows#" index="i">
        <tr>
            <!--- create columns for this row 'i' --->
            <cfloop from="1" to="#numColumns#" index="j">
                <!--- calculate which record we're outputting --->
                <cfset qryRow = ((i-1)*numColumns)+j >
                <!--- ...as long as wer're not trying to access a query record beyond our 'recordcount' value... --->
                <cfif qryRow lte qryQuestions.recordcount>
                    <td><cfoutput>#qryQuestions["question"][qryRow]#</cfoutput></td>
                <cfelse><!--- output an empty 'filler' td... --->
                    <td>&nbsp;</td>
                </cfif>
            </cfloop>
        </tr>
    </cfloop>
</table>
Posted by dougboude at 5:34 AM | PRINT THIS POST! | Link | 0 comments
20 September 2007
Adobe User Groups Everywhere: I Have A Dream

I haven't been a member of very many Coldfusion/Adobe-centered User groups, but the few I have participated in seem to have a common, perpetual and prevalent concern floating in the air: membership and attendance.

I have very recently had the privilege of being part of the resurrection of San Antonio’s own Coldfusion Users Group. It is being born out of the existing, more general, Alamo Area Multimedia Users Group due to the sudden influx of hardcore Coldfusion users. Our first meeting was two weeks ago. Nine of us sat in a library and talked about what we wanted to gain by participating in the group, who the group Manager would be, when we would meet, where we would host our site, etc. It was fast paced, and everybody in the room was SO zealous, it was contagious. All levels of skill were represented, and you could just feel the hunger for knowledge that the junior among us harbored. Being a brand new group, however, did not exempt us from the same question of attendance that even well established groups entertain: “How would we increase our membership”?

Here, O Best Beloved, is where the true heart of this post begins, as I found myself engaging in a session of combinatory play while looking at this scenario from a whole other perspective. My first inclination was to think of all the companies in town that I knew used Coldfusion, and then come up with ways to solicit their developers. Then came the epiphany that I must say seemed at the moment (and still does) to be absolutely brilliant:

If we want more Coldfusion developers, then why on earth don’t we just MAKE them ourselves?

We who are intimate with Coldfusion KNOW the deep level of satisfaction and sustenance that it can provide. We know that learning it is not so very difficult, we know the true power that it puts into the hands of a developer, and we know that if more people who already have a propensity for technology and problem solving were exposed to those basic truths, they would very likely adopt it in a heartbeat as we have we.

I raised my hand (keeping things orderly, you know) and when called on shared my idea with the group. The sudden silence and ambiguous faces made me feel as though I had just suggested that the emperor had no clothes, my idea appeared to be so foreign to them. In a moment, though, as they mentally extrapolated what investing the time to teach Coldfusion basics to newbies could mean, they heartily seconded the motion to do so (hammering out the details of who, where, and what will be fodder for the next meeting).

Now, I am not vain enough to think that no other User Group has ever come up with this idea. But again, in all the group’s I’ve had the privilege of sharing time with, and in the midst of all the apparent “worry” over attendance, I never once have heard anybody step outside the mindset of “bring in the Coldfusion developers!” and say “hey, why don’t we grow our own”? Isn’t it a marvelous idea, though? If I were a User Group Manager, or an Adobe corporate brain, and my prime directive was to increase User Group participation, I would be organizing and marketing free “Intro to Coldfusion” classes within my target area. I would put an ad in the Thrifty Nickel; I would post it on Craigslist; I would have all my existing members put flyers up on the corkboards at the office. Shoot, I would bring my kids! (we all know that even a child can learn Coldfusion…) I would follow the true spirit of Evangelism and I would preach Coldfusion to the ignorant and uninformed, not just those already converted. I’d gather as many people as would come and meet in a donated conference room with a few cookies and cokes in the back. I’d fire up my laptop and I would INSPIRE these people! The beauty of Coldfusion’s simplicity and elegant folding together with HTML would be revealed to them; the mystery of the request lifecycle would be elucidated to their previously darkened minds; the POWER of loops and conditionals would be handed over to them; and the secrets of interfacing with amorphous backend data would be made secret no more. When they would leave that one hour revival, those people would walk away filled with the spirit of Coldfusion, empowered and enlivened enough so that they would not soon forget what they had learned. That thin foundation would be the catalyst that would bring them back to the NEXT free session to add yet another layer, until they were brought to the point (as I was so long ago) where they could begin to add onto that foundation for themselves. The end result (in my optimist’s vision, anyway) would be a thriving, zealous San Antonio Coldfusion User Group whose collective affection for their craft would inspire all who approached us.

Wow. I think I’ve just cheerleaded myself to the point where I need to go sleep it off. Lol. But, my point is this, Coldfusion (and other Adobe-oriented) User Group Managers AND MEMBERS: If your membership and attendance leaves something to be desired, don’t be afraid to deviate from “the way we’ve always done it”; get creative, think outside the bun, and why on earth not make the time to grow a few of your own from scratch?  The process of introducing someone to Coldfusion and imparting your knowledge of it to them is SO rewarding and SO educational, most especially to those who are teaching. Everybody profits, the Group grows strong, and Adobe Coldfusion (and other products) lives long and prospers.

Doug out.

Posted by dougboude at 3:44 AM | PRINT THIS POST! | Link | 6 comments
11 September 2007
last time I promise...Coldfusion is Dead!
Sys-con and some of the other brilliant prognosticators out there were my source of inspiration this morning on the drive in to to work...here, have a look-see into my unique mind....

CF is dead!
Adding the name Dave Lowe, for google indexing's sake.... :)
Posted by dougboude at 4:49 PM | PRINT THIS POST! | Link | 2 comments
My Blog is Now MUCH More Print Friendly
Just an FYI for those who might be interested...

At the suggestion of a recent reader of my blog, I have made it MUCH easier now to print out any posts of interest. At the bottom of each post is a link which will render it for you in a new window (and in a format I'm sure you'll recognize).

If you do give this a whirl and find any posts that cause the process to "not work so good", please let me know about it!

Thank ye.

The Management
Posted by dougboude at 4:00 AM | PRINT THIS POST! | Link | 0 comments
20 August 2007
Promoting Family Unity: A Weekend at the Beach!
The plan materialized late one night over sweating glasses of White Merlot when Jen and I did the budget and weighed out our options.Camping was up there on the list, and fared well with regards to the budget; but even at night up in the south Texas hill country, it's STILL too hot to sleep comfortably outdoors, so we opted to postpone camping until late fall. Neither she nor her children had ever been to Mustang Island, Corpus Christi is only two hours down the road from San Antonio, the state Aquarium is there, and the budget allowed it, so we decided we'd host one last summer fling for the kids at the coast.

When I was a kid and my mom planned a vacation of any sort for my brothers and I, we really never had any say or part in it. I do believe that a chld will get more out of a trip like this, however,when they actually play a part in the decisions that will affect them,and and so Jen and I were careful to include the kids in as much of the actual logistics as possible.

First we informed them all individually (as opportunity presented itself) of our intentions to go to the beach as a family. Their enthusiastically positive responses were quite refreshing, as I had braced myself for at least one or two mutterings that never came. With everybody's buy-in, Jen and I invoked the aid of "The Negotiator"(Priceline) and booked two hotel rooms for the weekend. The plan was to have a girls' room and a boy's room since each gender was fairly evenly represented, yet another recommendation that was embraced by our youngsters.

As our weekend of sunshine and sand approached, Jen had already gathered her brood and employed their input to put together a grocery list of things we would need to stock the coolers. The house had been cleaned, laundry done, groceries procured, and some bags packed.Earlier that day I had gotten the Jeep outfitted with a trailer hitch so we could use our cargo carrier for the coolers and such, then headed over to pick my babies up. Two of my kids had birthdays this same month, so I made a pit stop at the mall to get each of them the 'cool shoes' they wanted. By 3 o'clock on Friday we had the kiddos all collected and present at our house, and so all gathered together in the living room for a briefing and then a question and answer session.

First, we gave them an overview of what our itinerary would be while in Corpus. I'm not too anal about having a plan (in my opinion; others may disagree  ), but I do think it's important to have at least a general idea of how we were going to spend our time to ensure that we made the most ofit. We would leave the house at 4 pm, arriving in Corpus around 6 or7. After checking in, we would chillax and have dinner out of the coolers. Saturday we'd get up early, partake of the hotel's continental breakfast, then head to the beach. We told the kids we'd stay at the beach until they didn't want to be there anymore, so Saturday was allotted to nothing but that. After the beach, we'd drive up the Island to Port Aransas so they could experience the ferry. Then we'd head to the hotel, clean up, and go out for a fancy dinner somewhere. Sunday the plan was that we'd again have our continental breakfast, then spend the bulk of the day at the State Aquarium. After that, we'd get a snack, get home in time for an early dinner, and spend our $25 Papa John's Pizza gift certificate to feed everyone.That was the plan, and it was immediately ratified by the congregation.

Next we shared a short checklist of things that everybody had to have packed. Toothbrush, two changes of clothes, three pairs of underwear,and some kind of footwear to use at the beach. Anything else was optional, but, everything they were taking with them had to be packed up and sitting by the front door in thirty minutes.

The last thing we did in our family meeting was to have a lottery to see who would ride in which vehicle. The lottery was our attempt to thwart any kind of polarization and natural segregation by making the seating arrangement random. The lottery results, however, didn't do a very good job at this, and most of her kids ended up riding with her except for her thirteen year old daughter. On a side note, as the weekend progressed and we let the kids choose what vehicle they wanted to ride in, it turns out that the polarization I feared was due more so to the quality of the Explorer's radio compared to the Jeep's than any family ties. Fancy that!

We adjourned the meeting, having given the children a time limit in which to have their luggage deposited by the door. In the meantime, the boys and I put the luggage carrier on top of the Explorer and the cargo carrier on the Jeep and began loading them. I was careful to make sure that everybody had a role to play, even Joshy (my oldest who is autistic). It was quite like watching a colony of ants work, with kids marching in and out of the house carrying items, others of us congregated at the vehicles practicing our puzzle-solving skills by making everything fit into a finite amount of space. At last we were all loaded and ready to go. By this time I was soaked in sweat (remember, we're in south Texas!), so the kids watched the Disney channel while I took a cool shower. Only a single hour later than planned we all loaded up according to our lottery and hit the road.

Not one minute after pulling out of the driveway, the wide Texas skies let loose the residue of the latest Gulf hurricane. Through the deluge we made our way to Sams' Club to gas up the vehicles. Since we had left an hour late, it was now straight up five o'clock on a Friday...rush hour, and we had to traverse the entire breadth of the city in order to reach our destination. Since it had been a few hours since anybody had eaten (and I had forgotten to feed my kids lunch...bad daddy! bad!), we decided to make our way to Jack in the Box and have a bite while letting the traffic die down and hopefully the rain subside. It wasn't in our budget, but we ate off the dollar menu, drank our own bottled water, and ended up only spending $24 to feed all eleven of us. Not bad. While we ate, the rain did subside and the traffic did die down, so it turned out to be a good choice after all.

Bellies full and kiddos happy, we loaded up again and hit the road.With only one emergency pit stop to drain a bursting bladder between San Antonio and Corpus, we checked into our rooms at seven thirty Friday evening. After jumping on the beds for a few minutes in the typical celebratory fashion of youth (mine included), we all donned our swimsuits and went down to the pool. Nothing beats the relaxation of laying beside a south Texas pool under a palm tree in the balmy breeze of evening, sipping Coke Zero, eating Cheezits, holding hands with your sweety, and watching your kids do cannonballs. I think I even fell asleep for a few minutes, it was so relaxing. At eleven o'clock we ushered all the kids back to the rooms (though some of the girls had opted to stay in the room and watch the Disney channel from bed rather than swim) to put on their pajamas and hang their wet items from the railing to dry. I don't know what exactly went on in the girls' room, but we boys laid in bed till 2 am telling dirty jokes(the kind dad's let kids tell, like the one about the doctor and the tapeworm) and exchanging funny stories. It was a blast.

Bright and early 10 am the next morning we rolled out of bed and headed to Mustang Island. We backed the Jeep and Explorer up as close to the water as we dared, and before we could get the hatches open, the kids were headed to the surf. We intercepted them long enough to cover them in SP 40 and to give them a briefing on jellyfish and syringes (the syringes part was just to scare them), then we let them go. Jen and I went last, meandering our way hand in hand down to the water's edge to do the romantic thing...you know, take a walk together with the waves lapping at our feet. For the next FOUR HOURS everybody played in the waves and the sand and the sun. Lesson learned here: take the time to reapply waterproof sunscreen every half hour or so...briny waves tend to wash it off as we discovered that evening as we carted several tender young lobsters back to the hotel. I also was given a harsh reminder of the aging process when I discovered much to my dismay that I had BURNED MY LOVE HANDLES! Not only that, but I also found that I had received a sunburn right smack on top of my head in the area where my cowlick has grown somewhat sparse. Eegad, man.

We left the beach and stopped in at a Valero station for some much needed gatorade, then went over to the ferry at Port Aransas. The wait in line for the ferry lasted three times longer than the actual ferry ride itself, but I was glad to have given them all the experience. We got out while the barge was crossing in hopes of spying some of the dolphins that frequent the area, but didn't see any. After reaching the opposite shore, we drove back to the hotel and just laid in our dimly lit, very cool rooms for an hour while mustering the strength to get dressed for dinner and reminiscing about our day. It's interesting how it is that individual waves can be so unique, because the kids must have discussed at least two dozen different specific individual waves and how they had hit them a certain way, or dragged them enough to fill their pants with sand. Every word was spoken through the exact same joyful smiles they had worn all day long, and it made me very satisfied as a provider to see them so happy.

Jen and I were completely whipped, but we managed to find enough strength and zeal to load everyone up and take them the the Outback Steakhouse for dinner. Yes, as you may be thinking, it was expensive. But as I told the kids after one of them snatched the bill from me and read the total aloud, they were well worth it. Jen and I had mandated that everybody drink bottomless water with lemon; that didn't go over too well, but it was the right decision since they were all sun-baked and several of them were harboring headaches from it. They all ordered whatever they wanted, and afterward we split a single "Chocolate Tower" dessert between us. Four of the kids couldn't finish their food so we bagged that up and took it home...though I accidentally backed right over it with the Jeep the next morning. Full tummies and tired, red faces found us at the end of our wonderful Saturday at the beach. We went back to the room and slept like rock (lobsters).

Sunday! Today was our visit to the State Aquarium. I woke up, saw that the clock said 9:30 and immediately called the girls' room to wake them up only to be informed that my clock was wrong and it was really just a little before 8. Oops! Oh well, nothing wrong with getting up early, so we all got up, packed up our stuff and checked out. The kids were absolutely wonderful about helping out with things, and were even proactive enough to act without being asked. By the time it was REALLY 9:30 we were checked out and headed down the road to find a place to grab some breakfast. Whataburger satisfied that need and kept us content for the next FOUR HOURS while we wandered around the aquarium and saw the sights. I think I absolutely made their weekend when, after we had paid the entrance fee, I told them that they didn't have to stay with us if they wanted to go off in groups. Scarcely had I finished uttering the words before the girls had gone one way and the older boys the other. So, Jen, Joshy, Amber, Jeremy, and I took our own sweet time checking out the sights and making sure not to miss any details. We had given every child their own disposable camera for this trip, so everyone was busy finishing up what they had left over from the day before, including Amber and Jeremy who really warmed my heart by asking to take pictures of their family in front of various tanks of fish.

A dolphin show and visit to the gift shop later and we were all gathered at the exit ready to hit the road for San Antonio. Nobody complained, and in all my life I've never gotten SO many unsolicited "thank you"s and "I'm having so much fun!"s. Jen, we did good, babe, and I do believe that we managed to give our kids and ourselves some memories that will affect us all in a positive way for the rest of our lives. We may have also promoted a little bit of that family unity we were shooting for, too. And, in the words of the kiddos, "we gotta do this again next year!"
Posted by dougboude at 11:20 PM | PRINT THIS POST! | Link | 1 comment
The Boude Bunch: When Two Families Become One
Promoting family unity is an ongoing process, one that can be extremely complex, fragile, and always requiring constant vigilance and purpose. The process can be even more challenging when the family you're unifying was once two separate ones. Enter if you will into the realm of "The Steps": Step children, step parents.Not only are there the normal human social hurdles to overcome when blending lives in this scenario, but there is also the inescapable issue of the emotional fallout left over from the split of the original families.Sometimes painful, never pleasant, but a fact of life that must be faced and worked with. As challenging as they can be, though, they are not insurmountable and it is entirely possible to find smooth sailing in the wake of a divorce.

Jen and I are near the beginning of such a journey, almost seven months into it. Both of us have come somewhat bitterly out of a lengthy marriage that we entered into while teenagers, both of us have children who are the most important thing in the world to us, and both of us are deeply in love with each other and determined to blend all of these lives and souls into a family. I have seven children, she has four. Of those eleven children, two are adults and living outside the home, two live with she and I, two with her ex, and my remaining five live with their mother. The ages and sexes are as follows: 22/m, 21/m,18/f, 14/f, 14/m, 13/f, 13/f, 11/m, 11/m, 8/m, 6/f. Oh, and on top of all of this, my 22 year old son is autistic and my 18 year old daughter made me a grandpa this past January. Sounds complicated, eh?!It is, and nothing has settled down yet into anything you could call routine...it's still a very fluid situation, with bridges needed between her children and I, my children and her, and among the children themselves. I'll tell you this, though, there's a whole lot of love flowing all kinds of directions that has the tendency to allow us all to overcome the polarities and loyalties that often try to encroach upon the beautiful thing Jen and I are attempting to build for everyone. Though the going does get tough and we do suffer the occasional setback, overall we're very happy with the progress being made. To facilitate this process, Jen and I try to come up with family activities that promote unity. So, as we go through this process of evolution and growth and weaving together the lives of so many beautiful souls in our care, I'm going to make it a point to try and share some of what we learn in hopes that it might help others who are in the midst of or considering being in the midst of a similar situation.

Related posts:

Promoting Family Unity: A Weekend at the Beach!
Posted by dougboude at 11:09 PM | PRINT THIS POST! | Link | 0 comments
14 August 2007
Have You Heard the One About...
Life's Little Lessons
It was a moonless, black, rainy night in the heart of the Texas Hill country as I hugged the hairpin curves of the two-lane blacktop, earnestly striving to make it to my appointment on time. Fall had descended a month ago and the black silhouettes of the dormant mesquite trees were the only thing outside of my truck's narrow high beams that I could discern. That is, until I rounded yet another sharp left bend in the road and saw, there on the right shoulder, standing half in and half out of the brown weeds, a chicken.

I'm cruising at between 50 and 70 mph depending on the length of my straightaways, I'm fifty miles out into the middle of nowhere, the windshield wipers are at full throttle and still I'm squinting to see the road, my mind is already at my meeting with the client, rehearsing the whole thing beforehand: and then suddenly there's...this...chicken.

For the next single second, everything went in slow motion. The chicken was as startled to see me as I was him, and if it's at all possible to do, his already wide open eyes opened wider. His head cocked slightly upward and for a millisecond I'm sure that our gazes met and We both mentally uttered a startled "wtf"? And then it happened. In the heat of the moment the chicken judged that the best thing he could do was run for it, and run he did. With the perfect timing and course of an ICBM he launched himself from the wet brown grass and out onto the pavement. His head low and chicken legs flailing wildly, he managed to hit the gravel on the other side of the road just as I reached him, and was swallowed up by the dark, wet scrub without having lost a single soggy feather.

It's at this point where my entire view of my very existence was altered, with everything I thought I knew and understood brought into question in one gleaming singularity. It was at this moment when a question formed in my mind and then asked itself of me before I even had a chance to evaluate whether or not it was valid: "Why did the chicken cross the road"?. What I had been utterly conditioned to regard as nothing more than a silly childish joke suddenly held deep philosophical meaning and actual, serious context in my personal existence. What should have been a joke was now sobering, and became the first falling domino within what I thought was a very secure, stable, and understood state of being. "If this isn't a joke, what else isn't a joke"? "If this isn't a joke, what things considered to be serious actually ARE jokes"?

I have since recovered from that moral dilemma that came upon me suddenly at the bend of a dark rainy curve several years ago, but the lesson learned has always stayed with me: question everything. Never just accept a thing because it appears to be one way, but always maintain a certain amount of reservation that other possibilities are just as relevant.

And the answer to the question of "why did the chicken cross the road"? Dude, he desperately wanted to get to the other side! No joke.

Doug out.
Posted by dougboude at 11:33 AM | PRINT THIS POST! | Link | 1 comment
12 August 2007
Accommodating Dynamic Terminology in your App
When building applications intended to service multiple clients, the ability to easily customize certain aspects is a must. In the health benefits management arena, this need couldn't be any greater, most notably with regards to terminology and phrasing. Whereas one client may wish to refer to their employees' usage of tobacco as "smoker certification", yet another will insist on calling it "tobacco usage".  In order to accommodate this dynamic requirement, my team and I came up with what we dubbed our "Lexicon" component, so I thought I'd share the basics of it here in case anybody else has a similar need.

The overall concept and approach is this:
  • Every term is given a generic name;
  • Every term will have a default value with the ability to have customized override values for specific clients;
  • Every term is given a language identifier, to accommodate the fact that languages other than english may be needed;
  • The component that performs lexicon lookups is made available to every display template in some global scope (application, session, request, viewstate (when using Modelglue), etc.);
  • Terms are output inline within the display template via a "getTerm" method call;

Firstly, let's look at the table used to store the lexicon data:


  • 'ID' is an autonumbering field;
  • 'itemID' contains the generic reference to the term. We utilized a hierarchical naming convention that identified the specific area of the app the term applied to, but any identifier could be used here, including a simple GUID;
  • 'ClientID' is either NULL (indicating this is the generic default term to use) or contains the ID of a specific client (indicating that we should use this client's term for this item);
  • 'term' is the actual text that will be displayed;
  • 'languageID' is the id of a language record from our 'languages' table (a simple list of languages we wanted to support). In our case, 1 = english, 2 = spanish, 4 = rap;
MSSQL script to generate the lexicon table:
CREATE TABLE [dbo].[Lexicon] (
    [id] [int] IDENTITY (1, 1) NOT NULL ,
    [itemID] [varchar] (75) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
    [clientID] [int] NULL ,
    [term] [varchar] (250) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
    [languageID] [int] NULL
) ON [PRIMARY]
GO


Simple enough, right? So, if we have a title that needs to be displayed within the Third Party Administrator pod (see illustration), we create an itemID of "pod.tpaadmin.title", providing a record for the default wording, one for our client 690 who wasn't happy with our default wording, a default spanish label, and a default rap label.

Now, we need a Lexicon object to perform lookups for us. The relevant method we'll be using ("getTerm") will look for this client's override value first. If it finds it, it will be returned. Otherwise we'll grab the default value. Here's our "getTerm" method:

<CFFUNCTION name="getTerm" returntype="string" access="public" hint="I return the appropriate term for a given itemID" >
    <CFARGUMENT name="termID" required="true" type="string" hint="I am the id of the term we're looking for. Typically this will be either a string or a UUID" />
    <CFARGUMENT name="languageID" type="numeric" required="no" DEFAULT="1" hint="I am the id of the language we want to use. default is 1 for english." />
    <CFARGUMENT name="clientID" required="true" type="number" hint="I am the id of the client associated with this lookup." />
    <CFARGUMENT name="DSN" required="true" type="string" hint="I am the DSN to use for queries." />

    <cfset var local = structnew() />
   
    <!--- look for the client's override value first... --->
    <CFQUERY name="local.getTerm" DATASOURCE="#arguments.DSN#">
        SELECT term
        from lexicon
        where
        clientID = <cfqueryparam value="#arguments.clientID#" CFSQLTYPE="CF_SQL_INTEGER">
        AND
        languageID = <cfqueryparam value="#arguments.languageID#" CFSQLTYPE="CF_SQL_INTEGER">
        AND
        termID = <cfqueryparam value="#arguments.termID#" CFSQLTYPE="CF_SQL_VARCHAR">
    </CFQUERY>
    <cfif local.getTerm.recordcount neq 1><!--- nothing client-specific returned, grab the default term --->
        <CFQUERY name="local.getTerm" DATASOURCE="#arguments.DSN#">
            SELECT term
            from lexicon
            where
            clientID IS NULL
            AND
            languageID = <cfqueryparam value="#arguments.languageID#" CFSQLTYPE="CF_SQL_INTEGER">
            AND
            termID = <cfqueryparam value="#arguments.termID#" CFSQLTYPE="CF_SQL_VARCHAR">
        </CFQUERY>
    </cfif>
    <CFRETURN local.getTerm.term />
</CFFUNCTION>

Last but not least, here's how we utilize the Lexicon object within our display template (using Model Glue's 'Viewstate'...could just as easily be application, session, or request):

<cfset lex = viewstate.getValue("lexicon") />

<span class="contentTitle">
 <cfoutput>#lex.getTerm(termid="landing.pagetitle",clientID="690",DSN="#application.dsn#")#</cfoutput>
</span>
<br />
<span class="subContentTitle">
 <cfoutput>#lex.getTerm(termid="landing.subtitle",clientID="690",DSN="#application.dsn#")#</cfoutput>
</span>


The lexicon also turned out to be very handy for managing the text to display within links. We have a seperate table containing links with generic descriptions, then utilizing the GUID of the link record as the termID, created lexicon entries to control what link text and language was displayed for a given client.

Pretty straightforward, eh?

Doug out.

Disclaimer: The code and samples in this post have been written as simply as possible in order to illustrate "how" the lexicon works. In actuality we utilized Reactor and Coldspring to operate our Lexicon. The reader is encouraged to use these snippets as a starting point, but by all means to come up with more efficient ways of execution, object setup, and architecture for his or her own purposes.
Posted by dougboude at 11:37 AM | PRINT THIS POST! | Link | 1 comment
10 August 2007
The Battle of the Bulge
I'm an observer. Not only do I look at what's in front of me while I drive down life's highway, I take note of the ever changing landscape on either side of me. One of those landscapes that seems to be "evolving" is my body. I can think back to my senior year in high school when I was a wrestler, weighing in at 129 pounds of pure muscle and sinew. My waist was 30 inches, and my height...well, it was exactly the same as it is right now! My 20s were spent in mild but socially acceptable debauchery as far as my eating habits and "the drink", but I frequented the gym and catered to my craving for outdoor activities, so "the bod" stayed fit. I became a bit bulkier and began to get close to being able to "pinch an inch", but when my shirt was off I could still solicit a hoot now and then from a carload of passing girls. My 30s ushered in a career change from disarming bombs to programming, and thus 40 hours a week where the only thing moving were my eyes and fingers. I was dead center in the midst of my "family man" days, with seven kiddos ranging from 12 down to a few months, and the gym became non-existent to me. My only real exercise as I recall were the slow walks we took around our block with kids in tow and every other month when I had to replace a part on my very aged Chevrolet suburban.

It was February of 1994 when I really took note of "my paunch". I had been truly enjoying my immersion in the world of technology and was absorbing it quite successfully...but I was growing, physically. I blamed it mostly on home cooking (which means I told my wife at the time that it was her fault!), but in fact it was a combination of my sedentary lifestyle AND my age. From that epiphany onward I have been fighting what I believe may be a losing battle in the end: staying skinny. I've dieted enough now to know that that doesn't work for most people (including myself), mostly because it just requires too darn much resolve! The greatest successes I've had came at those times when I made room in my schedule for good old fashioned exercise. After all, staying skinny is a simple equation, right? Burn more calories than you take in and you'll lose weight. Exercise not only burns more calories, but there's some added benefits that come afterwards by way of endorphins, an awareness of feeling stronger, and a sense of accomplishment. Plus, I could still indulge in the occasional Corona or White Russian. Or Apple Martini. Or Long Island Iced Tea. etc.

My latest attempt at staying skinny has been going on for just about exactly one year now: I joined a gym. Not the first time I joined a gym, mind you, and there's nothing magical about having a membership that shrinks your waistline. This gym had something I learned to love in the military: racquetball, and I figured that by getting back into that I could also accomplish the whole shrinkage thing. I had finally found a place in my life where I could truly apply what I consider to be one of the wisest proverbs ever spoken, a proverb I very OFTEN cite to my children when they grumble about an assigned task. It was Mary Poppins who graced the world with what has to be the truest truism ever spoken when she said "In every job that must be done there is an element of fun; find the fun aaaaaaaannnndd....SNAP! The job becomes a game!" (Yoda is a close second when he said "Do, or do not; there is no try"). So, I made working out fun, and played racquetball till my waist began to shrink, and shrink it did. I lost my racquetball partner a few months later (don't worry, she didn't die; I told her to hit the road), so started diversifying my routine to include 20 to 30 minutes on the treadmill followed by another 30 minutes of abusing different muscle groups on the weight machines. Doing this between once and three times a week, to date I dropped about 20 pounds. Not nearly where I want to be, and I seem to have hit some kind of plateau which I'm attributing to my inconsistency, age, and love of apple martinis. Give up, however, I am not. I now have a new gym partner who helps make the job a game again, so I'm confident I'll be able to drop at least another 15 pounds before spring.

As far as progress at the gym, I went through some phases of discouragement where I'd work real hard but when I got on the scale I didn't see what I was looking for. Although it isn't new knowledge, let me share and reiterate a few things that may help you manage your expectations and stay encouraged during your own journey back to skinniness.

What are the signs of progress? A shrinking waist, being influenced less and less by gravity, and the way you feel, both physically and mentally. The mental benefit of working out was one of the first I was able to reap. There were times when I was feeling a bit blue when I went to the gym, but by the time I left it was as if I had taken some kind of anti-depressant. There's also the overall feeling of "being strong" that you have after a workout on the weight machines that in itself is a strong source of self-confidence, progress, and hope of achieving your goals. Less stress on the scale and the shrinking waistline...these are effects that will come somewhere between two and four weeks of consistent work, typically, and even then you won't notice a lot of difference. For me, it took almost a month of consistent work to get to a place where I could actually see some significant weight loss and feel my pants getting looser. Why did it take a whole month to "see" progress? Because for that first month, the progress came in ways that we can't see outwardly. Here are a couple of things I wish someone would have told me when I first embarked upon my journey.

First of all, you must be aware that bulk isn't just on the outside: it's inside, too. All around our organs and even in the midst of our muscle itself we have fat. Ever cut into a thick prime rib, All marbly with fat? Well, human meat gets marbly too, and that fat is part of what will get used first during your journey to skinniness. You won't see it being used up, you just have to trust that you are becoming leaner.

Another thing to bear in mind is that being overweight means that a lot of things are out of sorts. Not only are you having to wear larger pants, but your metabolism is at a certain end of the spectrum, your internals are all cramped and embedded in fat, your habits are not conducive to a healthy lifestyle, and likely your very mindset and opinion of yourself are probably not very encouraging. Working out regularly WILL get all of these things back in order, but it will take time.

As with any worthy project of substantial size (no pun intended!), results come as you exercise patient continuance and will not be immediately visible. This fact is what you need to bear in mind as you begin and continue on your journey toward skinniness in order to remain encouraged. Manage your expectations, do your part to make this happen (be consistent), and without a doubt there is NO WAY that it CANNOT work! You will lose weight, you will get skinnier, and you will feel a thousand percent better both physically and mentally. Remember too those immortal words of Mary Poppins...find the fun, my fellow fat friend, aaaaaaand...SNAP! The job becomes a game!


Doug out  :0)
Posted by dougboude at 12:59 PM | PRINT THIS POST! | Link | 1 comment
31 July 2007
Coder Funnies (well, I think they are!)
This is a photo blog, click here to view all thumbnails. or click here to view the text of this entry.

A while back my sense of self-entertainment took the form of cartooning and over the course of a week I drew up a few that I thought were funny. Of course, everybody has a different sense of humor, so what makes my diaphragm spasm may not affect you at all. If not, well, I can only speculate that there's something terribly handicapped about your sense of humor, and I sincerely hope at some point  that a Lourdes-level miracle occurs (for some, that's what it takes!) and you suddenly find it healed. Just kidding. I'm man enough to accept that I may be somewhat more warped than the average gnurd bird. Anywho, without further adieux, click the pic links at the top if you wanna see into my 'maginations....
Posted by dougboude at 12:26 AM | PRINT THIS POST! | Link | 1 comment
17 July 2007
EgoSurfâ„¢ Formette Snippet
EgoSurf...I'm not really sure what to make of it, but it's an interesting use of asynchronous processing, to say the least. Anyway if you haven't heard of it, it's a site that will do deep searches of various search engines for you and your associated web site, then score you based on the results. I thought it was interesting enough to put a formette in my side nav, so thought I'd share the code in case anybody else was interested in doing something similar. It took a little time to dig through and find the hidden fields and what values are expected, etc., so hopefully this will save someone some time. Without further adieux, here it is in all its splendor:

<form action="http://www.egosurf.org/search.php" method="get" target="_blank" name="egosurf">
    <TABLE CELLPADDING="1" CELLSPACING="1">
        <tr>
            <td align="right" valign="bottom">You:</td><td><input type="text" name="search" size="17" /></td>
        </tr>
        <tr>
            <td align="right" valign="bottom">Your Web Site:</td><td valign="bottom"><input type="text" name="resource" size="17" /></td>
        </tr>
        <tr>
            <td colspan="2" align="center"><input type="submit" name="fj" value="Find Your EgoSurf&trade; Score!" /></td>
        </tr>
    </table>
    <input type="hidden" name="ds" value="1" />
    <input type="hidden" name="e-g" value="1" />
    <input type="hidden" name="c-g" value=".com" />
    <input type="hidden" name="c-y" value=".com" />
    <input type="hidden" name="c-m" value=".com" />
   
</form>
Posted by dougboude at 6:36 PM | PRINT THIS POST! | Link | 0 comments
10 July 2007
Promoting Family Unity
How to Build a Patio

We just moved into a new house. Fairly large, 2,500 square foot, two story house nestled (very tightly!) in a new subdivision in north San Antonio. The back porch is extra large and shaded by its own roof from the cruelly hot south Texas sun. This provides an absolutely lovely place to set up the hammock and relax on a breezy evening with a cold glass of white merlot and some tuneage; but, for fear of scorching the underside of the porch roof, it provides us no place to put the grill!

Anybody who knows anything about Texans, especially south Texans, knows the absolute imperativeness of BBQ at least once a week. Grilled skirt steak (fajitas), corn on the cob, asparagus dowsed in olive oil and lemon pepper, brauts, and all the other fixins that accompany such a cornucopia are as much a requirement for us as the thick humid air that keeps our terrestrial gills in working order. Now, you may wonder why we wouldn’t just put the grill right out there in the yard somewhere like people in places such as Missouri or Michigan do. What are we afraid of? That we might perspire if we have to stand in the sun? Do we have a phobia of grass? Nay, my foreign brethren (everybody NOT from Texas is a foreigner); The thing that deters us from spending too much time standing in one spot in our yards down here is a little thing I like to call Fire Ants. If you go to any house in San Antonio, step into the yard and stand still for more than a couple of minutes, a Fire Ant WILL climb onto you, find a bare piece of skin, and with great purpose, sting you repeatedly until you put it out of your misery. Though we South Texans have learned to live side by side with them to a degree, it has only been by accepting that the grass, any and all grass, is their territory. Hence our family’s need for a place to put the grill. The solution we came up with was to build a patio.

HOW TO BUILD A PATIO

Items Needed

  • One Long-Handled Flat Blade Spade
  • Two each 40 pound bags of course sand
  • One each 40 pound bag of crushed (or decayed) granite
  • Thirty three bricks
  • Your spousal unit, if applicable, and all the kids you got

Directions

1) Gather your family together and discuss the idea of making a patio for your BBQ or outdoor fireplace. If you don’t own a BBQ or outdoor fireplace, discuss getting one. As the father, husband, and guide of your household, ensure that the conversation ends up as all in favor of the project by using your experience, wisdom, and skills in gentle persuasion.

2) Set goals for the project. Procurement of the items can be a separate task from the actual building of the patio if need-be, due to time constraints.

3) Take all or as many as possible of the family on the outing to procure the needed items. Make sure to include the kids in on the process of deciding which sand, spade, and granite to purchase. Also, make sure you ask them if they can think of anything else you might need for the project (suggest gloves, bottled water, and other such amenities if no one else thinks of them  ).

4) Upon arriving home, enlist the aid of the family to transport the items to the construction site. Be sure to feed everyone’s anticipation and excitement of the final product.

5) Upon undertaking the actual construction, assign tasks. You will need:
  • A waterboy/girl and general purpose gopher or two;
  • Someone to provide and maintain the background music;
  • Mud puppies (kiddos to help remove mud/dirt from the hole and to dig through clods already removed to find worms and grubs to study);
  • A surveyor (someone to help you measure and layout the patio boundaries)
 Set all assigned personnel to accomplishing their tasks as needed

 6) Using stakes (or screwdrivers, or any other item you can hammer into the ground), mark the corners of your patio. Once marked, re-measure to ensure that each side is 40 inches long.

7) Using the spade, dig the entire border of the patio’s foundation. Remove all dirt/clay to a depth of about 4 inches. Be sure to keep the entire hole’s bottom level so that as few dips or humps are present as possible. If need be, task the Mud puppies with putting some dirt back into low areas to even it out.

8) Once the hole is prepared, pour in both bags of sand and level out.
 Place bricks into the hole, ensuring that each brick placed is level with the ones adjacent to it. More than one person can perform the brick placement simultaneously, and in fact, this is recommended in order to lend a greater sense of accomplishment and participation to the group. Here is the pattern for the bricks:


9) When all bricks are in place, pour a large pile of the crushed granite at each opposite corner of the patio. Have all available hands rub and roll granite around on top of the patio until all crevices appear to have been filled. Save the remaining crushed granite for another time when the fill will have settled, revealing the need for another round of “filling the gaps”.

10) Instruct the waterboys/girls to bring refreshments to the other workers and themselves. Make sure the radio station selected is one that the majority enjoy, and break out the chilled blush for you and the spousal unit.

11) Chillax for a half hour, making sure that everybody gets praise for a job well done. Talk about how the patio will be used, imagining scenarios out loud and even planning as a group your first usage of it. Also, plant the thought of coming up with a next family project.

CONCLUSION

Few things promote such sincere togetherness and family unity as do joint projects, and this was no exception. So many good things came from this task...the cooperation, the sense of accomplishment, the planning and procurement skills, the practicing of the mindset to not be afraid of hard work, the sweat equity that everybody contributed and thus a stake in seeing it succeed, the exercising of love and patience, the overall feeling of ‘togetherness’ that was promoted; And more than that, the hope that this patio represents: the future memories we’ll all make out here BBQing as a family.

Posted by dougboude at 5:34 PM | PRINT THIS POST! | Link | 2 comments
05 July 2007
DEMYSTIFYING JSON (for myself)
I'm doing this post because the term 'JSON' has continued to appear here and there within blog posts, conference sessions, articles, and emails that I consume as part of my professional growth regimen. Despite the fact that the term is so very often mentioned casually as if everybody has known about it since Kindergarten, the greater part of my understanding of JSON is barren except for the few clues I have managed to glean through context. So, I decided to take the time to get to know JSON a little more intimately, and learned some interesting things.

WHAT I THOUGHT IT WAS
Based solely on the info I managed to gather from "between the lines", I knew that JSON stood for JavaScript Object Notation, and that it was an alternative to XML when dealing with the results of Ajax calls. Knowing that much, I could deduce that it was basically "data in a string". But what it looked like, how to handle it, and why I would want to do it were still questions in my mind.

WHAT I FOUND IT TO BE
JSON is indeed a string representation of simple or complex data, just as is XML, only without the tags AND without the need to treat that string as a document type in order to transverse it elegantly. JSON contains two indicators: curly braces to indicate that a structure follows, and square brackets to indicate an array. Here's a structure written in JSON: {key1:"val1",key2:"val2",key3:"val3"} and a one dimensional array: ["val1","val2","val3"]. You can nest these types within one another, too; for example, the value of a structure key could be an array, and would be written as this: {key1:["val1","val2","val3"],key2:"val2"}. Pretty cool, eh?

Now, how to get data back and forth between JSON and raw CF types. It's a no-brainer using a tag made available as an open source project on RIA Forge by Andrew Powell: CFJSON . I downloaded it and was using it in less than two minutes on a test template. Very simple, very easy.

All of this good information led me to pose more questions, which I went on to get answers to.

  • Were there any performance advantages of using JSON over XML?
  • Was there significant differences in the length of the string produced by a JSON conversion as opposed to an XML conversion (significant in the realm of web services where that string will have to be sent over the wire)?
  • Were there any compelling reasons to adopt JSON in leiu of XML or WDDX?

Following are the results of me finding out the answers to those questions.

THE EXPERIMENT
Working with a 500 row recordset, each row containing 52 fields of mixed data types, including some sql text, convert it to both JSON and XML and record the results of the time it took and the resulting string length. Do this in both a local template/cfc environment AND over the wire via a web service. I used CFJSON as the JSON converter, and Ray Camden's XML.cfc as the XML converter.

THE RESULTS
LOCAL TESTS
1
JSONXML
String Size 539906 1250561
Time (ms) 36374 2046
2
JSONXML
String Size 539906 1250561
Time (ms) 36156 2640
3
JSONXML
String Size 539906 1250561
Time (ms) 37103 2156

WEB SERVICE TESTS
1
JSONXML
String Size 539906 1250561
Time (ms) 36581 2390
2
JSONXML
String Size 539906 1250561
Time (ms) 37832 2496
3
JSONXML
String Size 539906 1250561
Time (ms) 37644 2406

The string size was consistent, as expected. JSON produced a string that was 57% smaller than the same data represented as XML. Even so, the actual difference in the time it takes to send the larger string doesn't even come close to compensating for the additional time it took to produce the shorter JSON string. It took 1,370% LONGER to produce the JSON string than it did the XML string. Wow, at this point it's almost a no-brainer that I would NOT want to use JSON for much if anything. But this huge difference got me to thinking: What was so vastly different about the way the XML was being produced and the way the JSON was being produced? So I dug into the CFCs to find out.

THE DIFFERENCE
Both CFC methods make use of lots and lots of looping and string concatenation. The only glaring difference was in the way this was done. XML.CFC leverages the Java.lang.stringbuffer object and CFJSON uses straight CF string manipulation. So, I altered the portion of the method in CFJSON.CFC that converts queries to utilize the java.lang.stringbuffer object as well. The results were VERY favorable! check out these time tests after I made the switch:

CFJSON.CFC TESTS AFTER STRINGBUFFER CONVERSION
TESTLOCAL TIME(ms)WEB SERVICE TIME(ms)
1 2156 4437
2 2125 3843
3 2031 4022

Wow. The conversion to JSON now runs neck in neck with the XML conversion, simply by leveraging the stringbuffer object. Sweeeeet.

Okay, now that I know that JSON data strings are significantly smaller, just as fast to create, how about manipulation? How painful is that? To find out I decided I was going to use my JSON data in a javascript function. I scaled down the size of the data set to only five records and a handful of fields for this test. Check out how simple it was to 'dump' my data into javascript and to access it afterwards:
<script>
    function showMeSomeJSON(){
        var objQuery = <CFOUTPUT>#jsondata#</CFOUTPUT>;
        var i = 0;
        for(i=0;i<objQuery.recordcount;++i){
            alert(objQuery.data.due_date[i]);
        }
    }
</script>


CONCLUSION
I have come to no solid conclusion as of yet, but I must say that I'm leaning heavily towards JSON in lieu of XML. It's got a smaller footprint all the way around, has the ability to capture complex nesting, and is really straightforward to navigate using Javascript. Additionally, the same way CFJSON can encode complex data, it also DECODES it on the receiving end, turning it back into something that CF recognizes.

Oh, and I think I'm going to email the CFJSON guys and let them know about the significant performance improvement I got when switching to stringbuffer. They may have already experimented in this arena, but just in case, I'll let them know.

That's it for my personal "JSON Demystification". :)

Doug out.

P.S. If you would like to see an actual JSON representation of my five record query, here it is:

{"recordcount":5,"columnlist":"complete,date_desired,dept,details,developer,due_date,est_hours","data":{"complete":["100%","100%","0%","100%","0%"],"date_desired":["Less Than 2 weeks","Less Than 1 week","More Than 2 weeks","Less Than 1 week","Less Than 1 week"],"dept":["Eligibility","Marketing","Benplan_com","Repricing","EDI"],"details":["1. For rehires, if a rehire date is present on the file and the rehire date is greater than the date of hire, then I need to use it.\r\n\r\n2. Cobra reason - make sure the term reason for a benefit is being put on the AE2 file. This is tied in with Scott\'s work.\r\n\r\n3. Some dependents have termed benefits but no effective dates, also no effective date for the employee. I need to find out where these are coming from.\r\n\r\n4. Terms need to be removed from the file 60 days after the employment term date. This will actually be added to the TSS AE2 file as well.","Dave Lawson, SWI, would like for us to send him the annual report we created for him that gives him EE and Dependent data for Group #s 90947 and 90947P.","- We will send?? or receive daily RX claims\/deductible file updates to one or two PBM\'s--Innoviant for sure and maybe Catalyst.\r\n"," 1) Show correct network for each group. 2) Change elapsed days to start counting on the day the claim was sent out. ","Correct the SSN validation on inbound EDI returned repriced claims. "],"developer":["Dan Crouch","Kelly Young","Gemma Anthony","Kelly Billen","Kelly Billen"],"due_date":["09\/09\/2004","8\/19\/04","NA","08\/30\/2004","08\/13\/2004"],"est_hours":["",1,400,1,1 ]}}

Posted by dougboude at 5:50 PM | PRINT THIS POST! | Link | 4 comments
04 July 2007
Getting Your Deposit Back
Any other renters out there? If you've EVER rented before, you know the incredible disadvantage you can be at as a tenant when it comes time to get your deposit back. Out of all the places I've rented in my life...and that's actually quite a few...I can't remember EVER getting my deposit back, and that's not because I tore up every house I lived in, either. In fact, I've been a very GOOD tenant and have done my own repairs, upgrades, and even minimal remodeling. I didn't get my deposit back because the people I rented from summarily judged me and, using the advantage they have legally, managed to embellish enough to consume all of the funds. Not wanting to deal with the hassle of fighting it, I've always just walked away. Not this time.

I recently moved from the house I had been renting for two years to a larger one, and of course had to go through the whole "return of deposit" routine. As has been my experience, it went sour near the very end. While at first the landlady was amicable and friendly (by all appearances), when it came time to discuss the deposit she suddenly returned to me a laundry list of items that she said justified her keeping it all. Most of the items were normal wear and tear, which legally the tenant is not liable for covering financially, such as walls needing fresh paint, carpet getting worn, etc. One of the items on her list was, and I quote, "aquarium pebbles on the lawn". It got ridiculous, to say the least. So this time I decided to push the issue and formally request that she and the true owner (my landlady is a proxy) reconsider their position for several good reasons. I did this via a letter sent certified mail.

Since it took me several hours to draft and perfect this letter, I thought I'd share it here with you all in case anybody else is ever looking for a good "return of deposit dispute" letter template. Do with it what you will, and good luck getting your deposit back!

P.S. IF the lessor decides NOT to reconsider, and you feel that you have a very good case as to why you SHOULD receive some or all of your deposit back, the next step to take is to document everything that occurred (conversations, dates, times, etc.), go down to your county courthouse, and file a small claims suit naming your lessor as the defendant. I'll see in ten business days from now if that's what I need to do or not.

THE LETTER

Doug Boude

[my new address]

July 3, 2007

 

[insert ignorant landlady's name here]

[insert ignorant landlady's address here]

 

 
Dear Miss Ignorant Landlady:

 
I received what I can only assume is your handwritten letter postmarked June 29, 2007 which cited a lease agreement signed by myself on July 2, 2005 and stated that due to failure to provide thirty days written notice, all security deposits had been forfeited. This letter appeared to be a copy of a faxed document, and was signed by a person unknown to me and claiming to be co-owner of the property at [my old address].

 

I respectfully request that you and the true home owner, [true homeowner's name], reconsider your decision to withhold the entire amount of my nine hundred forty dollar deposit, paid by check on July 2, 2005 for the following reasons:

 

1. As of July 2, 2006, the lease agreement I signed on July 2, 2005 and all terms therein expired; no other lease agreement was ever signed. The supposed reason for withholding my deposit cited, vaguely, an expired agreement.

 

2. I was a very good tenant at all times, caring for the property as if it were my own and always with the mindset that if the homeowner were to show up at any time, she would not be disappointed with the state of the property. I paid the full amount of my rent every month, and only deviated from the acceptable window of payment when previous arrangements were made with you; hence the fact that there is no back rent due at this time. In addition, I performed countless hours of maintenance and repairs (approximately valued at $1,195), many at my own expense for supplies and all with my own time, in order to maintain the property in good working order. AC filters were bought and changed regularly; lawn maintenance was above par (mowed and edged, yard fertilized, trees fed/spiked, flowers planted, shrubbery trimmed, grass watered and kept green – I even contracted with a lawn care company for a time in order to keep the lawn healthy), lawn equipment (which came with the house) was maintained exquisitely (mower blade changed, cracked fuel tank replaced, synthetic oil used in the motor), toilet internal parts were replaced, loose towel racks repaired, shower fixtures repaired or replaced, incandescent light bulbs replaced with low wattage energy saving fluorescent (all of which I left with the house), exterminator called as needed, garbage disposal repaired, clogged toilets and sinks unclogged…and any other thing that needed attention: I took care of it all, and willingly so.

 

3. I am uncertain as to who is actually making the decisions and judgments that are affecting the return of my good faith deposit. Since I was given no contact information for the home owner (who I understand to be in the military) and was directed to deal with you, her proxy, I could not contact the owner directly to discuss the terms of the return of my deposit. I then requested of you, in writing, to be put in communication with the actual home owner on June 14, 2007 and received no response whatsoever to date, effectively and purposefully preventing me from contacting her. Even the handwritten letter I received on July 1 omitted any return address nor did the letter itself contain any contact information (though the postmark shows it was sent from San Antonio). In addition, according to the Bexar County Appraisal district records, [my old address] only has one owner, [homeowner's name], and that at 100% ownership, making the claim of someone else touting themselves as “co-owner” very much suspect.

 

4. My failure to give a full thirty days notice was not done maliciously nor was it premeditated. The circumstances surrounding it were strictly financial, wherein a plan that would have allowed me to meet my financial needs was suddenly and unexpectedly disrupted. As soon as I was aware that I would not be able to pay the rent for the following month of June, I contacted you in writing to let you know and even went so far as to put the “for rent” sign out into the yard for you and repair it when the wind had knocked it down. I also answered inquiries from passers-by who were interested in the house and took them on tours of it in order to assist you in finding subsequent tenants. I acted with my, yours, and the homeowners best interest in mind and with fidelity at all times, as my deeds do attest.

 

5. I did everything in my power to leave the house in as good or better condition than I received it, with the one exception of repainting three or four walls back to the original color. The repainting issue was discussed with you, I informed you that I would not be able to do that, and so there was no miscommunication on that matter. Everything else, however, I did do. I cleaned the house in its entirety, including kitchen, floors, and refrigerator. I disposed of all trash in the bins and even came back after I had moved out to ensure that the bins were set on the curb to be emptied. I’m sure that there was yet more cleaning that could have been done in order to pass the white glove test, but due to my grandfather having a stroke on May 27th and me being the only family he has locally, my cleaning efforts were delayed. I informed you of that situation in writing on May 31st. The hole in the garage which you brought to my attention as not being pre-existing, I purchased the supplies for and performed the repair of. The process of repairing such a hole is two step, and since the second step of sanding and painting had to be performed after my move-out date, I informed you of that need as well and offered to return to complete it. Once I had received from you a rough list of items that you deemed as deductible from my deposit (including items deemed by the attorney general as ‘normal wear and tear’), I offered to return and take care of any of them that you wanted me to. I received no response in the affirmative or negative…there was no communication from you. Again, as with the general care of the property while living there, my intentions and mindset regarding the state of the property at the time of my departure were completely upright and with the owner’s and my best interest in mind, even informing you that I was making myself available after my departure if it was desirable for me to return and take care of any outstanding matters.

 

For the reasons stated above, it is my contention that withholding of the full deposit amount is absolutely unfounded, morally wrong, and is not a just reciprocation for the very good tenant I have been for the past two years. If there were legal precedent that enabled a landlord to withhold the entire amount of a tenant’s good faith deposit when no valid lease agreement is in place due solely to the lack of a full thirty day written notice to vacate, that does NOT justify the doing of it when all other factors cast the tenant in so favorable a light.

 

My desire at this time is to be in communication directly with the home owner herself and not her proxy, in order to come to an amicable, fair, and just agreement regarding this matter as soon as possible.

 

I respectfully request that this letter be immediately forwarded to [homeowner] and that she contact me at the address in this letter’s header section within ten business days, in writing, regarding a fair and equitable agreement as to what portion of the deposit should be returned to me.

 

 Sincerely,

 

 DOUG BOUDE

PREVIOUS TENANT, [my old address]

(JULY 2005 – MAY 2007)

Posted by dougboude at 1:50 PM | PRINT THIS POST! | Link | 2 comments
01 July 2007
The Key to True Conflict Resolution
A Design Pattern in Life
A design pattern is a recurring concept, arrangement of things, and/or repeated process. Life is composed entirely of such patterns. From the patterns found in the genetic blueprints of even the simplest form of life to the consistent and measurable rising and setting of the sun: life is a beautiful matrix of overlaid and interacting patterns. Ah, but more than simply random patterns, these are patterns in the very design of how everything works, including human behavoir. I want to expound upon one such pattern in particular, and that is the pattern that exists between two people who are at odds with one another and what I have observed the pattern to be that leads to resolution in almost every case.

I was once conducted through a phone interview for a technical job and, as interviews will go, I was presented several questions which were nothing more than scenarios, with my response to be the approach I would take in resolving them. One question in particular involved the situation where I was project lead and had two separate groups between which I was liaison. The two groups had opposing opinions as to what decision should be made on a particular aspect of the project. The question posed to me: "How would I resolve it"? It didn't take me long to come up with an answer, because what I imagined in the scenario looked exactly like other scenarios I had encountered in life, and I recognized the pattern: that of two (or more) people who had opposing viewpoints, with each refusing to budge from their position and yet both having the need for agreement. My answer: to make sure that both sides were heard by the opposing side.

It may seem by all outward appearances that conflicts of viewpoint are all about whose viewpoint is better, and that resolution can only be had by compromise or relinquishment of one or both views. This is not true. In scenarios between my children where I have played diplomat and in scenarios between myself and my significant other (where I WISH I had a diplomat!), I have seen the same recurring pattern, and it was never one side managing to out muscle the other side that resulted in peace and accord. The true answer in mending discord lies in something so much more simple: fulfilling the need to be heard.

I type those words slowly, I say them in my mind slowly and with reverence as I read them, because they are so fundamentally important. Hours, days, years even of stubborn silence can be avoided if the parties involved would just recognize what the true need of the opposite side is and fulfill it. The other side really only wants to "have the floor", if you will, long enough to have expressed their opinion fully and, (this is the MOST important part, O Best Beloved) to KNOW that they were truly heard. Once a person has been given opportunity to speak uninterrupted and they are made to know that their side had truly fallen on open ears and an open mind, the fire amazingly just fizzles out. They still have their opinion, of course; but the ire that drove them and their inability to see beyond their own cause just melts away, because the true root need has been satiated. Ah, and the doors to communication that are immediately opened in a nearly miraculous way! Suddenly, whereas this individual was seemingly incapable of hearing a word the other side had to say, now they can hear with clarity and attention, and truly consider their opponent's viewpoint.

Simple enough, right? Just be quiet while the other side talks! Not so, O Best Beloved. A simple stay of the tongue does not a truly hearing opponent make. You see, once one side does agree to give the other the floor and hold their tongue while the other speaks, the speaker now will be examining every minute detail of everything that is occuring while he or she is laying things out. When I say 'everything', I mean absolutely EVERYTHING. It likely won't be done consciously, but without a DOUBT they will be noting every twitch of the listener's facial expressions, the movement of their eyes, their body posture, movements, shifting of body weight, and most of all where their attention is at all times. What they are doing is looking for the one thing they need, evidence that they have been truly heard. Now, although how a person listens is vital, even more vital and necessary to this process is how the listener then responds. The very next thing that comes out of their mouth will either make the exercise a success or total failure. Remember, it isn't agreement that fulfills this pattern in human behavior, it's listening, so it isn't required that one's response be to the effect that they agree. Anything along the lines of "I can see your point", or "I hadn't looked at it like that" can suffice. Be warned though: truly listening is an impossible thing to fake. I can't tell you the innumerable times I've been involved in this scenario and the other side, though seeming to have listened and even responding with something like "I see your point", IMMEDIATELY blew the whole thing out of the water by adding on the word "but". Look at this: "I really do see your point, BUT...." What just happened there? The speaker's viewpoint was instantly invalidated and minimized. What follows the 'but' is irrelevant, and the other person isn't going to be able to hear it anyway, because that 'but' told them that their honest outpour had fallen on ears that never intended to hear them in the first place. Back to square one for everybody.

Learn to recognize the human conflict pattern. It comes in many forms and has varying degrees of intensity, but it's always the same. When you DO recognize the pattern, just remember that the true key to resolution lies in you making sure that you give your opponent the opportunity to fully express himself, you truly open your ears and mind and hear him out, and above all cause him to know that you heard him. Do NOT allow yourself to let the word 'BUT' be part of anything you respond with, or you will have exposed yourself as someone who pretended to hear but really had no interest at all, leaving your opponent's one true need yet unfulfilled. There really is something to the phrase we've heard throughout our life, that 'communication is as much listening as talking'. Practice your listening skills, thus fulfilling your opponent's true need, and you'll find yourself spending a lot less time immersed in life's daily dose of human conflicts.

Doug out.
Posted by dougboude at 6:23 AM | PRINT THIS POST! | Link | 0 comments
27 June 2007
American Airlines, YOU SUCK!
American Airlines, if you were a gas, you would be malodiferous and noxious; if you were a teenage girl, you would be gangly and plain, nobody would ever ask you to the prom, and you would die a virgin.

This particular day, I've found myself with you, American Airlines, in an intimate setting, and all of the assumptions I had about you based on hearsay and things gleaned from between the lines over the years have turned out to be completely confirmed: you're absolutely hideous when it comes to everything you tout yourself to be good at.

Okay, I can understand that you can't do anything about the weather and that a major storm front between Dallas and DC is a good reason to keep the plane grounded on the tarmac while you calculate an alternate route. But THREE FREAKING HOURS to come to the conclusion that you're gonna have to go AROUND it? That amazing feat of absurdity is head-wagging in and of itself. Oh yes, you were "courteous" enough to give us updates every half hour, your likely under-paid captain reassuring the passengers that you were still working on coming up with an alternate route and that until somebody figured one out, we were "grounded indefinitely" (yes, he continued to use the word even three hours later...real good for morale, Einstein). But while we all sat there with nothing to do but cat nap and study the moles on our neighbors' necks, it took you two hours to even consider offering us a drink of water. Meanwhile, the "first class" passengers were being wined, dined, and doted upon continuously. By this time during the nightmare I'm noticing every little defect you have, American Airlines; the way you are sooo careful to ensure that you lay the little napkins face up...not to give me a nice map of the U.S. to look at like your MUCH more attractive competitor Southwest Airlines, but rather to shove your cheesy sleezy advertising in my face for Citi financial and their efforts to help me dig myself further into debt. I resent it, American Airlines, and say again: YOU SUCK!

THREE hours of letting gravity bring my coccyx and the metal frame of your undersized seat closer and closer to one another; three hours of my empty stomach attempting to implode upon itself, the anticipation of my obligatory snack growing more and more intense; wondering why, if you thought to bring me a drink (eventually), you didn't also think to offer me some peanuts or pretzels so I could satiate my hunger. Three hours later, when captain Smiley finally gave us the good news that the unseen geniuses who work with you had cleverly devised a solution to our dilemma and drew a line going AROUND the storm, I found out why you hadn't thought to offer me peanuts: YOU DON'T GIVE YOUR PASSENGERS PEANUTS! Ah, but you did lovingly offer to rape me by selling me a cookie for THREE DOLLARS! A 59 cent cookie, if that, for three bucks?!?!?

You truly do suck, American Airlines, without a doubt and unequivocably. You're a cheap, slutty, miserly, cruel, ugly thing who makes no secret of how little you truly care about your customer. The only thing about you that is remotely attractive (until one actually makes your acquaintance) is how CHEAP you are. You draw in the unsuspecting and unwary and milk them for all you can, then cast them out on the street, leaving them feeling quite used and abused. You hope that we'll go away thinking that "this is just the way flying is". Well, THIS traveler knows better, and can speak from experience when he says "YOU SUCK, AMERICAN AIRLINES!"

Ah, and the paradox of all paradoxes, the audacity you have when you speak lovingly to us over the intercome, "We know you have a choice when you fly, and thanks for choosing us
! We hope you'll come back!". More empty, absurd words could never be spoken, and if you, American Airlines, are naive enough to think that you are offering anything at all to the general public that is the LEAST bit attractive (besides being one of the cheapest ho's in town), you're sadly mistaken. I'm sure you adopted the line "we know you have a choice" from you more successful competitors and just thought you should say it, too; but you really shouldn't bother saying those words at all because it only makes you look even LESS business-savvy than you are.

If you ever want to have a hope of real success, of truly satisfying customers and making them WANT to return, American Airlines, then lay it to heart when I say, as a real world customer who currently loathes your very existence, "YOU SUCK!", and do something about that.

Doug out.
Posted by dougboude at 12:00 AM | PRINT THIS POST! | Link | 49 comments
23 June 2007
American Sign Language as a Second Language
Why I'm Learning Sign
On every Tuesday and Thursday evening in the month of June, I have been taking a sign language class. Of course the first question that tends to come to mind is "why?", so let me address that first. If you've ever read Kipling's 'The Elephant's Child', then you've caught a glimpse into the mind that drives one who is plagued with "insatiable curiosity"; they do odd things, like travel to the great, grey, green greasy Limpopo River to ask questions that to others seem unworthy of being uttered. I am plagued with a similarly insatiable curiosity (although I DO know what the Crocodile has for supper, O Best Beloved), and in particular am insatiably curious about languages. How one idea can be thoroughly communicated in so many different ways fascinates me, and so at any given point in my life since about the age of 16, I have been in the process of exploring some new language. Some stuck rather well, like Spanish, French, Albanian, and Hindi; Others linger in the form of a few words or expressions, like Mandarin, Hebrew, and Russian. American Sign Language is a language in its own right, with its own syntax and word form, so it too fell into my sights. I first learned a portion of it several years ago when I desired to do a Bible study with a deaf couple I had met. I spent several hours creating a list of all the potential vocabulary words I might need, then went to the library and looked up each one, drawing myself little pictures of how to form the word. I spent many hours practicing the same, and finally did have that Bible study. Presently, I'm in love with a woman who happens to have two profoundly deaf sisters and a deaf brother in law. Last month we went and visited her family in Chicago, and I felt myself to be quite handicapped when it came to communicating with them. Fortunately, they all read lips well and even speak, so their skill set made up for what I consider to be MY handicap in not knowing Sign Language; but I did resolve within myself that the next time we meet they wouldn't have to carry so much of the burden of communication. And so, I'm making Sign Language a part of my daily regimen!

Last night, as part of our class, I attended a performance of "Annie" put on by the San Antonio Deaf Dance Company. It was mandatory, as our instructor had made it part of the curriculum. I took my fiancee and five of my seven children. We arrived early and got into our seats amidst several of the kids' grumblings about how boring this was going to be. But an hour and a half later, after all was said and done, none of us had a single shred of regret that we had come, not even the kids! Everything was done in sign language, with interpreters for the hearing provided, but even though I know very little sign and my kids know even less, we all learned something about human communication, that there is so much more than words to it all, and of the incredible amount of information that can be transferred accurately by a simple raise of an eyebrow or a facial expression. So what appears to be at first glance a language that would severely impair one's ability to communicate deep thoughts or detailed feelings is actually probably more robust and alive than any spoken language ever could be.

The performance itself was amazing. As you know, "Annie" is a musical, so there were several songs, both solo and with chorus, as well as several dance numbers that required coordination among the dancers themselves. Most of the time, music did accompany those numbers, though once or twice they were done completely in silence. It was mesmerizing when, without song or music, the troupe performed flawlessly and in beautiful synchronicity. For those of us who are hearing and who are able to realize a different perspective on the challenge that such a thing can present, it was inspiring and impressive beyond words. I'm extremely glad that I took my kids, and I'm very happy at the final reactions that came from them. Whereas at first they thought it was funny...not the people's handicap, but the way signing appeared...by the end they were impressed, themselves, and had even managed to learn some signs just by watching the audience interact during intermissions.

Don't know any Sign Language yourself? I highly recommend that, the next time your local school district offers adult education classes that you sign yourself up and partake of the benefits that come by delving a little bit into the world that our non-hearing brethren live in. Empower yourself a little, become capable of doing your part to bridge the communication gap the next time you encounter someone whose primary language is visual. You won't regret it. ;)

Doug out.
Posted by dougboude at 10:03 AM | PRINT THIS POST! | Link | 5 comments
22 June 2007
Human-Protist Symbiotic Experiment
The man who could digest wood!
We all have one, don't we? A favorite protist. One protozoa out of them all that just stands out in our minds as THE protozoa, the one that captures our imagination and intrigues us to no end. Well, I must say that personally I have a list of about five protozoa that are at the top, but one has just recently topped them all: Triconympha. Oh yes, Paramecium with his 'oral groove' and cilia-covered body is amazing to behold as he swims about sucking his smaller cousins into his gullet; and Euglena...wow, a pioneer in the protista kingdom, blurring the line between plant and animal in a very politically incorrect manner. But Triconympha is my all time favorite as of late because, through MUCH experimentation, I have finally succeeded in breeding a strain that can survive in the stomach of a human being! Namely, me. You may be thinking "gross", or "wtf?!?", but allow me to elucidate. Triconympha has been a long time symbiotic partner of our little-loved friend the termite. He lives in his stomach and does Mr. Termite the favor of digesting the wood that termite eats. I learned this in sixth grade, and from that moment onward it has been a constant source of meditatory food for me as I marveled at how perfect their relationship was. I also longed to have that same kind of relationship with Triconympha. But alas! My gastric crib was quite inhospitable to the little guy! Every time I munched a sunflower seed (and ate the hulls, as is my habit), I longed to be able to receive not only the benefit of the huge amount of roughage the hulls provided me, but also the nutritional sustenance that I KNEW was locked in their, too! The only way I could do this would be by having my own personal colony of Triconympha living, breeding, and dying within me. Hence the experimentation that has led to my success!

I'm no gene splicer myself, so I relied on the gene splicing that's built into every living thing: reproduction. Since these little guys are single celled, they don't actually exchange genetic material during reproduction, but simply divide in half, producing two from one. So what I had to do was find individual specimens that exhibited unusually high tolerance to travel through my gut and intestinal tract. Once I had found several such specimens, I would give them time to multiply into yet another thriving colony, then perform another iteration of the "gut travel" procedure, until finally I had a thriving colony that were perfectly at home inside of ME! The entire process took about five years. At the rate that Triconympha reproduces, that actually equates to about four thousand years of evolution in human terms. I acting as the "natural selection" mechanism managed to guide them into exactly what I wanted them to be. Following are a few details of the experiment as it progressed, for your entertainment value.

Step one: Establish a thriving colony.
Where does one find a starter colony of Triconympha? Inside the stomachs of termites, of course! That is their natural habitat, and so that is where I went. Several days of searching the uninhabited lands around my subdivision resulted in the collection of thousands of donor termites. Now, how to extract the Triconympha...hmmm. Well, the first obvious approach was to just grind a bunch of termites to mush and hope the Triconympha could find their own way out. Microscopic examination revealed that, although the Triconympha were indeed emancipated from their gastric domains, they didn't do so well on the outside. What I needed then was some way of emulating a termite's stomach environment, only on a larger scale than the termite itself. Something maintainable, and preferably non-decaying. So, I determined the PH of the termite's stomach content and through a combination of white distilled vinegar, non-iodized salt, and distilled water, produced a solution with an equivalent PH; I produced what looked to be an approximate substitute for decaying wood chewed and swallowed by a termite by combining sawdust from sawing old wood with a diluted solution of off-the-shelf hydrochloric acid and my own saliva. Yep, that is gross, BUT it was precisely what the doctor ordered. I didn't know it at the time, but it was ALSO the first step in producing Triconympha that were acclimatized to the human body as a host. As a matrix in which to combine the "stomach juices" and "food", I used a sterile, non-lubricated or powdered surgical glove. A small piece of new sponge was sealed up in the opening of the glove so that  a free exchange of gases could take place. I know, I know, the danger of microbial invasion existed at that point due to the fact that the sponges pores are monstrous compared to the tiny openings that serve to exchange gases and nutrition within the mucous membranes of a termite, but it never seemed to be a factor in my experiments. I got lucky on that one.  Now, with the glove prepared, I tossed in a pre-measured amount of freshly ground termites and let the process begin. After three days I did a microscopic examination of the contents and found that the Triconympha were doing absolutely marvelous. I measured the PH again and found that adding the termite bodies had lowered it a bit, so I created a new matrix that had a slightly higher PH prior to the addition of the termites. That one, after three days, had a colony of Triconympha that were multiplying like crazy. I had found the magic balance and was ready now to set up the endurance test.

Step two: Endurance testing.
Now, how to find out which of these little buggers would be most prone to live in MY gut? I decided to gradually alter the environment of the matrices towards the environment of my own gullet. But what was that environment? I could look it up online, the PH of my stomach...but to be even more accurate, I had to measure it myself. I won't go into the gory details, but I did measure the PH of my OWN stomach and found it to be about actually a bit higher than the averages: I'm a 3.5. This actually made it a little easier for me since the PH of a termite's stomach is around 4.3. I set up ten test matrices, and decreased each of their PH by .1 every four days. Just prior to decreasing the PH, I would do a culture exam to see who was thriving and who wasn't. As soon as I saw a decrease of between 10 and 15% of active Triconympha, I would cease the PH adjustment schedule, remove a small sample of Triconympha from that matrix, and place them into a matrix that was .2 PH higher than what they had just come from. That culture would be allowed to grow for two weeks, after which the PH decrease regimen would begin again. Eventually, I had seven colonies of Triconympha who were living, breeding, and thriving in a PH of 3.5, which put me at the precipice of the final step in my experiment.

Final Step: Introducing The Colony to Doug Boude.
While I left the seven colonies to thrive, I now proceeded to do some preliminary wood digestion experiments on myself with which to compare results after introduction of the protozoa to my gut. Since sunflower seed hulls had been the catalyst for the experiment in the first place, I used them. Again, I won't go into the gory details, but suffice it to say that the process involved eating a pre-measured amount of sunflower seeds on an empty stomach, then closely monitoring and collecting appropriate stool samples for examination. As you may have envisioned (he he he he he), without Triconympha present, all I got was a neatly packaged lump of chewed sunflower seed hulls, with a slightly malodiferous disposition. I then introduced Triconympha into my stomach. It was an early Sunday morning. I filled three sterile gelatin capsules with samples from three of the matrices, and swallowed them down with a distilled water chaser. I then quickly ate a pre-measured amount of sunflower seeds, and waited. Although I thought I could imagine feeling the little guys working in my stomach, all I really felt was a bit hungry. Hours passed. I believe this was the first time ever I was actually giddy with anticipation at taking a poo. Finally, late that night, nature called. The results were astounding! The amount of fibrous hull present in my stool was a mere 5% of what it had been prior to Triconympha's introduction! The remainder of the mass more closely resembled what you might consider a "normal" poo consistency and texture, and with the same essence about it. Microscopic examination of the stool sample revealed some dead trace representation of Triconympha, but not nearly enough to indicate a complete evacuation of the critters. As a final test to conclude that they guys had indeed taken up residence within me, I forced myself to vomit. You guessed it: There under the microscope, within my own vomit, were living, swimming, happy Triconympha. SUCCESS!

So now I do believe that I'm the only living human being who has the benefit of being able to receive sustenance from the consumption of wood. Pretty cool, eh?

Doug out.
Posted by dougboude at 10:47 AM | PRINT THIS POST! | Link | 2 comments
31 May 2007
My Grandpa

My mind swam as I drove the thirty minutes to the hospital, my thoughts wandering back and forth between the cares of my day and my love and concern for my grandpa. I had just walked in from a pleasant lunch with one of my fellow Masons when the front desk lady flagged me down and handed me a scribbled note that read “grandpa – hospital – emergency – call mom”. Sometimes I really despise the way I always analyze things. Rather than allowing the naturally occurring emotions of such a surprise scenario to run their course, I immediately and instinctively subdued them, opting rather to convince myself that there was no cause for alarm or concern at this point since I had no real information upon which to base a judgment. But c’mon…with keywords such as those scrawled on that yellow piece of paper, how many conclusions could there be that don’t end in a situation where emotions are absolutely appropriate? I do believe that it’s more from fear than strength that I do that to myself…that I throw logic at myself in order to postpone the inevitable. Others look at me and see me as the strong one, the reasonable one, the one who is really able to think clearly and rationally. In reality, I am the one most afraid, and only do it as a self-protective measure. I tend to equate crying with vomiting…both are natural and healing reactions that our humanity thrust upon us when necessary, yet I find myself going to the greatest of lengths to utterly avoid them at all costs. I believe it’s because both of them are involuntary, and nothing frightens me more than not being in control of my situation. Well there, I’ve done it. I’ve just auto-analyzed my own psychology in a public forum. But I don’t care. I’m only writing about this in order to provide some sort of relief for myself, a vent for the anguish I kept bottled up as I helped my grandpa…no, suffered with my grandpa, through the stroke he had had that very morning.

 

I love that man, so very, very much. One of my greatest regrets is that I never put forth the effort to get to know him better in my younger years. He was always traveling here and there with my grandma, living what I envisioned as a very exciting and lavish life. Their home base was Texas, mine was Missouri. I married right out of high school and immediately had my life swept up with responsibilities I was trying to grow into. Our paths crossed only on occasion over the course of thirty years, and even when it was that I eventually ended up living in the same city as he and grandma, I still never seemed to find time in my schedule to invest in our relationship. How sad. What a rich and meaningful relationship I robbed myself and my children of by not being more determined in that.

 

In the past year, I had finally made up my mind to turn over a new leaf in that department, and I embarked upon an endeavor that gave grandpa and I some common ground: Masonry. I didn’t do it just for him, but I was glad that we now had something to ensure we would spend time together. Entering into Masonry necessarily involves the requirement of spending one on one time with other more experienced Masons, and whenever possible, I chose my grandpa. In what Masons refer to as esoteric work, I began to finally get to know who my grandpa was. How smart he was, how even tempered; his wit, his sincere care for his wife, myself, and family (even those of the feline persuasion). He wears his 84 years as though he were just barely entering retirement age, always smiling, laughing, helping with something.

When I finally completed my brisk walk of the hospital’s length (I parked at the wrong end of the facility…fancy that) and entered the room where they were still examining him, it was a completely foreign way to be seeing my grandpa. Whereas he was always the active one, the one helping somebody else, now it was his turn to receive the attention and assistance. And oh, how it grieved him so! I saw my beloved, strong grandpa weep multiple times that day. Not out of self-pity, but for the worry that others had over him and the time they were taking out of their own schedules for his sake. When he cried, I would be strong for him, put my hand on his shoulder, smile at him so he could see that I was there to fill in for whatever strength the stroke had temporarily taken away. My mind was set, and I told him so, that we would all overcome this together and press on as the happy loving family that we were no matter what it might require of us all. I was so glad for my own youth and strength and health so that I could lend it to him, that my abundance could be supply for his lack at that time, as his has been for me in past situations.

 

My grandma…she who is always so beautiful and elegant and graceful in every way; she really rose to the occasion as well, impressing me mightily. The love I always knew was there between them was more evident now than ever, as she tirelessly held his hand from her wheelchair, looked at him so lovingly and dear, smiled at him, and kept him energized by her amazing love for him. He reciprocated, and though few words were spoken between them, the air between their mutual gazes was alive. Her stamina, which normally is quite short due to her progressing Parkinsons, was as good or better than my own; her speech, which is normally not more than a whisper (again, due to her Parkinsons) was quite audible and clear. Though my grandpa may not see it right now, his unfortunate illness has brought out so much good and strength in everyone around him…his weakness, his lacking, has given those around him who love him so such a blessed opportunity to give back…there honestly can be so much beauty in tragedy, such that the tragic event itself is paled in comparison and appears as nothing more than an insignificant thing that should soon be forgotten.

 

This is the second day since my grandpa realized he was having a stroke when he couldn’t lift his spoon of cereal to his mouth, but the sadness that plagued him yesterday has been set aside, and he even walked a couple hundred feet today down the hall of the hospital wing. Tomorrow my mom and Aunt will arrive to see their father and perform the happy duty of a child to their aging parents, and I am excited to see them as well. I was my mother’s eyes, ears, voice, and arms while she couldn’t be here, and I feel very privileged to have fulfilled that role for her. I’m glad I was here when my grandpa got sick, and I will continue to offer myself to them all for the love I have for them, most especially grandpa.

 

There is nothing more beautiful, satisfying, nor purposeful than to give yourself for the sake of another. May I encourage everyone who reads this to consider those who are nearest and dearest to them, and even those who aren’t but should be, and take advantage of the precious moments you have so that when all is said and done (and it will eventually be), you will have no regrets.

Posted by dougboude at 1:29 AM | PRINT THIS POST! | Link | 2 comments