A good friend of mine IM'd me this morning, hot in the middle of a sales pitch to his boss as to why it was a good idea to get away from the Spaghetti paradigm and move towards some semblance of OO methodologies. Our conversation was short and sweet, but I thought I'd toss it out here in case anybody else has anything to add (or correct), for the benefit of any other developer who finds themselves in a similar "sales" position.
MyBuddyJack: My boss wants to know, why do we have to create instances of objects and go that route? Why can't we just use cfinvoke to get the data we need and just code everything an object needs right in the same cfc.
MyBuddyJack: I think he is wanting to code things procedurally, but with cfcs...that's the feeling I get. But what is the advantage to coding it more OO by creating instances etc.?
MyBuddyJack: I just told him it is easier to maintain and change down the road.
Doug Boude: persistence
MyBuddyJack: I couldn't really sell it like I wanted.
Doug Boude: for example, i have a user object. If I instantiate it, then I can keep it alive and interact with it, changing things about it here and there and maintaining those changes throughout the life of the object
Doug Boude: if I just use invoke, I'm treating it as nothing more than a dumb collection of functions
MyBuddyJack: he wants to put everything into the session scope
MyBuddyJack: so everything is always available anywhere
Doug Boude: and what would BE in session? Oodles of variables sitting there beside one another, all in the same bucket, differentiated only by some naming convention, if that?
Doug Boude: using a CFC as an object, you collect related things together, so you always know where to go for a specific thing
Doug Boude: if you didn't use a User object, I'm POSITIVE he'd probably create a User structure that lived in session to hold all of the user's info
Doug Boude: but, the methods he needed to act upon and with that user info would all be living here and there and everywhere...little semblance to anything truly organized and encapsulated.
Doug Boude: to move that user functionality, then, at some later date would be a nightmare because SOME Of the user stuff would live in application.cfm, some of it would live in some UDFs, some would live here, some would live there...no encapsulation
Doug Boude: it would be what we have always been used to: spaghetti
MyBuddyJack: Yeah that's a good point. I told him that if he has an instance of a User Object, then when he wants to output stuff he just calls the object, whereas otherwise he'd have to invoke everytime to get the data he wanted.
Doug Boude: but stuff it all into a user CFC, and voila! it's always packaged up, ready to drop into anything
Doug Boude: invoke is expensive...it instantiates the object first, then calls the method, then destroys the object again
Doug Boude: extra overhead
Doug Boude: why not just breathe life into it ONE time initially and have it waiting to be called upon instead?
Doug Boude: i could only see using cfinvoke under two circumstances
Doug Boude: one, when my CFC is truly just a collection of random methods that I will need on occasion only
Doug Boude: or two, when I need to call dynamically named methods, which isn't possible on an already instantiated object
MyBuddyJack: I think I understand encapsulating everything as a benefit. But is that it? What other beneifts are there to instantiating objects OR why is that better than procedural?
Doug Boude: you can instantiate objects and still code procedurally
Doug Boude: it's really two different things
Doug Boude: using CreateObject does not an OO application make
Doug Boude: like i said, there's the savings of overhead; there's the logical arrangement and grouping of both functions, business logic, and data
Doug Boude: there's the re-usability factor, should that ever be a desire
Doug Boude: if you use objects as the main basis for your app, then one day when the light DOES go off for them and they see the beauty in MVC, moving to a new framework won't be nearly as large a pain
Doug Boude: it's MUCH easier to make modifications to an app when you have things arranged in objects, and without having that old common spaghetti coder's fear, "What else is going to break if i change this?"
Doug Boude: but putting things into CFCs and then using CFINVOKE to interact with them is like starting your car long enough to get you to the first stop sign, then turning it off again. then starting it up again when you're ready to proceed, then turning it off again at the next red light. etc.
Doug Boude: in that analogy, it almost looks...ridiculous to do it that way
Doug Boude: and, it's easy to see the extra overhead involved with that analogy...extra wear and tear on your starter, extra time needed to actually respond to the light turning green, etc. Same thing in an app
Okay, actually there's no way to COMPLETELY distill this topic down to the point where you don't have to have a decent understanding of web development to grasp it. BUT, I do believe that I've been able to demystify the subject sufficient to allow even the beginner OOP/Model-Glue developer to "get it", which is what I wish I would have had a year ago when I left the comforts of procedural and embarked on my OO journey. That being said, this post is not one of the shorter ones I've done; necessarily so, as there's a lot of ground that I felt couldn't afford to be skimmed over. By taking the time to read through it, however, I do believe that a LOT of the "gray areas" that we OO newbies face, architecturally speaking, will be cleared up enough to at least give us a clue which direction to go. That being said, what follows is my PERSONAL understanding (which has worked well for me) of the Model-Glue Event Lifecycle. Enjoy.
UNDERSTANDING THE MODEL-GLUE EVENT LIFECYCLE
The Model-Glue framework can present an intimidating learning curve when coming directly out of the procedural world to that of Object Oriented Programming. One of the things that will help the Model-Glue student immensely is to have a solid understanding of how the Model-Glue event lifecycle works: being able to visualize the invisible. With this knowledge in pocket, many of the architectural questions that will inevitably arise will be more easily addressed. So, let’s explore what we mean by the term ‘lifecycle’, what the definition of ‘event’ is in a Model-Glue context, and what Event’s role is in a Model-Glue application!
A Lifecycle You Already Understand
Let’s look at a lifecycle you’re already familiar with: http request. Pictures are worth a thousand words, so allow me to illustrate this:
Looks familiar, doesn’t it? The request begins life when the client initiates it with an http call to our web server for a specific template. The server receives the request, grabs the template requested, and hands it over to the Coldfusion server for processing. The Coldfusion server renders the CFM template into pure HTML (since that’s all a browser understands), hands the HTML back to the web server, and the web server returns the HTML to the client. End of the request’s lifecycle!
When a client makes a request of a Model-Glue application, a similar process takes place. The focus of this post will begin (and end) at the point where index.cfm is requested and passed a parameter called “event” (eg; http://www.myMGapp.com/index.cfm?event=home). The receipt of this parameter is the first breath our Model-Glue application draws, and begins the internal process which we will now follow through to its conclusion.
What Exactly IS A Model-Glue Event?
‘Event’ is two distinct things:
‘Event’ is a simple value: a name, such as ‘home’, or ‘do.login’ (yes, compound event names are acceptable and even advantageous for organization’s sake!);
‘Event’ is a bucket.
“A bucket?”, you may be asking yourself. Yes, a bucket! Buckets carry things around, and at a certain point in our event lifecycle study we will see how the event changes from being simply a name to an actual value-carrying OBJECT that gets passed around within the application! It’s a beautiful metamorphosis, as you will see.
The Event Lifecycle in a Nutshell
Let’s begin with an overview of what takes place in the lifecycle of a Model-Glue event (We’ll be exploring each step in detail later):
A request is made, notifying model-glue to execute a specific event (eg; http://www.myMGapp.com/index.cfm?event=do.login). At this point, Model-Glue generates an empty Event Object (aka: bucket).
Model-Glue looks in its modelglue.xml file to find out what messages to broadcast to listening controllers ( <broadcast><message name="login" /></broadcast> ) for the 'do.login' event.
Controllers listening for the "login" message execute their corresponding functions ( <controller name="AuthenticationController" type="controller.AuthenticationController"><message-listener message="login" function="AuthenticateUser" /></controller> ).
Model-Glue executes any relevant result actions (behaves as an 'if' statement), if present.
Model-Glue renders any views that are defined for this event. HTML is delivered and the event lifecycle is over.
Event Lifecycle, Step One: Create the Event Object (aka: bucket)
The whole event lifecycle depends upon values being passed around within the application, so the first thing Model-Glue does after being notified which event to execute is to create an Event Object. Just visualize it as a bucket at this point.
After the Event object is created, any incoming form fields and URL parameters are immediately stuffed into it for safekeeping.
Event Lifecycle, Step Two: Reading the Event Definition
Now that Model-Glue has the Event Object all populated with incoming values, it’s time to discover exactly what should be done with them for the event name that was called. The instructions, or event definitions, that Model-Glue refers to when an event name is received all live in an XML document called “Modelglue.xml”. Here is what an event definition looks like:
The <event-handler> tag has three children: <broadcasts>, <views>, and <results>, and they are evaluated in that order. Let’s look at each one more closely, following our Event Object through the process.
Event Lifecycle, Step Three: Giving Shout-Outs (aka: making broadcasts)
The first thing Model-Glue does for a given event is to make any defined broadcast. A broadcast is simply a shout out that triggers certain CFCs to execute specific methods. In our example, the broadcast “login” is made. Model-Glue looks in another section of its Modelglue.XML file to find out what CFCs are “listening” for that particular message, then executes the relevant method. In this scenario, this is what Model-Glue found in it’s XML file:
Ah, so Model-Glue is going to invoke the “Login” method of the AuthenticationController.cfc (which lives in the “controller” directory). Here is an important thing to know at this point: for every method that Model-Glue calls, the Event Object that was initially created and populated will be passed in as an argument, like so:
Why? So that our methods can read values out of it (such as the username and password that were passed in via our login form) AND so our methods can put values INTO the bucket as well! In our example, the Login method is going to attempt to authenticate a user. If it succeeds, it’s going to add a ‘Success’ result to the Event bucket; if authentication fails, it’s going to add a ‘Failure’ result.
Summary of Step 3
Okay, the “Login” method finished, Model-Glue has the Event Object in hand, and there are no more broadcasts defined for this event, so let’s go on to the next step in the Lifecycle.
Event Lifecycle, Step Four: Decide What Results to Execute
Take a look at the <results> section of the event definition. We see two named results defined (so called because each of them has been given a specific name): success and failure. Recall how our Login method added one of two possible results to the event bucket, either “Success” or “Failure”. At this point, Model-Glue looks for any results in the Event bucket and executes whichever one is appropriate. Executing a result really just means “hey Model-Glue, you found a result named “Success”, eh? Then I want you to jump to the defined event named “home” and execute that now.” If a result named “Failure” had been found, then the “login” event would have been called, presenting the user with the login form again. When Model-Glue executes a named result and jumps to another event, the programmer has two choices at this juncture. To start the new event fresh with an empty Event object, or to carry the existing, populated Object over to the next event. This is controlled with the <result> attribute “redirect”. If set to ‘True’, a new, empty Event object is created; if set to “False’, the original Event Object is passed along.
Summary of Step 4
At this point, all of our messages have been broadcast and relevant methods executed; if we needed to redirect to another event, that was also already accomplished. We’ve now arrived at the final step in the event lifecycle: Render our HTML and send it back!
Event Lifecycle, Step Five: Render The Views!
(aka: create the HTML that will be passed back)
Since our “do.login” event was all about actions and not views, let’s look at the “home” event that a successful login attempt redirects us to:
This particular event doesn’t ask Model-Glue to make any broadcasts nor does it ask Model-Glue to look for and process any results. What it DOES do, however, is ask Model-Glue to render two different CFM templates, dspMainContent.cfm and dspLayout.cfm. There are several important things to note here.
First, that we can tell Model-Glue to render any number of individual CFM templates. Second, notice how each template is given a “name” attribute…the rendered HTML will later be referred to by this name. Third, the order we list them in is irrelevant with one WHOPPING exception: the last template rendered is the ONLY one that Model-Glue will return to the browser. So what happens to the other Views that were rendered and placed onto the Viewstack (notice how I just increased your Model-Glue vocabulary ;) )? All other rendered templates are “included” in the final template, in nearly EXACTLY the same manner as using a CFINCLUDE, only slightly different. See a previous post I did on Views called “Model-Glue Views Demystified” for more detailed information on this topic. Now, let’s find out where our Event Object/Bucket is relative to the views that are being rendered, shall we?
Model-Glue makes the contents of the Event Object available to your CFM view templates in an object referred to as “ViewState”. It, too, is an object, very similar to the Event Object, and it contains all the values that your template could possibly need. You may be asking yourself, “why do we need the ViewState Object if we already have a perfectly good Event Object?”. Valid question, and the answer lies within the philosophy of object oriented programming and the two cardinal virtues that a framework like Model-Glue seeks to uphold: Encapsulation and Autonomy. Suffice it to say that in order not to violate basic MVC principles, copying the contents of the Event Object into the ViewState Object was the better thing to do. Okay, take a look at the following two lines of code taken from within a view template:
Any CFM template created for use within a Model-Glue application can absolutely count on the presence of “ViewState”. The value “firstname” that we’re retrieving from ViewState is a value that was placed in the Event Bucket at some point previously in the event lifecycle, likely by one of our broadcasts. Are you seeing the beauty of the Event Object yet? From the beginning of our event call, that bucket was passed around here and there, values being put into it, redirections being made as needed, until finally we arrive at our views where all of the previously saved values are made available for use within your templates. Strings, Arrays, Structures, Queries, and even other Objects can be stuffed into the Event Bucket and made available via ViewState. No limitations in that arena!
Slightly off topic from this article, let’s quench some curiosity and sneak a quick peek at how the final view being rendered includes content previously rendered in the viewstack, in this case, how “body” is output within “final”. Take a gander at the following few lines of code:
<cfset body = ViewCollection.getView("body") />
Yes, it’s really that simple. All rendered views are placed onto the viewstack, referenced by the name we gave them in the event definition, and are available within our template by accessing the “ViewCollection”. Again, for more detail on Model-Glue views, see the “Model-Glue Views Demystified” post.
Views all rendered and the final HTML complete, Model-Glue returns it to the web server, which in turn returns it to the browser that requested this particular event in the first place.
Summary of Step 5, and the end of our Event Lifecycle
This is the end of our Event’s life; it is disposed of properly. RIP.
One Final Bit of Trivia
If you’re curious about what the Event Object really looks like, here’s a CFDUMP showing it and its methods:
CFDUMP of the Event Object
As its name implies, Model-Glue is a framework that allows a developer to create code that is truly self-contained and reusable by acting as the glue that binds all the pieces together. It accomplishes this by managing the one common denominator that the Model, View, and Controller all have in common: The Event Object. From the client’s initial call to the final delivery of the requested HTML to the browser, it is the Event Object that the developer must understand and interact with in order to create a working application. Only a mid-level understanding of the event lifecycle is required to meet the majority of architectural needs. How to incorporate security, how to ensure the display of appropriate navigation, how to easily add in new functionality: these are all common challenges that understanding the Model-Glue event lifecycle will enable any developer to overcome.
So I'm sitting on the couch the other night watching a documentary on Gigantism...a condition in humans where uncontrolled growth occurs due to a genetic defect...when the narrator says that "unfortunately it is beyond the scope of our current abilities to correct this condition at the genetic level...". Suddenly I have an epiphany, that indeed it should be within the scope of our ability to correct this or any other genetic defect, as long as that defect occurs in a part of the human genome that we already have mapped! Now bear in mind, if I, a non genetic scientist can come up with a solution in a matter of literally seconds, why is it that the entire world BOK (body of knowledge) couldn't have already come up with it? Allow me to elucidate the process I propose....
The first breakthrough came to me when I altered the context I give to the human body. Rather than think of it as a pulsing mass of jellies and chemicals, I rather visualized it as a database. In the IT world, a database is a collection of individual records, each with a common structure, yet each also having its own unique value. This entire collection of records IS a database. The human body is a collection of individual cells (records), each with a common structure (human DNA), yet also having its own unique value (liver cell as opposed to a skin cell, etc.). This entire collection of cells (records) is the living body (database). Okay, so here's where the first major bridge takes place.
Let's say for example's sake that my database stores birthdays. I have one million individual records, each one stores a name, social security number, and a birthday. Well, somehow my database was "off" from the beginning, and every one of the birthdays I have stored is one day ahead of what it should be. How this happened is irrelevant; what IS important is that I need to correct it...globally. In the database world, when we need to make the SAME change to every record in the database, we refer to this as a "global update". We create a routine that will go throughout the entire database, one record at a time, and for each one it will make the correction we need. Now then, let's take this same approach and apply it to the human database.
Using Gigantism as our example, every cell in the human body has the same identical defect: a section of a gene is slightly malformed. The solution would be to go throughout the entire body and, for each cell, correct that defective gene. The idea of doing this is already a reality and is called gene therapy. WHY it isn't in a more advanced state, though, I'm a bit befuddled. The crux of my proposed "global update" rests upon a little fella that we all live with and who is quite intimate with us all at a cellular level: Mr. Virus.
For eons we, as husbandmen, have been doing selective breeding. We've practiced it with dogs, cats, cattle, horses, and even on each other at different periods in time. The results of selective breeding are known before the process even takes place: we're trying to take an existing life form and, by leveraging nature's own process, guide the transformation of that species into something more advantageous for ourselves. Sheep with more wool, cows with more meat, cats with no hair...whatever it may be. Let's introduce our subject at this point, and illustrate the remainder of my idea. His name is Dieter, and he was born with Gigantism.
Dieter is 17 years old now, and stands 7 feet tall. His particular form of Gigantism has resulted in extremely exaggerated facial features and super elastic joints that cause his muscles to work extra hard trying to hold him physically together. Dieter needs a global update to correct the genetic malformation he's carrying. So let's send a sample of Dieter's DNA to the "Circle O" virus ranch to begin the process.
Circle O receives Dieter's DNA and immediately isolates and marks the defective section of the gene. They then construct a replacement section that corrects the defect, and begin the process of creating a virus that is specific to Dieter himself...one that is unable to reproduce or thrive in any host except Dieter. Since the lifecycle of a virus already includes the injection of its own DNA into its human host cell, this makes it the absolute perfect means of performing Dieter's global update. The trick now is to selectively breed this generic virus (itself selectively bred from the common influenza virus) until we have a batch whose DNA includes the genetic "patch" needed for Dieter. Oh, and in order to alleviate any unforseen propogation of our customized virus, we also breed in a 'self-destruct' mechanism that makes it non-viable after X number of replications...embed a "counter", if you will, within the DNA of the initial virus batch that will render it incapable of reproducing at a pre-set generation of itself. Additionally, since we have selectively bred this virus to Dieter specifically, Dieter's immune system will take longer to actually recognize it as an invader, thus giving the virus time to perform its duties.
After three months of selective breeding, Dieter's custom virus is ready to be introduced to its host. Dieter will experience mild flue-like symptoms throughout the process, but because of the rate at which viruses replicate and execute their lifecycle, within the course of two weeks the global update will be complete. It won't be necessary to reach 100% of the cells in Dieter's body; having affected 90 to 95% will be sufficient due to the body's own natural design of replacing itself. After two weeks, the virus fails to reproduce further due to its own built-in self-destruct mechanism, macrophages clean up what's left, and Dieter's gene therapy is complete.
The effects of the defective gene won't be taken away...Dieter won't shrink back to a normal size, nor will his joints become less elastic. He WILL, however, cease to grow at an uncontrolled rate and will resume a normal metabolism. Interceding at an earlier age, before puberty, would ensure that the adverse effects of Dieter's condition would have been minimal, at most. Heck, for that matter, the condition could have been detected during Dieter's first trimester, the virus created, and his gene therapy performed before his due date.
Now tell me...WHAT was so hard about that? Hmm? WHY after twenty years (or however long it's been) of sending money to Jerry's kids do we STILL have Jerry's kids???? I don't know, perhaps..PERHAPS...I'm over-simplifying things just a bit. But generally speaking, is there a flaw in my approach to performing human global genetic updates?
The floor is now open to feedback. Thanks for humoring me.
My friend Jim just turned me on to a blog post he came across that attempts to prove that "Coldfusion is dead". Of course, even without reading such a post it's easy to properly classify it as complete idiocy...it is an interesting read, though, and it does solicit input from the community of apparently blind CF developers who have embraced a corpse of a language. The guy wants to know if we exist or not. I posted a comment asking him to elucidate on the standard he and his IT department used to formulate their judgment; hopefully he will.
Myself, I'm of the opinion that the true motivation of his post has more to do with the fact that he's immersed well into the heart of "The Land of the Fruits and Nuts" than anything else. ;)
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!"
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.
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.
Some time back someone asked me if I would consider exposing my personal OO lexicon as a webservice. My answer was yes. However, I didn't get around to doing it until just today. I've decided to secure it, so if you're interested in consuming it, just email me (email address buried in my funky "Yahoo Personals" style resume) and I'll likely give you access to it. Okay, here are the particulars:
Since I'm requiring authentication upon invocation, and since the contents of the lexicon aren't changing all that often, I recommend that you just cache the results after the first call and refresh once a day or so.
Oh, one last thing while we're on the subject...
I know that my lexicon is much less than fully comprehensive, so if anybody has any suggested terms to add and a starter definition to go along with it, I'd be glad to consider them for addition.
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):
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?
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.
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!
Inspired by Michael Dinowitz' article on "Making Google Pay", I recently decided to follow his recipe and see if perhaps I too could make Google pay. I really had no expectations going into it, but I must say that I'm not overly impressed with the results. Over the course of about two months, Google has paid me a whopping [exact amount ommitted for fear of ticking off Google. Suffice it to say, it isn't much]. On top of that, the time it took me to dissect Michael's article, set myself up on Adsense, apply some of the more subtleties of Adsense (like adding Adsense-specific comments to portions of my site in order to help guide the types of ads that showed up), etc. was actually more than $9 worth of work. Yeah, it can be argued that "it's nine dollars more than you had!", true; and frankly all I have to do now that the work is done is just wait. However, in my frequent perusings I came across another company who also pays you to display a few relevant links on your site: Text-Link-Ads.com
Unlike Google's pay structure, where you are paid according to actual click-throughs, Text-Link-Ads pays you for hosting the ad itself. For instance, in the course of the first day after I registered with them, they sold an ad onto my site and promptly paid me $17. As I understand it, they are going to sell up to seven more ads at the same price, and I'll receive that same money every month. I know, in either case (Google or Text-Link-Ads) it isn't a boatload of money; but when it took me two months and a few hours of work to earn $9 from Google, and it took me one day and about fifteen minutes of work to earn $17 from Text-Link-Ads, to me it's a no-brainer: Text-Link-Ads rocks! Besides that, there's no rule against having both side by side, and in fact, visually you can't tell much of a difference between them.
If anybody else is interested in trying out Text-Link-Ads, here are the basic steps:
1. Register with Text-Link-Ads (http://www.text-link-ads.com). You'll receive a key. 2. Add a wee bit o' code to your site to retrieve your ad inventory (here's the code I wrote to do that, just to help you get started):
<!--- Text Link Ads section ---> <cfset xPath = "//Link"> <cfhttp URL="http://www.text-link-ads.com/xml.php?inventory_key=[your key here]" DELIMITER="," RESOLVEURL="no" /> <cfif isXML(cfhttp.filecontent)> <cfset linkads = xmlSearch(xmlparse(cfhttp.filecontent),xpath)> <br> <cfloop from="1" to="#arraylen(linkads)#" index="i"> <cfif linkads[i].Text.xmlText IS NOT "Test Link Ad"> <CFOUTPUT>#linkads[i].BeforeText.xmlText# <a href="#linkads[i].URL.xmlText#">#linkads[i].Text.xmlText#</a> #linkads[i].AfterText.xmlText#</CFOUTPUT><br /> <cfelse><!--- let's output the content into a comment so we can view it in the source, just to see what it was ---> <!-- <CFOUTPUT>#linkads[i].BeforeText.xmlText# <a href="#linkads[i].URL.xmlText#">#linkads[i].Text.xmlText#</a> #linkads[i].AfterText.xmlText#</CFOUTPUT> --> </cfif> </cfloop> <br> <hr width="75%"> <br> <cfelse> <!-- file content was not xml... --> </cfif>
3. Give Text-Link-Ads a little time to sell ads on your site!
For the first twelve hours or so, your xml will only contain a test ad. After 12 hours that goes away and any ads sold will be present. Whenever Text-Link-Ads sells an ad on your site, you'll get an email informing you of that fact so you can make sure it's showing up properly.
That's it. I've only been using this service for a few days now, but if any other relevant info manifests itself I'll be sure to share it.